diff --git a/.devcontainer/mobile/container-compose-overrides.yml b/.devcontainer/mobile/container-compose-overrides.yml index 62a97a01eb..c0655e50e7 100644 --- a/.devcontainer/mobile/container-compose-overrides.yml +++ b/.devcontainer/mobile/container-compose-overrides.yml @@ -11,8 +11,8 @@ services: - open_api_node_modules:/workspaces/immich/open-api/typescript-sdk/node_modules - server_node_modules:/workspaces/immich/server/node_modules - web_node_modules:/workspaces/immich/web/node_modules - - ${UPLOAD_LOCATION}/photos:/workspaces/immich/server/upload - - ${UPLOAD_LOCATION}/photos/upload:/workspaces/immich/server/upload/upload + - ${UPLOAD_LOCATION}/photos:/data + - ${UPLOAD_LOCATION}/photos/upload:/data/upload - /etc/localtime:/etc/localtime:ro database: diff --git a/.devcontainer/server/container-compose-overrides.yml b/.devcontainer/server/container-compose-overrides.yml index d7efc92cb1..c4745ad199 100644 --- a/.devcontainer/server/container-compose-overrides.yml +++ b/.devcontainer/server/container-compose-overrides.yml @@ -13,8 +13,8 @@ services: - open_api_node_modules:/workspaces/immich/open-api/typescript-sdk/node_modules - server_node_modules:/workspaces/immich/server/node_modules - web_node_modules:/workspaces/immich/web/node_modules - - ${UPLOAD_LOCATION:-upload1-devcontainer-volume}${UPLOAD_LOCATION:+/photos}:/workspaces/immich/server/upload - - ${UPLOAD_LOCATION:-upload2-devcontainer-volume}${UPLOAD_LOCATION:+/photos/upload}:/workspaces/immich/server/upload/upload + - ${UPLOAD_LOCATION:-upload1-devcontainer-volume}${UPLOAD_LOCATION:+/photos}:/data + - ${UPLOAD_LOCATION:-upload2-devcontainer-volume}${UPLOAD_LOCATION:+/photos/upload}:/data/upload - /etc/localtime:/etc/localtime:ro immich-web: diff --git a/.dockerignore b/.dockerignore index d152800d1b..f7efb5c56e 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,41 +1,41 @@ .vscode/ .github/ .git/ +.env* +*.log +*.tmp +*.temp + +**/Dockerfile +**/node_modules/ +**/.pnpm-store/ +**/dist/ +**/coverage/ +**/build/ design/ docker/ -Dockerfile !docker/scripts + docs/ !docs/package.json !docs/package-lock.json + e2e/ !e2e/package.json !e2e/package-lock.json + fastlane/ machine-learning/ misc/ mobile/ -cli/coverage/ -cli/dist/ -cli/node_modules/ -cli/Dockerfile - open-api/typescript-sdk/build/ -open-api/typescript-sdk/node_modules/ +!open-api/typescript-sdk/package.json +!open-api/typescript-sdk/package-lock.json -server/coverage/ -server/node_modules/ server/upload/ server/src/queries -server/dist/ server/www/ -server/Dockerfile -web/node_modules/ -web/coverage/ web/.svelte-kit -web/build/ -web/.env -web/Dockerfile diff --git a/.gitattributes b/.gitattributes index 3d43ff20ed..e3fb061bbc 100644 --- a/.gitattributes +++ b/.gitattributes @@ -12,6 +12,12 @@ mobile/lib/**/*.drift.dart linguist-generated=true mobile/drift_schemas/main/drift_schema_*.json -diff -merge mobile/drift_schemas/main/drift_schema_*.json linguist-generated=true +mobile/lib/infrastructure/repositories/db.repository.steps.dart -diff -merge +mobile/lib/infrastructure/repositories/db.repository.steps.dart linguist-generated=true + +mobile/test/drift/main/generated/** -diff -merge +mobile/test/drift/main/generated/** linguist-generated=true + open-api/typescript-sdk/fetch-client.ts -diff -merge open-api/typescript-sdk/fetch-client.ts linguist-generated=true diff --git a/.github/.nvmrc b/.github/.nvmrc index fc37597bcc..7377d130ed 100644 --- a/.github/.nvmrc +++ b/.github/.nvmrc @@ -1 +1 @@ -22.17.0 +22.17.1 diff --git a/.github/workflows/build-mobile.yml b/.github/workflows/build-mobile.yml index a9b8cdf4dd..bf0933a75b 100644 --- a/.github/workflows/build-mobile.yml +++ b/.github/workflows/build-mobile.yml @@ -124,17 +124,17 @@ jobs: IS_MAIN: ${{ github.ref == 'refs/heads/main' }} run: | if [[ $IS_MAIN == 'true' ]]; then - flutter build apk --release --flavor production - flutter build apk --release --flavor production --split-per-abi --target-platform android-arm,android-arm64,android-x64 + flutter build apk --release + flutter build apk --release --split-per-abi --target-platform android-arm,android-arm64,android-x64 else - flutter build apk --debug --flavor production --split-per-abi --target-platform android-arm64 + flutter build apk --debug --split-per-abi --target-platform android-arm64 fi - name: Publish Android Artifact uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: release-apk-signed - path: mobile/build/app/outputs/flutter-apk/**/*.apk + path: mobile/build/app/outputs/flutter-apk/*.apk - name: Save Gradle Cache id: cache-gradle-save diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 6f1e68afce..b10bda81f8 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -50,7 +50,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 + uses: github/codeql-action/init@4e828ff8d448a8a6e532957b1811f387a63867e8 # v3.29.4 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -63,7 +63,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 + uses: github/codeql-action/autobuild@4e828ff8d448a8a6e532957b1811f387a63867e8 # v3.29.4 # ℹ️ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -76,6 +76,6 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 + uses: github/codeql-action/analyze@4e828ff8d448a8a6e532957b1811f387a63867e8 # v3.29.4 with: category: '/language:${{matrix.language}}' diff --git a/.github/workflows/docs-build.yml b/.github/workflows/docs-build.yml index 9313605f64..e2edb3905e 100644 --- a/.github/workflows/docs-build.yml +++ b/.github/workflows/docs-build.yml @@ -35,6 +35,8 @@ jobs: filters: | docs: - 'docs/**' + open-api: + - 'open-api/immich-openapi-specs.json' force-filters: | - '.github/workflows/docs-build.yml' force-events: 'release' diff --git a/.github/workflows/static_analysis.yml b/.github/workflows/static_analysis.yml index b65e60a5f4..ce5220bdb0 100644 --- a/.github/workflows/static_analysis.yml +++ b/.github/workflows/static_analysis.yml @@ -93,7 +93,7 @@ jobs: env: CHANGED_FILES: ${{ steps.verify-changed-files.outputs.changed_files }} run: | - echo "ERROR: Generated files not up to date! Run make_build inside the mobile directory" + echo "ERROR: Generated files not up to date! Run 'make build' and 'make pigeon' inside the mobile directory" echo "Changed files: ${CHANGED_FILES}" exit 1 @@ -101,7 +101,7 @@ jobs: run: dart analyze --fatal-infos - name: Run dart format - run: dart format lib/ --set-exit-if-changed + run: make format - name: Run dart custom_lint run: dart run custom_lint @@ -132,7 +132,7 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Upload SARIF file - uses: github/codeql-action/upload-sarif@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 + uses: github/codeql-action/upload-sarif@4e828ff8d448a8a6e532957b1811f387a63867e8 # v3.29.4 with: sarif_file: results.sarif category: zizmor diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 246c88b039..fd83245097 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -661,7 +661,7 @@ jobs: contents: read services: postgres: - image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3@sha256:1f5583fe3397210a0fbc7f11b0cec18bacc4a99e3e8ea0548e9bd6bcf26ec37a + image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3@sha256:ec713143dca1a426eba2e03707c319e2ec3cc9d304ef767f777f8e297dee820c env: POSTGRES_PASSWORD: postgres POSTGRES_USER: postgres diff --git a/.github/workflows/weblate-lock.yml b/.github/workflows/weblate-lock.yml index 9bbfd16c0d..45ff0e0d3e 100644 --- a/.github/workflows/weblate-lock.yml +++ b/.github/workflows/weblate-lock.yml @@ -45,7 +45,7 @@ jobs: exit 1 fi - name: Find Pull Request - uses: juliangruber/find-pull-request-action@48b6133aa6c826f267ebd33aa2d29470f9d9e7d0 # v1.9.0 + uses: juliangruber/find-pull-request-action@952b3bb1ddb2dcc0aa3479e98bb1c2d1a922f096 # v1.10.0 id: find-pr with: branch: chore/translations diff --git a/.gitignore b/.gitignore index b4ebd04841..af85d96c02 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ mobile/android/fastlane/report.xml mobile/ios/fastlane/report.xml vite.config.js.timestamp-* +.pnpm-store diff --git a/.vscode/launch.json b/.vscode/launch.json index 8682376cda..0cc9b256ca 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,7 +7,7 @@ "restart": true, "port": 9231, "name": "Immich API Server", - "remoteRoot": "/usr/src/app", + "remoteRoot": "/usr/src/app/server", "localRoot": "${workspaceFolder}/server" }, { @@ -16,27 +16,8 @@ "restart": true, "port": 9230, "name": "Immich Workers", - "remoteRoot": "/usr/src/app", + "remoteRoot": "/usr/src/app/server", "localRoot": "${workspaceFolder}/server" - }, - { - "name": "Flavor - Production", - "request": "launch", - "type": "dart", - "codeLens": { - "for": [ - "run-test", - "run-test-file", - "run-file", - "debug-test", - "debug-test-file", - "debug-file", - ], - "title": "${debugType}", - }, - "args": [ - "--flavor", "production" - ], } ] } diff --git a/cli/.nvmrc b/cli/.nvmrc index fc37597bcc..7377d130ed 100644 --- a/cli/.nvmrc +++ b/cli/.nvmrc @@ -1 +1 @@ -22.17.0 +22.17.1 diff --git a/cli/package-lock.json b/cli/package-lock.json index 7c87c3be2e..1271247865 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -1,12 +1,12 @@ { "name": "@immich/cli", - "version": "2.2.72", + "version": "2.2.77", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@immich/cli", - "version": "2.2.72", + "version": "2.2.77", "license": "GNU Affero General Public License version 3", "dependencies": { "chokidar": "^4.0.3", @@ -27,13 +27,13 @@ "@types/lodash-es": "^4.17.12", "@types/micromatch": "^4.0.9", "@types/mock-fs": "^4.13.1", - "@types/node": "^22.15.33", + "@types/node": "^22.16.5", "@vitest/coverage-v8": "^3.0.0", "byte-size": "^9.0.0", "cli-progress": "^3.12.0", "commander": "^12.0.0", "eslint": "^9.14.0", - "eslint-config-prettier": "^10.0.0", + "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-unicorn": "^59.0.0", "globals": "^16.0.0", @@ -54,14 +54,14 @@ }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.135.3", + "version": "1.137.3", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.15.33", + "@types/node": "^22.16.5", "typescript": "^5.3.3" } }, @@ -682,9 +682,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.30.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.30.1.tgz", - "integrity": "sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg==", + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", + "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", "dev": true, "license": "MIT", "engines": { @@ -1355,9 +1355,9 @@ } }, "node_modules/@types/node": { - "version": "22.15.34", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.34.tgz", - "integrity": "sha512-8Y6E5WUupYy1Dd0II32BsWAx5MWdcnRd8L84Oys3veg1YrYtNtzgO4CFhiBg6MDSjk7Ay36HYOnU7/tuOzIzcw==", + "version": "22.16.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.16.5.tgz", + "integrity": "sha512-bJFoMATwIGaxxx8VJPeM8TonI8t579oRvgAuT8zFugJsJZgzqv0Fu8Mhp68iecjzG7cnN3mO2dJQ5uUM2EFrgQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1365,17 +1365,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.36.0.tgz", - "integrity": "sha512-lZNihHUVB6ZZiPBNgOQGSxUASI7UJWhT8nHyUGCnaQ28XFCw98IfrMCG3rUl1uwUWoAvodJQby2KTs79UTcrAg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz", + "integrity": "sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/type-utils": "8.36.0", - "@typescript-eslint/utils": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/type-utils": "8.38.0", + "@typescript-eslint/utils": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -1389,7 +1389,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.36.0", + "@typescript-eslint/parser": "^8.38.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } @@ -1405,16 +1405,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.36.0.tgz", - "integrity": "sha512-FuYgkHwZLuPbZjQHzJXrtXreJdFMKl16BFYyRrLxDhWr6Qr7Kbcu2s1Yhu8tsiMXw1S0W1pjfFfYEt+R604s+Q==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.38.0.tgz", + "integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "debug": "^4.3.4" }, "engines": { @@ -1430,14 +1430,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.36.0.tgz", - "integrity": "sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz", + "integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.36.0", - "@typescript-eslint/types": "^8.36.0", + "@typescript-eslint/tsconfig-utils": "^8.38.0", + "@typescript-eslint/types": "^8.38.0", "debug": "^4.3.4" }, "engines": { @@ -1452,14 +1452,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.36.0.tgz", - "integrity": "sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz", + "integrity": "sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0" + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1470,9 +1470,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.36.0.tgz", - "integrity": "sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz", + "integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==", "dev": true, "license": "MIT", "engines": { @@ -1487,14 +1487,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.36.0.tgz", - "integrity": "sha512-5aaGYG8cVDd6cxfk/ynpYzxBRZJk7w/ymto6uiyUFtdCozQIsQWh7M28/6r57Fwkbweng8qAzoMCPwSJfWlmsg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.38.0.tgz", + "integrity": "sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/utils": "8.36.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -1511,9 +1512,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.36.0.tgz", - "integrity": "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz", + "integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==", "dev": true, "license": "MIT", "engines": { @@ -1525,16 +1526,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.36.0.tgz", - "integrity": "sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz", + "integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.36.0", - "@typescript-eslint/tsconfig-utils": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/project-service": "8.38.0", + "@typescript-eslint/tsconfig-utils": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -1580,16 +1581,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.36.0.tgz", - "integrity": "sha512-VOqmHu42aEMT+P2qYjylw6zP/3E/HvptRwdn/PZxyV27KhZg2IOszXod4NcXisWzPAGSS4trE/g4moNj6XmH2g==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz", + "integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0" + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1604,13 +1605,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.36.0.tgz", - "integrity": "sha512-vZrhV2lRPWDuGoxcmrzRZyxAggPL+qp3WzUrlZD+slFueDiYHxeBa34dUXPuC0RmGKzl4lS5kFJYvKCq9cnNDA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz", + "integrity": "sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.36.0", + "@typescript-eslint/types": "8.38.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -2305,9 +2306,9 @@ } }, "node_modules/eslint": { - "version": "9.30.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.30.1.tgz", - "integrity": "sha512-zmxXPNMOXmwm9E0yQLi5uqXHs7uq2UIiqEKo3Gq+3fwo1XrJ+hijAZImyF7hclW3E6oHz43Yk3RP8at6OTKflQ==", + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", + "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2315,9 +2316,9 @@ "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", "@eslint/config-helpers": "^0.3.0", - "@eslint/core": "^0.14.0", + "@eslint/core": "^0.15.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.30.1", + "@eslint/js": "9.31.0", "@eslint/plugin-kit": "^0.3.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -2366,9 +2367,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz", - "integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==", + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", "bin": { @@ -2382,9 +2383,9 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.1.tgz", - "integrity": "sha512-dobTkHT6XaEVOo8IO90Q4DOSxnm3Y151QxPJlM/vKC0bVy+d6cVWQZLlFiuZPP0wS6vZwSKeJgKkcS+KfMBlRw==", + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.3.tgz", + "integrity": "sha512-NAdMYww51ehKfDyDhv59/eIItUVzU0Io9H2E8nHNGKEeeqlnci+1gCvrHib6EmZdf6GxF+LCV5K7UC65Ezvw7w==", "dev": true, "license": "MIT", "dependencies": { @@ -2504,6 +2505,19 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/@eslint/core": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", + "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/espree": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", @@ -3534,15 +3548,15 @@ } }, "node_modules/prettier-plugin-organize-imports": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.1.0.tgz", - "integrity": "sha512-5aWRdCgv645xaa58X8lOxzZoiHAldAPChljr/MT0crXVOWTZ+Svl4hIWlz+niYSlO6ikE5UXkN1JrRvIP2ut0A==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.2.0.tgz", + "integrity": "sha512-Zdy27UhlmyvATZi67BTnLcKTo8fm6Oik59Sz6H64PgZJVs6NJpPD1mT240mmJn62c98/QaL+r3kx9Q3gRpDajg==", "dev": true, "license": "MIT", "peerDependencies": { "prettier": ">=2.0", "typescript": ">=2.9", - "vue-tsc": "^2.1.0" + "vue-tsc": "^2.1.0 || 3" }, "peerDependenciesMeta": { "vue-tsc": { @@ -4125,15 +4139,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.36.0.tgz", - "integrity": "sha512-fTCqxthY+h9QbEgSIBfL9iV6CvKDFuoxg6bHPNpJ9HIUzS+jy2lCEyCmGyZRWEBSaykqcDPf1SJ+BfCI8DRopA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.38.0.tgz", + "integrity": "sha512-FsZlrYK6bPDGoLeZRuvx2v6qrM03I0U0SnfCLPs/XCCPCFD80xU9Pg09H/K+XFa68uJuZo7l/Xhs+eDRg2l3hg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.36.0", - "@typescript-eslint/parser": "8.36.0", - "@typescript-eslint/utils": "8.36.0" + "@typescript-eslint/eslint-plugin": "8.38.0", + "@typescript-eslint/parser": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4196,9 +4211,9 @@ } }, "node_modules/vite": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.4.tgz", - "integrity": "sha512-SkaSguuS7nnmV7mfJ8l81JGBFV7Gvzp8IzgE8A8t23+AxuNX61Q5H1Tpz5efduSN7NHC8nQXD3sKQKZAu5mNEA==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.5.tgz", + "integrity": "sha512-1mncVwJxy2C9ThLwz0+2GKZyEXuC3MyWtAAlNftlZZXZDP3AJt5FmwcMit/IGGaNZ8ZOB2BNO/HFUB+CpN0NQw==", "dev": true, "license": "MIT", "dependencies": { @@ -4329,9 +4344,9 @@ } }, "node_modules/vite/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", "engines": { diff --git a/cli/package.json b/cli/package.json index e1f49475c0..a5c1b19159 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@immich/cli", - "version": "2.2.72", + "version": "2.2.77", "description": "Command Line Interface (CLI) for Immich", "type": "module", "exports": "./dist/index.js", @@ -21,13 +21,13 @@ "@types/lodash-es": "^4.17.12", "@types/micromatch": "^4.0.9", "@types/mock-fs": "^4.13.1", - "@types/node": "^22.15.33", + "@types/node": "^22.16.5", "@vitest/coverage-v8": "^3.0.0", "byte-size": "^9.0.0", "cli-progress": "^3.12.0", "commander": "^12.0.0", "eslint": "^9.14.0", - "eslint-config-prettier": "^10.0.0", + "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-unicorn": "^59.0.0", "globals": "^16.0.0", @@ -69,6 +69,6 @@ "micromatch": "^4.0.8" }, "volta": { - "node": "22.17.0" + "node": "22.17.1" } } diff --git a/deployment/modules/cloudflare/docs-release/.terraform.lock.hcl b/deployment/modules/cloudflare/docs-release/.terraform.lock.hcl index 417f262157..90a7bd6259 100644 --- a/deployment/modules/cloudflare/docs-release/.terraform.lock.hcl +++ b/deployment/modules/cloudflare/docs-release/.terraform.lock.hcl @@ -2,37 +2,37 @@ # Manual edits may be lost in future updates. provider "registry.opentofu.org/cloudflare/cloudflare" { - version = "4.52.0" - constraints = "4.52.0" + version = "4.52.1" + constraints = "4.52.1" hashes = [ - "h1:2BEJyXJtYC4B4nda/WCYUmuJYDaYk88F8t1pwPzr0iQ=", - "h1:4IASk5SESeWKQ7JU0+M7KApuF5mZyklvwMXPBabim3c=", - "h1:5ImZxxALSnWfH/4EXw/wFirSmk5Tr0ACmcysy51AafE=", - "h1:6TJ3dxLSin4ZKBJLsZDn95H2ZYnGm8S7GGHvvXuuMQU=", - "h1:IzTUjg9kQ4N3qizP9CjYLeHwjsuGgtxwXvfUQWyOLcA=", - "h1:NTaOQfYINA0YTG/V1/9+SYtgX1it63+cBugj4WK4FWc=", - "h1:PXH48LuJn329sCfMXprdMDk51EZaWFyajVvS03qhQLs=", - "h1:Pi5M+GeoMSN2eJ6QnIeXjBf19O+rby/74CfB2ocpv20=", - "h1:ShXZ2ZjBvm3thfoPPzPT8+OhyismnydQVkUAfI8X12w=", - "h1:WQ9hu0Wge2msBbODfottCSKgu8oKUrw4Opz+fDPVVHk=", - "h1:Z5yXML2DE0uH9UU+M0ut9JMQAORcwVZz1CxBHzeBmao=", - "h1:jqI2qKknpleS3JDSplyGYHMu0u9K/tor1ZOjFwDgEMk=", - "h1:kgfutDh14Q5nw4eg6qGFamFxIiY8Ae0FPKRBLDOzpcI=", - "h1:zCAO7GZmfYhWb+i6TfqlqhMeDyPZWGio2IzEzAh3YTs=", - "zh:19be1a91c982b902c42aba47766860dfa5dc151eed1e95fd39ca642229381ef0", - "zh:1de451c4d1ecf7efbe67b6dace3426ba810711afdd644b0f1b870364c8ae91f8", - "zh:352b4a2120173298622e669258744554339d959ac3a95607b117a48ee4a83238", - "zh:3c6f1346d9154afbd2d558fabb4b0150fc8d559aa961254144fe1bc17fe6032f", - "zh:4c4c92d53fb535b1e0eff26f222bbd627b97d3b4c891ec9c321268676d06152f", - "zh:53276f68006c9ceb7cdb10a6ccf91a5c1eadd1407a28edb5741e84e88d7e29e8", - "zh:7925a97773948171a63d4f65bb81ee92fd6d07a447e36012977313293a5435c9", - "zh:7dfb0a4496cfe032437386d0a2cd9229a1956e9c30bd920923c141b0f0440060", + "h1:2lHvafwGbLdmc9lYkuJFw3nsInaQjRpjX/JfIRKmq/M=", + "h1:596JomwjrtUrOSreq9NNCS+rj70+jOV+0pfja5MXiTI=", + "h1:7mBOA5TVAIt3qAwPXKCtE0RSYeqij9v30mnksuBbpEg=", + "h1:ELVgzh4kHKBCYdL+2A8JjWS0E1snLUN3Mmz3Vo6qSfw=", + "h1:FGGM5yLFf72g3kSXM3LAN64Gf/AkXr5WCmhixgnP+l4=", + "h1:JupkJbQALcIVoMhHImrLeLDsQR1ET7VJLGC7ONxjqGU=", + "h1:KsaE4JNq+1uV1nJsuTcYar/8lyY6zKS5UBEpfYg3wvc=", + "h1:NHZ5RJIzQDLhie/ykl3uI6UPfNQR9Lu5Ti7JPR6X904=", + "h1:NfAuMbn6LQPLDtJhbzO1MX9JMIGLMa8K6CpekvtsuX8=", + "h1:e+vNKokamDsp/kJvFr2pRudzwEz2r49iZ/oSggw+1LY=", + "h1:jnb4VdfNZ79I3yj7Q8x+JmOT+FxbfjjRfrF0dL0yCW8=", + "h1:kmF//O539d7NuHU7qIxDj7Wz4eJmLKFiI5glwQivldU=", + "h1:s6XriaKwOgV4jvKAGPXkrxhhOQxpNU5dceZwi9Z/1k8=", + "h1:wt3WBEBAeSGTlC9OlnTlAALxRiK4SQgLy0KgBIS7qzs=", + "zh:2fb95e1d3229b9b6c704e1a413c7481c60f139780d9641f657b6eb9b633b90f2", + "zh:379c7680983383862236e9e6e720c3114195c40526172188e88d0ffcf50dfe2e", + "zh:55533beb6cfc02d22ffda8cba8027bc2c841bb172cd637ed0d28323d41395f8f", + "zh:5abd70760e4eb1f37a1c307cbd2989ea7c9ba0afb93818c67c1d363a31f75703", + "zh:699f1c8cd66129176fe659ebf0e6337632a8967a28d2630b6ae5948665c0c2ae", + "zh:69c15acd73c451e89de6477059cda2f3ec200b48ae4b9ff3646c4d389fd3205e", + "zh:6e02b687de21b844f8266dff99e93e7c61fc8eb688f4bbb23803caceb251839e", + "zh:7a51d17b87ed87b7bebf2ad9fc7c3a74f16a1b44eee92c779c08eb89258c0496", + "zh:88ad84436837b0f55302f22748505972634e87400d6902260fd6b7ba1610f937", "zh:890df766e9b839623b1f0437355032a3c006226a6c200cd911e15ee1a9014e9f", - "zh:8d4aa79f0a414bb4163d771063c70cd991c8fac6c766e685bac2ee12903c5bd6", - "zh:a67540c13565616a7e7e51ee9366e88b0dc60046e1d75c72680e150bd02725bb", - "zh:a936383a4767f5393f38f622e92bf2d0c03fe04b69c284951f27345766c7b31b", - "zh:d4887d73c466ff036eecf50ad6404ba38fd82ea4855296b1846d244b0f13c380", - "zh:e9093c8bd5b6cd99c81666e315197791781b8f93afa14fc2e0f732d1bb2a44b7", - "zh:efd3b3f1ec59a37f635aa1d4efcf178734c2fcf8ddb0d56ea690bec342da8672", + "zh:8d46c3d9f4f7ad20ac6ef01daa63f4e30a2d16dcb1bb5c7c7ee3dc6be38e9ca1", + "zh:913d64e72a4929dae1d4793e2004f4f9a58b138ea337d9d94fa35cafbf06550a", + "zh:c8d93cf86e2e49f6cec665cfe78b82c144cce15a8b2e30f343385fadd1251849", + "zh:cc4f69397d9bc34a528a5609a024c3a48f54f21616c0008792dd417297add955", + "zh:df99cdb8b064aad35ffea77e645cf6541d0b1b2ebc51b6d26c42031de60ab69e", ] } diff --git a/deployment/modules/cloudflare/docs-release/config.tf b/deployment/modules/cloudflare/docs-release/config.tf index cd370f9353..9dd16d5982 100644 --- a/deployment/modules/cloudflare/docs-release/config.tf +++ b/deployment/modules/cloudflare/docs-release/config.tf @@ -5,7 +5,7 @@ terraform { required_providers { cloudflare = { source = "cloudflare/cloudflare" - version = "4.52.0" + version = "4.52.1" } } } diff --git a/deployment/modules/cloudflare/docs/.terraform.lock.hcl b/deployment/modules/cloudflare/docs/.terraform.lock.hcl index 417f262157..90a7bd6259 100644 --- a/deployment/modules/cloudflare/docs/.terraform.lock.hcl +++ b/deployment/modules/cloudflare/docs/.terraform.lock.hcl @@ -2,37 +2,37 @@ # Manual edits may be lost in future updates. provider "registry.opentofu.org/cloudflare/cloudflare" { - version = "4.52.0" - constraints = "4.52.0" + version = "4.52.1" + constraints = "4.52.1" hashes = [ - "h1:2BEJyXJtYC4B4nda/WCYUmuJYDaYk88F8t1pwPzr0iQ=", - "h1:4IASk5SESeWKQ7JU0+M7KApuF5mZyklvwMXPBabim3c=", - "h1:5ImZxxALSnWfH/4EXw/wFirSmk5Tr0ACmcysy51AafE=", - "h1:6TJ3dxLSin4ZKBJLsZDn95H2ZYnGm8S7GGHvvXuuMQU=", - "h1:IzTUjg9kQ4N3qizP9CjYLeHwjsuGgtxwXvfUQWyOLcA=", - "h1:NTaOQfYINA0YTG/V1/9+SYtgX1it63+cBugj4WK4FWc=", - "h1:PXH48LuJn329sCfMXprdMDk51EZaWFyajVvS03qhQLs=", - "h1:Pi5M+GeoMSN2eJ6QnIeXjBf19O+rby/74CfB2ocpv20=", - "h1:ShXZ2ZjBvm3thfoPPzPT8+OhyismnydQVkUAfI8X12w=", - "h1:WQ9hu0Wge2msBbODfottCSKgu8oKUrw4Opz+fDPVVHk=", - "h1:Z5yXML2DE0uH9UU+M0ut9JMQAORcwVZz1CxBHzeBmao=", - "h1:jqI2qKknpleS3JDSplyGYHMu0u9K/tor1ZOjFwDgEMk=", - "h1:kgfutDh14Q5nw4eg6qGFamFxIiY8Ae0FPKRBLDOzpcI=", - "h1:zCAO7GZmfYhWb+i6TfqlqhMeDyPZWGio2IzEzAh3YTs=", - "zh:19be1a91c982b902c42aba47766860dfa5dc151eed1e95fd39ca642229381ef0", - "zh:1de451c4d1ecf7efbe67b6dace3426ba810711afdd644b0f1b870364c8ae91f8", - "zh:352b4a2120173298622e669258744554339d959ac3a95607b117a48ee4a83238", - "zh:3c6f1346d9154afbd2d558fabb4b0150fc8d559aa961254144fe1bc17fe6032f", - "zh:4c4c92d53fb535b1e0eff26f222bbd627b97d3b4c891ec9c321268676d06152f", - "zh:53276f68006c9ceb7cdb10a6ccf91a5c1eadd1407a28edb5741e84e88d7e29e8", - "zh:7925a97773948171a63d4f65bb81ee92fd6d07a447e36012977313293a5435c9", - "zh:7dfb0a4496cfe032437386d0a2cd9229a1956e9c30bd920923c141b0f0440060", + "h1:2lHvafwGbLdmc9lYkuJFw3nsInaQjRpjX/JfIRKmq/M=", + "h1:596JomwjrtUrOSreq9NNCS+rj70+jOV+0pfja5MXiTI=", + "h1:7mBOA5TVAIt3qAwPXKCtE0RSYeqij9v30mnksuBbpEg=", + "h1:ELVgzh4kHKBCYdL+2A8JjWS0E1snLUN3Mmz3Vo6qSfw=", + "h1:FGGM5yLFf72g3kSXM3LAN64Gf/AkXr5WCmhixgnP+l4=", + "h1:JupkJbQALcIVoMhHImrLeLDsQR1ET7VJLGC7ONxjqGU=", + "h1:KsaE4JNq+1uV1nJsuTcYar/8lyY6zKS5UBEpfYg3wvc=", + "h1:NHZ5RJIzQDLhie/ykl3uI6UPfNQR9Lu5Ti7JPR6X904=", + "h1:NfAuMbn6LQPLDtJhbzO1MX9JMIGLMa8K6CpekvtsuX8=", + "h1:e+vNKokamDsp/kJvFr2pRudzwEz2r49iZ/oSggw+1LY=", + "h1:jnb4VdfNZ79I3yj7Q8x+JmOT+FxbfjjRfrF0dL0yCW8=", + "h1:kmF//O539d7NuHU7qIxDj7Wz4eJmLKFiI5glwQivldU=", + "h1:s6XriaKwOgV4jvKAGPXkrxhhOQxpNU5dceZwi9Z/1k8=", + "h1:wt3WBEBAeSGTlC9OlnTlAALxRiK4SQgLy0KgBIS7qzs=", + "zh:2fb95e1d3229b9b6c704e1a413c7481c60f139780d9641f657b6eb9b633b90f2", + "zh:379c7680983383862236e9e6e720c3114195c40526172188e88d0ffcf50dfe2e", + "zh:55533beb6cfc02d22ffda8cba8027bc2c841bb172cd637ed0d28323d41395f8f", + "zh:5abd70760e4eb1f37a1c307cbd2989ea7c9ba0afb93818c67c1d363a31f75703", + "zh:699f1c8cd66129176fe659ebf0e6337632a8967a28d2630b6ae5948665c0c2ae", + "zh:69c15acd73c451e89de6477059cda2f3ec200b48ae4b9ff3646c4d389fd3205e", + "zh:6e02b687de21b844f8266dff99e93e7c61fc8eb688f4bbb23803caceb251839e", + "zh:7a51d17b87ed87b7bebf2ad9fc7c3a74f16a1b44eee92c779c08eb89258c0496", + "zh:88ad84436837b0f55302f22748505972634e87400d6902260fd6b7ba1610f937", "zh:890df766e9b839623b1f0437355032a3c006226a6c200cd911e15ee1a9014e9f", - "zh:8d4aa79f0a414bb4163d771063c70cd991c8fac6c766e685bac2ee12903c5bd6", - "zh:a67540c13565616a7e7e51ee9366e88b0dc60046e1d75c72680e150bd02725bb", - "zh:a936383a4767f5393f38f622e92bf2d0c03fe04b69c284951f27345766c7b31b", - "zh:d4887d73c466ff036eecf50ad6404ba38fd82ea4855296b1846d244b0f13c380", - "zh:e9093c8bd5b6cd99c81666e315197791781b8f93afa14fc2e0f732d1bb2a44b7", - "zh:efd3b3f1ec59a37f635aa1d4efcf178734c2fcf8ddb0d56ea690bec342da8672", + "zh:8d46c3d9f4f7ad20ac6ef01daa63f4e30a2d16dcb1bb5c7c7ee3dc6be38e9ca1", + "zh:913d64e72a4929dae1d4793e2004f4f9a58b138ea337d9d94fa35cafbf06550a", + "zh:c8d93cf86e2e49f6cec665cfe78b82c144cce15a8b2e30f343385fadd1251849", + "zh:cc4f69397d9bc34a528a5609a024c3a48f54f21616c0008792dd417297add955", + "zh:df99cdb8b064aad35ffea77e645cf6541d0b1b2ebc51b6d26c42031de60ab69e", ] } diff --git a/deployment/modules/cloudflare/docs/config.tf b/deployment/modules/cloudflare/docs/config.tf index cd370f9353..9dd16d5982 100644 --- a/deployment/modules/cloudflare/docs/config.tf +++ b/deployment/modules/cloudflare/docs/config.tf @@ -5,7 +5,7 @@ terraform { required_providers { cloudflare = { source = "cloudflare/cloudflare" - version = "4.52.0" + version = "4.52.1" } } } diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml index 56d1817317..d9a321240a 100644 --- a/docker/docker-compose.dev.yml +++ b/docker/docker-compose.dev.yml @@ -16,7 +16,7 @@ name: immich-dev services: immich-server: container_name: immich_server - command: ['/usr/src/app/bin/immich-dev'] + command: ['immich-dev'] image: immich-server-dev:latest # extends: # file: hwaccel.transcoding.yml @@ -27,11 +27,11 @@ services: target: dev restart: unless-stopped volumes: - - ../server:/usr/src/app - - ../open-api:/usr/src/open-api - - ${UPLOAD_LOCATION}/photos:/usr/src/app/upload - - ${UPLOAD_LOCATION}/photos/upload:/usr/src/app/upload/upload - - /usr/src/app/node_modules + - ../server:/usr/src/app/server + - ../open-api:/usr/src/app/open-api + - ${UPLOAD_LOCATION}/photos:/data + - ${UPLOAD_LOCATION}/photos/upload:/data/upload + - /usr/src/app/server/node_modules - /etc/localtime:/etc/localtime:ro env_file: - .env @@ -69,19 +69,20 @@ services: # Needed for rootless docker setup, see https://github.com/moby/moby/issues/45919 # user: 0:0 build: - context: ../web - command: ['/usr/src/app/bin/immich-web'] + context: ../ + dockerfile: web/Dockerfile + command: ['immich-web'] env_file: - .env ports: - 3000:3000 - 24678:24678 volumes: - - ../web:/usr/src/app - - ../i18n:/usr/src/i18n - - ../open-api/:/usr/src/open-api/ + - ../web:/usr/src/app/web + - ../i18n:/usr/src/app/i18n + - ../open-api/:/usr/src/app/open-api/ # - ../../ui:/usr/ui - - /usr/src/app/node_modules + - /usr/src/app/web/node_modules ulimits: nofile: soft: 1048576 @@ -122,7 +123,7 @@ services: database: container_name: immich_postgres - image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:5f6a838e4e44c8e0e019d0ebfe3ee8952b69afc2809b2c25f7b0119641978e91 + image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:32324a2f41df5de9efe1af166b7008c3f55646f8d0e00d9550c16c9822366b4a env_file: - .env environment: diff --git a/docker/docker-compose.prod.yml b/docker/docker-compose.prod.yml index a025ff4cc5..6ed0aeee8e 100644 --- a/docker/docker-compose.prod.yml +++ b/docker/docker-compose.prod.yml @@ -20,7 +20,7 @@ services: context: ../ dockerfile: server/Dockerfile volumes: - - ${UPLOAD_LOCATION}/photos:/usr/src/app/upload + - ${UPLOAD_LOCATION}/photos:/data - /etc/localtime:/etc/localtime:ro env_file: - .env @@ -63,7 +63,7 @@ services: database: container_name: immich_postgres - image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:5f6a838e4e44c8e0e019d0ebfe3ee8952b69afc2809b2c25f7b0119641978e91 + image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:32324a2f41df5de9efe1af166b7008c3f55646f8d0e00d9550c16c9822366b4a env_file: - .env environment: diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 6af0e0aa2a..ed4e921ff6 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -18,7 +18,7 @@ services: # service: cpu # set to one of [nvenc, quicksync, rkmpp, vaapi, vaapi-wsl] for accelerated transcoding volumes: # Do not edit the next line. If you want to change the media storage location on your system, edit the value of UPLOAD_LOCATION in the .env file - - ${UPLOAD_LOCATION}:/usr/src/app/upload + - ${UPLOAD_LOCATION}:/data - /etc/localtime:/etc/localtime:ro env_file: - .env @@ -56,7 +56,7 @@ services: database: container_name: immich_postgres - image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:5f6a838e4e44c8e0e019d0ebfe3ee8952b69afc2809b2c25f7b0119641978e91 + image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:32324a2f41df5de9efe1af166b7008c3f55646f8d0e00d9550c16c9822366b4a environment: POSTGRES_PASSWORD: ${DB_PASSWORD} POSTGRES_USER: ${DB_USERNAME} diff --git a/docs/.nvmrc b/docs/.nvmrc index fc37597bcc..7377d130ed 100644 --- a/docs/.nvmrc +++ b/docs/.nvmrc @@ -1 +1 @@ -22.17.0 +22.17.1 diff --git a/docs/docs/FAQ.mdx b/docs/docs/FAQ.mdx index 9e14d10a0e..e57039a420 100644 --- a/docs/docs/FAQ.mdx +++ b/docs/docs/FAQ.mdx @@ -180,7 +180,7 @@ services: ... volumes: # Do not edit the next line. If you want to change the media storage location on your system, edit the value of UPLOAD_LOCATION in the .env file - - ${UPLOAD_LOCATION}:/usr/src/app/upload + - ${UPLOAD_LOCATION}:/data - /etc/localtime:/etc/localtime:ro + - originals:/usr/src/app/originals ... diff --git a/docs/docs/administration/oauth.md b/docs/docs/administration/oauth.md index 833b70f77a..7450ae1b08 100644 --- a/docs/docs/administration/oauth.md +++ b/docs/docs/administration/oauth.md @@ -64,7 +64,7 @@ Once you have a new OAuth client application configured, Immich can be configure | Storage Label Claim | string | preferred_username | Claim mapping for the user's storage label**¹** | | Role Claim | string | immich_role | Claim mapping for the user's role. (should return "user" or "admin")**¹** | | Storage Quota Claim | string | immich_quota | Claim mapping for the user's storage**¹** | -| Default Storage Quota (GiB) | number | 0 | Default quota for user without storage quota claim (Enter 0 for unlimited quota) | +| Default Storage Quota (GiB) | number | 0 | Default quota for user without storage quota claim (empty for unlimited quota) | | Button Text | string | Login with OAuth | Text for the OAuth button on the web | | Auto Register | boolean | true | When true, will automatically register a user the first time they sign in | | [Auto Launch](#auto-launch) | boolean | false | When true, will skip the login page and automatically start the OAuth login process | @@ -106,6 +106,89 @@ Immich has a route (`/api/oauth/mobile-redirect`) that is already configured to ## Example Configuration +
+Authelia Example + +### Authelia Example + +Here's an example of OAuth configured for Authelia: + +This assumes there exist an attribute `immichquota` in the user schema, which is used to set the user's storage quota in Immich. +The configuration concerning the quota is optional. + +```yaml +authentication_backend: + ldap: + # The LDAP server configuration goes here. + # See: https://www.authelia.com/c/ldap + attributes: + extra: + immichquota: # The attribute name from LDAP + name: 'immich_quota' + multi_valued: false + value_type: 'integer' +identity_providers: + oidc: + ## The other portions of the mandatory OpenID Connect 1.0 configuration go here. + ## See: https://www.authelia.com/c/oidc + claims_policies: + immich_policy: + custom_claims: + immich_quota: + attribute: 'immich_quota' + scopes: + immich_scope: + claims: + - 'immich_quota' + + clients: + - client_id: 'immich' + client_name: 'Immich' + # https://www.authelia.com/integration/openid-connect/frequently-asked-questions/#how-do-i-generate-a-client-identifier-or-client-secret + client_secret: $pbkdf2-sha512$310000$c8p78n7pUMln0jzvd4aK4Q$JNRBzwAo0ek5qKn50cFzzvE9RXV88h1wJn5KGiHrD0YKtZaR/nCb2CJPOsKaPK0hjf.9yHxzQGZziziccp6Yng' + public: false + require_pkce: false + redirect_uris: + - 'https://example.immich.app/auth/login' + - 'https://example.immich.app/user-settings' + - 'app.immich:///oauth-callback' + scopes: + - 'openid' + - 'profile' + - 'email' + - 'immich_scope' + claims_policy: 'immich_policy' + response_types: + - 'code' + grant_types: + - 'authorization_code' + id_token_signed_response_alg: 'RS256' + userinfo_signed_response_alg: 'RS256' + token_endpoint_auth_method: 'client_secret_post' +``` + +Configuration of OAuth in Immich System Settings + +| Setting | Value | +| ---------------------------------- | ------------------------------------------------------------------- | +| Issuer URL | `https://example.immich.app/.well-known/openid-configuration` | +| Client ID | immich | +| Client Secret | 0v89FXkQOWO\***\*\*\*\*\***\*\*\***\*\*\*\*\***mprbvXD549HH6s1iw... | +| Token Endpoint Auth Method | client_secret_post | +| Scope | openid email profile immich_scope | +| ID Token Signed Response Algorithm | RS256 | +| Userinfo Signed Response Algorithm | RS256 | +| Storage Label Claim | uid | +| Storage Quota Claim | immich_quota | +| Default Storage Quota (GiB) | 0 (empty for unlimited quota) | +| Button Text | Sign in with Authelia (optional) | +| Auto Register | Enabled (optional) | +| Auto Launch | Enabled (optional) | +| Mobile Redirect URI Override | Disable | +| Mobile Redirect URI | | + +
+
Authentik Example @@ -128,7 +211,7 @@ Configuration of OAuth in Immich System Settings | Signing Algorithm | RS256 | | Storage Label Claim | preferred_username | | Storage Quota Claim | immich_quota | -| Default Storage Quota (GiB) | 0 (0 for unlimited quota) | +| Default Storage Quota (GiB) | 0 (empty for unlimited quota) | | Button Text | Sign in with Authentik (optional) | | Auto Register | Enabled (optional) | | Auto Launch | Enabled (optional) | @@ -159,7 +242,7 @@ Configuration of OAuth in Immich System Settings | Signing Algorithm | RS256 | | Storage Label Claim | preferred_username | | Storage Quota Claim | immich_quota | -| Default Storage Quota (GiB) | 0 (0 for unlimited quota) | +| Default Storage Quota (GiB) | 0 (empty for unlimited quota) | | Button Text | Sign in with Google (optional) | | Auto Register | Enabled (optional) | | Auto Launch | Enabled | diff --git a/docs/docs/administration/server-commands.md b/docs/docs/administration/server-commands.md index b414f5deaa..a25673abf2 100644 --- a/docs/docs/administration/server-commands.md +++ b/docs/docs/administration/server-commands.md @@ -2,16 +2,17 @@ The `immich-server` docker image comes preinstalled with an administrative CLI (`immich-admin`) that supports the following commands: -| Command | Description | -| ------------------------ | ------------------------------------- | -| `help` | Display help | -| `reset-admin-password` | Reset the password for the admin user | -| `disable-password-login` | Disable password login | -| `enable-password-login` | Enable password login | -| `enable-oauth-login` | Enable OAuth login | -| `disable-oauth-login` | Disable OAuth login | -| `list-users` | List Immich users | -| `version` | Print Immich version | +| Command | Description | +| ------------------------ | ------------------------------------------------------------- | +| `help` | Display help | +| `reset-admin-password` | Reset the password for the admin user | +| `disable-password-login` | Disable password login | +| `enable-password-login` | Enable password login | +| `enable-oauth-login` | Enable OAuth login | +| `disable-oauth-login` | Disable OAuth login | +| `list-users` | List Immich users | +| `version` | Print Immich version | +| `change-media-location` | Change database file paths to align with a new media location | ## How to run a command @@ -88,3 +89,21 @@ Print Immich Version immich-admin version v1.129.0 ``` + +Change media location + +``` +immich-admin change-media-location +? Enter the previous value of IMMICH_MEDIA_LOCATION: /data +? Enter the new value of IMMICH_MEDIA_LOCATION: /my-data +... + Previous value: /data + Current value: /my-data + + Changing database paths from "/data/*" to "/my-data/*" + +? Do you want to proceed? [Y/n] y + +Database file paths updated successfully! 🎉 +... +``` diff --git a/docs/docs/developer/pr-checklist.md b/docs/docs/developer/pr-checklist.md index 58581e669a..8fe3772b03 100644 --- a/docs/docs/developer/pr-checklist.md +++ b/docs/docs/developer/pr-checklist.md @@ -38,6 +38,19 @@ Run all server checks with `npm run check:all` You can use `npm run __:fix` to potentially correct some issues automatically for `npm run format` and `lint`. ::: +## Mobile Checks + +The following commands must be executed from within the mobile app directory of the codebase. + +- [ ] `make build` (auto-generate files using build_runner) +- [ ] `make analyze` (static analysis via Dart Analyzer and DCM) +- [ ] `make format` (formatting via Dart Formatter) +- [ ] `make test` (unit tests) + +:::info Auto Fix +You can use `dart fix --apply` and `dcm fix lib` to potentially correct some issues automatically for `make analyze`. +::: + ## OpenAPI The OpenAPI client libraries need to be regenerated whenever there are changes to the `immich-openapi-specs.json` file. Note that you should not modify this file directly as it is auto-generated. See [OpenAPI](/docs/developer/open-api.md) for more details. diff --git a/docs/docs/features/libraries.md b/docs/docs/features/libraries.md index ad76eb44b2..f274ca3c70 100644 --- a/docs/docs/features/libraries.md +++ b/docs/docs/features/libraries.md @@ -58,7 +58,7 @@ Internally, Immich uses the [glob](https://www.npmjs.com/package/glob) package t This feature is considered experimental and for advanced users only. If enabled, it will allow automatic watching of the filesystem which means new assets are automatically imported to Immich without needing to rescan. -If your photos are on a network drive, automatic file watching likely won't work. In that case, you will have to rely on a periodic library refresh to pull in your changes. +If your photos are on a network drive, automatic file watching likely won't work. In that case, you will have to rely on a [periodic library refresh](#set-custom-scan-interval) to pull in your changes. #### Troubleshooting @@ -72,7 +72,9 @@ In rare cases, the library watcher can hang, preventing Immich from starting up. ### Nightly job -There is an automatic scan job that is scheduled to run once a day. This job also cleans up any libraries stuck in deletion. It is possible to trigger the cleanup by clicking "Scan all libraries" in the library management page. +There is an automatic scan job that is scheduled to run once a day. Its schedule is configurable, see [Set Custom Scan Interval](#set-custom-scan-interval). + +This job also cleans up any libraries stuck in deletion. It is possible to trigger the cleanup by clicking "Scan all libraries" in the library management page. ## Usage @@ -91,7 +93,7 @@ The `immich-server` container will need access to the gallery. Modify your docke ```diff title="docker-compose.yml" immich-server: volumes: - - ${UPLOAD_LOCATION}:/usr/src/app/upload + - ${UPLOAD_LOCATION}:/data + - /mnt/nas/christmas-trip:/mnt/media/christmas-trip:ro + - /home/user/old-pics:/mnt/media/old-pics:ro + - /mnt/media/videos:/mnt/media/videos:ro diff --git a/docs/docs/features/mobile-app.mdx b/docs/docs/features/mobile-app.mdx index 38222479b0..cd837741f1 100644 --- a/docs/docs/features/mobile-app.mdx +++ b/docs/docs/features/mobile-app.mdx @@ -88,9 +88,9 @@ It will only reflect files you add. ::: If the same asset is in more than one album it will only sync to the first album it's in, after that it won't sync again even if the user clicks sync albums manually. -To overcome this limitation, the files must be removed from the blacklist by +To overcome this limitation, the files must be removed from the ignore list by App settings -> Advanced -> Duplicate Assets -> Clear :::info -Cleaning duplicate assets from the list will cause all the previously uploaded duplicate files to be re-uploaded, the files will not actually be uploaded and will be rejected on the server side (due to duplication) but will be synchronized to the album and at the end will be added to the black list again at the end of the synchronization. +Cleaning duplicate assets from the list will cause all the previously uploaded duplicate files to be re-uploaded, the files will not actually be uploaded and will be rejected on the server side (due to duplication) but will be synchronized to the album and at the end will be added to the ignore list again at the end of the synchronization. ::: diff --git a/docs/docs/guides/custom-locations.md b/docs/docs/guides/custom-locations.md index ba5caf4b26..af8ca438e7 100644 --- a/docs/docs/guides/custom-locations.md +++ b/docs/docs/guides/custom-locations.md @@ -27,11 +27,11 @@ After defining the locations of these files, we will edit the `docker-compose.ym services: immich-server: volumes: - - ${UPLOAD_LOCATION}:/usr/src/app/upload -+ - ${THUMB_LOCATION}:/usr/src/app/upload/thumbs -+ - ${ENCODED_VIDEO_LOCATION}:/usr/src/app/upload/encoded-video -+ - ${PROFILE_LOCATION}:/usr/src/app/upload/profile -+ - ${BACKUP_LOCATION}:/usr/src/app/upload/backups + - ${UPLOAD_LOCATION}:/data ++ - ${THUMB_LOCATION}:/data/thumbs ++ - ${ENCODED_VIDEO_LOCATION}:/data/encoded-video ++ - ${PROFILE_LOCATION}:/data/profile ++ - ${BACKUP_LOCATION}:/data/backups - /etc/localtime:/etc/localtime:ro ``` @@ -44,7 +44,7 @@ docker compose up -d :::note Because of the underlying properties of docker bind mounts, it is not recommended to mount the `upload/` and `library/` folders as separate bind mounts if they are on the same device. -For this reason, we mount the HDD or the network storage (NAS) to `/usr/src/app/upload` and then mount the folders we want to access under that folder. +For this reason, we mount the HDD or the network storage (NAS) to `/data` and then mount the folders we want to access under that folder. The `thumbs/` folder contains both the small thumbnails displayed in the timeline and the larger previews shown when clicking into an image. These cannot be separated. diff --git a/docs/docs/guides/database-queries.md b/docs/docs/guides/database-queries.md index 209f673993..267e7bf2ad 100644 --- a/docs/docs/guides/database-queries.md +++ b/docs/docs/guides/database-queries.md @@ -12,118 +12,148 @@ Run `docker exec -it immich_postgres psql --dbname= --username ## Assets +### Name + :::note The `"originalFileName"` column is the name of the file at time of upload, including the extension. ::: ```sql title="Find by original filename" -SELECT * FROM "assets" WHERE "originalFileName" = 'PXL_20230903_232542848.jpg'; -SELECT * FROM "assets" WHERE "originalFileName" LIKE 'PXL_%'; -- all files starting with PXL_ -SELECT * FROM "assets" WHERE "originalFileName" LIKE '%_2023_%'; -- all files with _2023_ in the middle +SELECT * FROM "asset" WHERE "originalFileName" = 'PXL_20230903_232542848.jpg'; +SELECT * FROM "asset" WHERE "originalFileName" LIKE 'PXL_%'; -- all files starting with PXL_ +SELECT * FROM "asset" WHERE "originalFileName" LIKE '%_2023_%'; -- all files with _2023_ in the middle ``` ```sql title="Find by path" -SELECT * FROM "assets" WHERE "originalPath" = 'upload/library/admin/2023/2023-09-03/PXL_2023.jpg'; -SELECT * FROM "assets" WHERE "originalPath" LIKE 'upload/library/admin/2023/%'; +SELECT * FROM "asset" WHERE "originalPath" = 'upload/library/admin/2023/2023-09-03/PXL_2023.jpg'; +SELECT * FROM "asset" WHERE "originalPath" LIKE 'upload/library/admin/2023/%'; ``` +### ID + ```sql title="Find by ID" -SELECT * FROM "assets" WHERE "id" = '9f94e60f-65b6-47b7-ae44-a4df7b57f0e9'; +SELECT * FROM "asset" WHERE "id" = '9f94e60f-65b6-47b7-ae44-a4df7b57f0e9'; ``` ```sql title="Find by partial ID" -SELECT * FROM "assets" WHERE "id"::text LIKE '%ab431d3a%'; +SELECT * FROM "asset" WHERE "id"::text LIKE '%ab431d3a%'; ``` +### Checksum + :::note You can calculate the checksum for a particular file by using the command `sha1sum `. ::: ```sql title="Find by checksum (SHA-1)" -SELECT encode("checksum", 'hex') FROM "assets"; -SELECT * FROM "assets" WHERE "checksum" = decode('69de19c87658c4c15d9cacb9967b8e033bf74dd1', 'hex'); -SELECT * FROM "assets" WHERE "checksum" = '\x69de19c87658c4c15d9cacb9967b8e033bf74dd1'; -- alternate notation +SELECT encode("checksum", 'hex') FROM "asset"; +SELECT * FROM "asset" WHERE "checksum" = decode('69de19c87658c4c15d9cacb9967b8e033bf74dd1', 'hex'); +SELECT * FROM "asset" WHERE "checksum" = '\x69de19c87658c4c15d9cacb9967b8e033bf74dd1'; -- alternate notation ``` ```sql title="Find duplicate assets with identical checksum (SHA-1) (excluding trashed files)" -SELECT T1."checksum", array_agg(T2."id") ids FROM "assets" T1 - INNER JOIN "assets" T2 ON T1."checksum" = T2."checksum" AND T1."id" != T2."id" AND T2."deletedAt" IS NULL +SELECT T1."checksum", array_agg(T2."id") ids FROM "asset" T1 + INNER JOIN "asset" T2 ON T1."checksum" = T2."checksum" AND T1."id" != T2."id" AND T2."deletedAt" IS NULL WHERE T1."deletedAt" IS NULL GROUP BY T1."checksum"; ``` +### Metadata + ```sql title="Live photos" -SELECT * FROM "assets" WHERE "livePhotoVideoId" IS NOT NULL; +SELECT * FROM "asset" WHERE "livePhotoVideoId" IS NOT NULL; ``` ```sql title="By description" -SELECT "assets".*, "exif"."description" FROM "exif" - JOIN "assets" ON "assets"."id" = "exif"."assetId" - WHERE TRIM("exif"."description") <> ''; -- all files with a description -SELECT "assets".*, "exif"."description" FROM "exif" - JOIN "assets" ON "assets"."id" = "exif"."assetId" - WHERE "exif"."description" ILIKE '%string to match%'; -- search by string +SELECT "asset".*, "asset_exif"."description" FROM "asset_exif" + JOIN "asset" ON "asset"."id" = "asset_exif"."assetId" + WHERE TRIM("asset_exif"."description") <> ''; -- all files with a description +SELECT "asset".*, "asset_exif"."description" FROM "asset_exif" + JOIN "asset" ON "asset"."id" = "asset_exif"."assetId" + WHERE "asset_exif"."description" ILIKE '%string to match%'; -- search by string ``` ```sql title="Without metadata" -SELECT "assets".* FROM "exif" - LEFT JOIN "assets" ON "assets"."id" = "exif"."assetId" - WHERE "exif"."assetId" IS NULL; +SELECT "asset".* FROM "asset_exif" + LEFT JOIN "asset" ON "asset"."id" = "asset_exif"."assetId" + WHERE "asset_exif"."assetId" IS NULL; ``` ```sql title="size < 100,000 bytes, smallest to largest" -SELECT * FROM "assets" - JOIN "exif" ON "assets"."id" = "exif"."assetId" - WHERE "exif"."fileSizeInByte" < 100000 - ORDER BY "exif"."fileSizeInByte" ASC; +SELECT * FROM "asset" + JOIN "asset_exif" ON "asset"."id" = "asset_exif"."assetId" + WHERE "asset_exif"."fileSizeInByte" < 100000 + ORDER BY "asset_exif"."fileSizeInByte" ASC; ``` -```sql title="Without thumbnails" -SELECT * FROM "assets" WHERE "assets"."previewPath" IS NULL OR "assets"."thumbnailPath" IS NULL; -``` +### Type ```sql title="By type" -SELECT * FROM "assets" WHERE "assets"."type" = 'VIDEO'; -SELECT * FROM "assets" WHERE "assets"."type" = 'IMAGE'; +SELECT * FROM "asset" WHERE "asset"."type" = 'VIDEO'; +SELECT * FROM "asset" WHERE "asset"."type" = 'IMAGE'; ``` ```sql title="Count by type" -SELECT "assets"."type", COUNT(*) FROM "assets" GROUP BY "assets"."type"; +SELECT "asset"."type", COUNT(*) FROM "asset" GROUP BY "asset"."type"; ``` ```sql title="Count by type (per user)" -SELECT "users"."email", "assets"."type", COUNT(*) FROM "assets" - JOIN "users" ON "assets"."ownerId" = "users"."id" - GROUP BY "assets"."type", "users"."email" ORDER BY "users"."email"; +SELECT "user"."email", "asset"."type", COUNT(*) FROM "asset" + JOIN "user" ON "asset"."ownerId" = "user"."id" + GROUP BY "asset"."type", "user"."email" ORDER BY "user"."email"; ``` -```sql title="Failed file movements" -SELECT * FROM "move_history"; +## Tags + +```sql title="Count by tag" +SELECT "t"."value" AS "tag_name", COUNT(*) AS "number_assets" FROM "tag" "t" + JOIN "tag_asset" "ta" ON "t"."id" = "ta"."tagsId" JOIN "asset" "a" ON "ta"."assetsId" = "a"."id" + WHERE "a"."visibility" != 'hidden' + GROUP BY "t"."value" ORDER BY "number_assets" DESC; +``` + +```sql title="Count by tag (per user)" +SELECT "t"."value" AS "tag_name", "u"."email" as "user_email", COUNT(*) AS "number_assets" FROM "tag" "t" + JOIN "tag_asset" "ta" ON "t"."id" = "ta"."tagsId" JOIN "asset" "a" ON "ta"."assetsId" = "a"."id" JOIN "user" "u" ON "a"."ownerId" = "u"."id" + WHERE "a"."visibility" != 'hidden' + GROUP BY "t"."value", "u"."email" ORDER BY "number_assets" DESC; ``` ## Users ```sql title="List all users" -SELECT * FROM "users"; +SELECT * FROM "user"; ``` ```sql title="Get owner info from asset ID" -SELECT "users".* FROM "users" JOIN "assets" ON "users"."id" = "assets"."ownerId" WHERE "assets"."id" = 'fa310b01-2f26-4b7a-9042-d578226e021f'; +SELECT "user".* FROM "user" JOIN "asset" ON "user"."id" = "asset"."ownerId" WHERE "asset"."id" = 'fa310b01-2f26-4b7a-9042-d578226e021f'; ``` -## System Config - -```sql title="Custom settings" -SELECT "key", "value" FROM "system_metadata" WHERE "key" = 'system-config'; -``` - -(Only used when not using the [config file](/docs/install/config-file)) - ## Persons ```sql title="Delete person and unset it for the faces it was associated with" DELETE FROM "person" WHERE "name" = 'PersonNameHere'; ``` +## System + +### Config + +```sql title="Custom settings" +SELECT "key", "value" FROM "system_metadata" WHERE "key" = 'system-config'; +``` + +(Only used when not using the [config file](/docs/install/config-file)) + +### File properties + +```sql title="Without thumbnails" +SELECT * FROM "asset" WHERE "asset"."previewPath" IS NULL OR "asset"."thumbnailPath" IS NULL; +``` + +```sql title="Failed file movements" +SELECT * FROM "move_history"; +``` + ## Postgres internal ```sql title="Change DB_PASSWORD" diff --git a/docs/docs/guides/external-library.md b/docs/docs/guides/external-library.md index 3ad1679423..7921843297 100644 --- a/docs/docs/guides/external-library.md +++ b/docs/docs/guides/external-library.md @@ -12,7 +12,7 @@ If you want Immich to be able to delete the images in the external library or ad ```diff immich-server: volumes: - - ${UPLOAD_LOCATION}:/usr/src/app/upload + - ${UPLOAD_LOCATION}:/data + - /home/user/photos1:/home/user/photos1:ro + - /mnt/photos2:/mnt/photos2:ro # you can delete this line if you only have one mount point, or you can add more lines if you have more than two ``` diff --git a/docs/docs/install/environment-variables.md b/docs/docs/install/environment-variables.md index b9dab5b9c8..928e0b26e5 100644 --- a/docs/docs/install/environment-variables.md +++ b/docs/docs/install/environment-variables.md @@ -34,7 +34,7 @@ These environment variables are used by the `docker-compose.yml` file and do **N | `TZ` | Timezone | \*1 | server | microservices | | `IMMICH_ENV` | Environment (production, development) | `production` | server, machine learning | api, microservices | | `IMMICH_LOG_LEVEL` | Log level (verbose, debug, log, warn, error) | `log` | server, machine learning | api, microservices | -| `IMMICH_MEDIA_LOCATION` | Media location inside the container ⚠️**You probably shouldn't set this**\*2⚠️ | `./upload`\*3 | server | api, microservices | +| `IMMICH_MEDIA_LOCATION` | Media location inside the container ⚠️**You probably shouldn't set this**\*2⚠️ | `/data` | server | api, microservices | | `IMMICH_CONFIG_FILE` | Path to config file | | server | api, microservices | | `NO_COLOR` | Set to `true` to disable color-coded log output | `false` | server, machine learning | | | `CPU_CORES` | Number of cores available to the Immich server | auto-detected CPU core count | server | | @@ -49,9 +49,6 @@ These environment variables are used by the `docker-compose.yml` file and do **N \*2: This path is where the Immich code looks for the files, which is internal to the docker container. Setting it to a path on your host will certainly break things, you should use the `UPLOAD_LOCATION` variable instead. -\*3: With the default `WORKDIR` of `/usr/src/app`, this path will resolve to `/usr/src/app/upload`. -It only needs to be set if the Immich deployment method is changing. - ## Workers | Variable | Description | Default | Containers | @@ -202,12 +199,11 @@ Additional machine learning parameters can be tuned from the admin UI. | `IMMICH_TELEMETRY_INCLUDE` | Collect these telemetries. List of `host`, `api`, `io`, `repo`, `job`. Note: You can also specify `all` to enable all | | server | api, microservices | | `IMMICH_TELEMETRY_EXCLUDE` | Do not collect these telemetries. List of `host`, `api`, `io`, `repo`, `job` | | server | api, microservices | -## Docker Secrets +## Secrets -The following variables support the use of [Docker secrets][docker-secrets] for additional security. +The following variables support reading from files, either via [Systemd Credentials][systemd-creds] or [Docker secrets][docker-secrets] for additional security. -To use any of these, replace the regular environment variable with the equivalent `_FILE` environment variable. The value of -the `_FILE` variable should be set to the path of a file containing the variable value. +To use any of these, either set `CREDENTIALS_DIRECTORY` to a directory that contains files whose name is the “regular variable” name, and whose content is the secret. If using Docker Secrets, setting `CREDENTIALS_DIRECTORY=/run/secrets` will cause all secrets present to be used. Alternatively, replace the regular variable with the equivalent `_FILE` environment variable as below. The value of the `_FILE` variable should be set to the path of a file containing the variable value. | Regular Variable | Equivalent Docker Secrets '\_FILE' Variable | | :----------------- | :------------------------------------------ | @@ -229,3 +225,4 @@ to use a Docker secret for the password in the Redis container. [docker-secrets-docs]: https://github.com/docker-library/docs/tree/master/postgres#docker-secrets [docker-secrets]: https://docs.docker.com/engine/swarm/secrets/ [ioredis]: https://ioredis.readthedocs.io/en/latest/README/#connect-to-redis +[systemd-creds]: https://systemd.io/CREDENTIALS/ diff --git a/docs/package.json b/docs/package.json index b1faa4cfcb..97072d8eb5 100644 --- a/docs/package.json +++ b/docs/package.json @@ -59,6 +59,6 @@ "node": ">=20" }, "volta": { - "node": "22.17.0" + "node": "22.17.1" } } diff --git a/docs/src/components/community-projects.tsx b/docs/src/components/community-projects.tsx index 03a384162b..46e28b3b76 100644 --- a/docs/src/components/community-projects.tsx +++ b/docs/src/components/community-projects.tsx @@ -100,6 +100,11 @@ const projects: CommunityProjectProps[] = [ description: 'Automatically optimize files uploaded to Immich in order to save storage space', url: 'https://github.com/miguelangel-nubla/immich-upload-optimizer', }, + { + title: 'Immich Machine Learning Load Balancer', + description: 'Speed up your machine learning by load balancing your requests to multiple computers', + url: 'https://github.com/apetersson/immich_ml_balancer', + }, ]; function CommunityProject({ title, description, url }: CommunityProjectProps): JSX.Element { diff --git a/docs/src/pages/errors.md b/docs/src/pages/errors.md index e9bf09770c..5f73162a61 100644 --- a/docs/src/pages/errors.md +++ b/docs/src/pages/errors.md @@ -2,4 +2,23 @@ ## TypeORM Upgrade -The upgrade to Immich `v2.x.x` has a required upgrade path to `v1.132.0+`. This means it is required to start up the application at least once on version `1.132.0` (or later). Doing so will complete database schema upgrades that are required for `v2.0.0`. After Immich has successfully booted on this version, shut the system down and try the `v2.x.x` upgrade again. +In order to update to Immich to `v1.137.0` (or above), the application must be started at least once on a version in the range between `1.132.0` and `1.136.0`. Doing so will complete database schema upgrades that are required for `v1.137.0` (and above). After Immich has successfully updated to a version in this range, you can now attempt to update to v1.137.0 (or above). We recommend users upgrade to `1.132.0` since it does not have any other breaking changes. + +## Inconsistent Media Location + +:::caution +This error is related to the location of media files _inside the container_. Never move files on the host system when you run into this error message. +::: + +Immich automatically tries to detect where your Immich data is located. On start up, it compares the detected media location with the file paths in the database and throws an Inconsistent Media Location error when they do not match. + +To fix this issue, verify that the `IMMICH_MEDIA_LOCATION` environment variable and `UPLOAD_LOCATION` volume mount are in sync with the database paths. + +If you would like to migrate from one media location to another, simply successfully start Immich on `v1.136.0` or later, then do the following steps: + +1. Stop Immich +2. Update `IMMICH_MEDIA_LOCATION` to the new location +3. Update the right-hand side of the `UPLOAD_LOCATION` volume mount to the new location +4. Start up Immich + +After version `1.136.0`, Immich can detect when a media location has moved and will automatically update the database paths to keep them in sync. diff --git a/docs/static/archived-versions.json b/docs/static/archived-versions.json index adbd9aa717..04c6604a14 100644 --- a/docs/static/archived-versions.json +++ b/docs/static/archived-versions.json @@ -1,4 +1,24 @@ [ + { + "label": "v1.137.3", + "url": "https://v1.137.3.archive.immich.app" + }, + { + "label": "v1.137.2", + "url": "https://v1.137.2.archive.immich.app" + }, + { + "label": "v1.137.1", + "url": "https://v1.137.1.archive.immich.app" + }, + { + "label": "v1.137.0", + "url": "https://v1.137.0.archive.immich.app" + }, + { + "label": "v1.136.0", + "url": "https://v1.136.0.archive.immich.app" + }, { "label": "v1.135.3", "url": "https://v1.135.3.archive.immich.app" diff --git a/e2e/.nvmrc b/e2e/.nvmrc index fc37597bcc..7377d130ed 100644 --- a/e2e/.nvmrc +++ b/e2e/.nvmrc @@ -1 +1 @@ -22.17.0 +22.17.1 diff --git a/e2e/docker-compose.yml b/e2e/docker-compose.yml index 6ec5402360..b923eb0579 100644 --- a/e2e/docker-compose.yml +++ b/e2e/docker-compose.yml @@ -3,7 +3,6 @@ name: immich-e2e services: immich-server: container_name: immich-e2e-server - command: ['./start.sh'] image: immich-server:latest build: context: ../ @@ -36,10 +35,10 @@ services: - 2285:2285 redis: - image: redis:6.2-alpine@sha256:03fd052257735b41cd19f3d8ae9782926bf9b704fb6a9dc5e29f9ccfbe8827f0 + image: redis:6.2-alpine@sha256:7fe72c486b910f6b1a9769c937dad5d63648ddee82e056f47417542dd40825bb database: - image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0@sha256:3aef84a0a4fabbda17ef115c3019ba0c914ec73e9f6e59203674322d858b8eea + image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0@sha256:0e763a2383d56f90364fcd72767ac41400cd30d2627f407f7e7960c9f1923c21 command: -c fsync=off -c shared_preload_libraries=vchord.so -c config_file=/var/lib/postgresql/data/postgresql.conf environment: POSTGRES_PASSWORD: postgres diff --git a/e2e/package-lock.json b/e2e/package-lock.json index 1e2f8b47e1..319f1b34b3 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -1,12 +1,12 @@ { "name": "immich-e2e", - "version": "1.135.3", + "version": "1.137.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich-e2e", - "version": "1.135.3", + "version": "1.137.3", "license": "GNU Affero General Public License version 3", "devDependencies": { "@eslint/eslintrc": "^3.1.0", @@ -16,14 +16,14 @@ "@playwright/test": "^1.44.1", "@socket.io/component-emitter": "^3.1.2", "@types/luxon": "^3.4.2", - "@types/node": "^22.15.33", + "@types/node": "^22.16.5", "@types/oidc-provider": "^9.0.0", "@types/pg": "^8.15.1", "@types/pngjs": "^6.0.4", "@types/supertest": "^6.0.2", "@vitest/coverage-v8": "^3.0.0", "eslint": "^9.14.0", - "eslint-config-prettier": "^10.0.0", + "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-unicorn": "^59.0.0", "exiftool-vendored": "^28.3.1", @@ -46,7 +46,7 @@ }, "../cli": { "name": "@immich/cli", - "version": "2.2.72", + "version": "2.2.77", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { @@ -68,13 +68,13 @@ "@types/lodash-es": "^4.17.12", "@types/micromatch": "^4.0.9", "@types/mock-fs": "^4.13.1", - "@types/node": "^22.15.33", + "@types/node": "^22.16.5", "@vitest/coverage-v8": "^3.0.0", "byte-size": "^9.0.0", "cli-progress": "^3.12.0", "commander": "^12.0.0", "eslint": "^9.14.0", - "eslint-config-prettier": "^10.0.0", + "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-unicorn": "^59.0.0", "globals": "^16.0.0", @@ -95,14 +95,14 @@ }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.135.3", + "version": "1.137.3", "dev": true, "license": "GNU Affero General Public License version 3", "dependencies": { "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.15.33", + "@types/node": "^22.16.5", "typescript": "^5.3.3" } }, @@ -181,9 +181,9 @@ } }, "node_modules/@emnapi/runtime": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz", - "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==", + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.5.tgz", + "integrity": "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==", "dev": true, "license": "MIT", "optional": true, @@ -734,9 +734,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.30.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.30.1.tgz", - "integrity": "sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg==", + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", + "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", "dev": true, "license": "MIT", "engines": { @@ -837,9 +837,9 @@ } }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.2.tgz", - "integrity": "sha512-OfXHZPppddivUJnqyKoi5YVeHRkkNE2zUFT2gbpKxp/JZCFYEYubnMg+gOp6lWfasPrTS+KPosKqdI+ELYVDtg==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.3.tgz", + "integrity": "sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==", "cpu": [ "arm64" ], @@ -856,13 +856,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.1.0" + "@img/sharp-libvips-darwin-arm64": "1.2.0" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.2.tgz", - "integrity": "sha512-dYvWqmjU9VxqXmjEtjmvHnGqF8GrVjM2Epj9rJ6BUIXvk8slvNDJbhGFvIoXzkDhrJC2jUxNLz/GUjjvSzfw+g==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.3.tgz", + "integrity": "sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==", "cpu": [ "x64" ], @@ -879,13 +879,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.1.0" + "@img/sharp-libvips-darwin-x64": "1.2.0" } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.1.0.tgz", - "integrity": "sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.0.tgz", + "integrity": "sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==", "cpu": [ "arm64" ], @@ -900,9 +900,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.1.0.tgz", - "integrity": "sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.0.tgz", + "integrity": "sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==", "cpu": [ "x64" ], @@ -917,9 +917,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.1.0.tgz", - "integrity": "sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.0.tgz", + "integrity": "sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==", "cpu": [ "arm" ], @@ -934,9 +934,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.1.0.tgz", - "integrity": "sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.0.tgz", + "integrity": "sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==", "cpu": [ "arm64" ], @@ -951,9 +951,9 @@ } }, "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.1.0.tgz", - "integrity": "sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.0.tgz", + "integrity": "sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==", "cpu": [ "ppc64" ], @@ -968,9 +968,9 @@ } }, "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.1.0.tgz", - "integrity": "sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.0.tgz", + "integrity": "sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==", "cpu": [ "s390x" ], @@ -985,9 +985,9 @@ } }, "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.1.0.tgz", - "integrity": "sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.0.tgz", + "integrity": "sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==", "cpu": [ "x64" ], @@ -1002,9 +1002,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.1.0.tgz", - "integrity": "sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.0.tgz", + "integrity": "sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==", "cpu": [ "arm64" ], @@ -1019,9 +1019,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.1.0.tgz", - "integrity": "sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.0.tgz", + "integrity": "sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==", "cpu": [ "x64" ], @@ -1036,9 +1036,9 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.2.tgz", - "integrity": "sha512-0DZzkvuEOqQUP9mo2kjjKNok5AmnOr1jB2XYjkaoNRwpAYMDzRmAqUIa1nRi58S2WswqSfPOWLNOr0FDT3H5RQ==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.3.tgz", + "integrity": "sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==", "cpu": [ "arm" ], @@ -1055,13 +1055,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.1.0" + "@img/sharp-libvips-linux-arm": "1.2.0" } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.2.tgz", - "integrity": "sha512-D8n8wgWmPDakc83LORcfJepdOSN6MvWNzzz2ux0MnIbOqdieRZwVYY32zxVx+IFUT8er5KPcyU3XXsn+GzG/0Q==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.3.tgz", + "integrity": "sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==", "cpu": [ "arm64" ], @@ -1078,13 +1078,36 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.1.0" + "@img/sharp-libvips-linux-arm64": "1.2.0" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.3.tgz", + "integrity": "sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.0" } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.2.tgz", - "integrity": "sha512-EGZ1xwhBI7dNISwxjChqBGELCWMGDvmxZXKjQRuqMrakhO8QoMgqCrdjnAqJq/CScxfRn+Bb7suXBElKQpPDiw==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.3.tgz", + "integrity": "sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==", "cpu": [ "s390x" ], @@ -1101,13 +1124,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.1.0" + "@img/sharp-libvips-linux-s390x": "1.2.0" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.2.tgz", - "integrity": "sha512-sD7J+h5nFLMMmOXYH4DD9UtSNBD05tWSSdWAcEyzqW8Cn5UxXvsHAxmxSesYUsTOBmUnjtxghKDl15EvfqLFbQ==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.3.tgz", + "integrity": "sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==", "cpu": [ "x64" ], @@ -1124,13 +1147,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.1.0" + "@img/sharp-libvips-linux-x64": "1.2.0" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.2.tgz", - "integrity": "sha512-NEE2vQ6wcxYav1/A22OOxoSOGiKnNmDzCYFOZ949xFmrWZOVII1Bp3NqVVpvj+3UeHMFyN5eP/V5hzViQ5CZNA==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.3.tgz", + "integrity": "sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==", "cpu": [ "arm64" ], @@ -1147,13 +1170,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.1.0" + "@img/sharp-libvips-linuxmusl-arm64": "1.2.0" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.2.tgz", - "integrity": "sha512-DOYMrDm5E6/8bm/yQLCWyuDJwUnlevR8xtF8bs+gjZ7cyUNYXiSf/E8Kp0Ss5xasIaXSHzb888V1BE4i1hFhAA==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.3.tgz", + "integrity": "sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==", "cpu": [ "x64" ], @@ -1170,13 +1193,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.1.0" + "@img/sharp-libvips-linuxmusl-x64": "1.2.0" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.2.tgz", - "integrity": "sha512-/VI4mdlJ9zkaq53MbIG6rZY+QRN3MLbR6usYlgITEzi4Rpx5S6LFKsycOQjkOGmqTNmkIdLjEvooFKwww6OpdQ==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.3.tgz", + "integrity": "sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==", "cpu": [ "wasm32" ], @@ -1184,7 +1207,7 @@ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.4.3" + "@emnapi/runtime": "^1.4.4" }, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" @@ -1194,9 +1217,9 @@ } }, "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.2.tgz", - "integrity": "sha512-cfP/r9FdS63VA5k0xiqaNaEoGxBg9k7uE+RQGzuK9fHt7jib4zAVVseR9LsE4gJcNWgT6APKMNnCcnyOtmSEUQ==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.3.tgz", + "integrity": "sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==", "cpu": [ "arm64" ], @@ -1214,9 +1237,9 @@ } }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.2.tgz", - "integrity": "sha512-QLjGGvAbj0X/FXl8n1WbtQ6iVBpWU7JO94u/P2M4a8CFYsvQi4GW2mRy/JqkRx0qpBzaOdKJKw8uc930EX2AHw==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.3.tgz", + "integrity": "sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==", "cpu": [ "ia32" ], @@ -1234,9 +1257,9 @@ } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.2.tgz", - "integrity": "sha512-aUdT6zEYtDKCaxkofmmJDJYGCf0+pJg3eU9/oBuqvEeoB9dKI6ZLc/1iLJCTuJQDO4ptntAlkUmHgGjyuobZbw==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.3.tgz", + "integrity": "sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==", "cpu": [ "x64" ], @@ -1356,12 +1379,13 @@ } }, "node_modules/@koa/router": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/@koa/router/-/router-13.1.0.tgz", - "integrity": "sha512-mNVu1nvkpSd8Q8gMebGbCkDWJ51ODetrFvLKYusej+V0ByD4btqHYnPIzTBLXnQMVUlm/oxVwqmWBY3zQfZilw==", + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/@koa/router/-/router-13.1.1.tgz", + "integrity": "sha512-JQEuMANYRVHs7lm7KY9PCIjkgJk73h4m4J+g2mkw2Vo1ugPZ17UJVqEH8F+HeAdjKz5do1OaLe7ArDz+z308gw==", "dev": true, "license": "MIT", "dependencies": { + "debug": "^4.4.1", "http-errors": "^2.0.0", "koa-compose": "^4.1.0", "path-to-regexp": "^6.3.0" @@ -1510,13 +1534,13 @@ } }, "node_modules/@playwright/test": { - "version": "1.53.2", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.53.2.tgz", - "integrity": "sha512-tEB2U5z74ebBeyfGNZ3Jfg29AnW+5HlWhvHtb/Mqco9pFdZU1ZLNdVb2UtB5CvmiilNr2ZfVH/qMmAROG/XTzw==", + "version": "1.54.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.54.1.tgz", + "integrity": "sha512-FS8hQ12acieG2dYSksmLOF7BNxnVf2afRJdCuM1eMSxj6QTSE6G4InGF7oApGgDb65MX7AwMVlIkpru0yZA4Xw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright": "1.53.2" + "playwright": "1.54.1" }, "bin": { "playwright": "cli.js" @@ -1996,9 +2020,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.15.34", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.34.tgz", - "integrity": "sha512-8Y6E5WUupYy1Dd0II32BsWAx5MWdcnRd8L84Oys3veg1YrYtNtzgO4CFhiBg6MDSjk7Ay36HYOnU7/tuOzIzcw==", + "version": "22.16.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.16.5.tgz", + "integrity": "sha512-bJFoMATwIGaxxx8VJPeM8TonI8t579oRvgAuT8zFugJsJZgzqv0Fu8Mhp68iecjzG7cnN3mO2dJQ5uUM2EFrgQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2101,17 +2125,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.36.0.tgz", - "integrity": "sha512-lZNihHUVB6ZZiPBNgOQGSxUASI7UJWhT8nHyUGCnaQ28XFCw98IfrMCG3rUl1uwUWoAvodJQby2KTs79UTcrAg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz", + "integrity": "sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/type-utils": "8.36.0", - "@typescript-eslint/utils": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/type-utils": "8.38.0", + "@typescript-eslint/utils": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -2125,7 +2149,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.36.0", + "@typescript-eslint/parser": "^8.38.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } @@ -2141,16 +2165,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.36.0.tgz", - "integrity": "sha512-FuYgkHwZLuPbZjQHzJXrtXreJdFMKl16BFYyRrLxDhWr6Qr7Kbcu2s1Yhu8tsiMXw1S0W1pjfFfYEt+R604s+Q==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.38.0.tgz", + "integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "debug": "^4.3.4" }, "engines": { @@ -2166,14 +2190,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.36.0.tgz", - "integrity": "sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz", + "integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.36.0", - "@typescript-eslint/types": "^8.36.0", + "@typescript-eslint/tsconfig-utils": "^8.38.0", + "@typescript-eslint/types": "^8.38.0", "debug": "^4.3.4" }, "engines": { @@ -2188,14 +2212,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.36.0.tgz", - "integrity": "sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz", + "integrity": "sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0" + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2206,9 +2230,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.36.0.tgz", - "integrity": "sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz", + "integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==", "dev": true, "license": "MIT", "engines": { @@ -2223,14 +2247,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.36.0.tgz", - "integrity": "sha512-5aaGYG8cVDd6cxfk/ynpYzxBRZJk7w/ymto6uiyUFtdCozQIsQWh7M28/6r57Fwkbweng8qAzoMCPwSJfWlmsg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.38.0.tgz", + "integrity": "sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/utils": "8.36.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -2247,9 +2272,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.36.0.tgz", - "integrity": "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz", + "integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==", "dev": true, "license": "MIT", "engines": { @@ -2261,16 +2286,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.36.0.tgz", - "integrity": "sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz", + "integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.36.0", - "@typescript-eslint/tsconfig-utils": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/project-service": "8.38.0", + "@typescript-eslint/tsconfig-utils": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -2316,16 +2341,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.36.0.tgz", - "integrity": "sha512-VOqmHu42aEMT+P2qYjylw6zP/3E/HvptRwdn/PZxyV27KhZg2IOszXod4NcXisWzPAGSS4trE/g4moNj6XmH2g==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz", + "integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0" + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2340,13 +2365,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.36.0.tgz", - "integrity": "sha512-vZrhV2lRPWDuGoxcmrzRZyxAggPL+qp3WzUrlZD+slFueDiYHxeBa34dUXPuC0RmGKzl4lS5kFJYvKCq9cnNDA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz", + "integrity": "sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.36.0", + "@typescript-eslint/types": "8.38.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -3439,9 +3464,9 @@ } }, "node_modules/eslint": { - "version": "9.30.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.30.1.tgz", - "integrity": "sha512-zmxXPNMOXmwm9E0yQLi5uqXHs7uq2UIiqEKo3Gq+3fwo1XrJ+hijAZImyF7hclW3E6oHz43Yk3RP8at6OTKflQ==", + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", + "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3449,9 +3474,9 @@ "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", "@eslint/config-helpers": "^0.3.0", - "@eslint/core": "^0.14.0", + "@eslint/core": "^0.15.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.30.1", + "@eslint/js": "9.31.0", "@eslint/plugin-kit": "^0.3.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -3500,9 +3525,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz", - "integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==", + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", "bin": { @@ -3516,9 +3541,9 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.1.tgz", - "integrity": "sha512-dobTkHT6XaEVOo8IO90Q4DOSxnm3Y151QxPJlM/vKC0bVy+d6cVWQZLlFiuZPP0wS6vZwSKeJgKkcS+KfMBlRw==", + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.3.tgz", + "integrity": "sha512-NAdMYww51ehKfDyDhv59/eIItUVzU0Io9H2E8nHNGKEeeqlnci+1gCvrHib6EmZdf6GxF+LCV5K7UC65Ezvw7w==", "dev": true, "license": "MIT", "dependencies": { @@ -3638,6 +3663,19 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/@eslint/core": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", + "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/espree": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", @@ -3945,15 +3983,16 @@ } }, "node_modules/form-data": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { @@ -5154,17 +5193,17 @@ } }, "node_modules/oidc-provider": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/oidc-provider/-/oidc-provider-9.2.0.tgz", - "integrity": "sha512-L0JL1ymI/hLKzDRqYzhKluNfRRQUR0++q5fTTziniKmJgNrJ6DnI5h5SP6w8Z0U/3wZrCndpVmbbu0VpKpY0CA==", + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/oidc-provider/-/oidc-provider-9.4.0.tgz", + "integrity": "sha512-1mEUejJq7cQV/b6cw2nitqOyIlOJTfQ6RNwGFcA7/Pp+vKIWBn8p48ylFtogP3Hbvrkf9s9W5HUeFe+v1KpcEQ==", "dev": true, "license": "MIT", "dependencies": { "@koa/cors": "^5.0.0", - "@koa/router": "^13.1.0", + "@koa/router": "^13.1.1", "debug": "^4.4.1", "eta": "^3.5.0", - "jose": "^6.0.11", + "jose": "^6.0.12", "jsesc": "^3.1.0", "koa": "^3.0.0", "nanoid": "^5.1.5", @@ -5177,9 +5216,9 @@ } }, "node_modules/oidc-provider/node_modules/jose": { - "version": "6.0.11", - "resolved": "https://registry.npmjs.org/jose/-/jose-6.0.11.tgz", - "integrity": "sha512-QxG7EaliDARm1O1S8BGakqncGT9s25bKL1WSf6/oa17Tkqwi8D2ZNglqCF+DsYF88/rV66Q/Q2mFAy697E1DUg==", + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.0.12.tgz", + "integrity": "sha512-T8xypXs8CpmiIi78k0E+Lk7T2zlK4zDyg+o1CZ4AkOHgDg98ogdP2BeZ61lTFKFyoEwJ9RgAgN+SdM3iPgNonQ==", "dev": true, "license": "MIT", "funding": { @@ -5488,13 +5527,13 @@ } }, "node_modules/playwright": { - "version": "1.53.2", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.53.2.tgz", - "integrity": "sha512-6K/qQxVFuVQhRQhFsVZ9fGeatxirtrpPgxzBYWyZLEXJzqYwuL4fuNmfOfD5et1tJE4GScKyPNeLhZeRwuTU3A==", + "version": "1.54.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.54.1.tgz", + "integrity": "sha512-peWpSwIBmSLi6aW2auvrUtf2DqY16YYcCMO8rTVx486jKmDTJg7UAhyrraP98GB8BoPURZP8+nxO7TSd4cPr5g==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.53.2" + "playwright-core": "1.54.1" }, "bin": { "playwright": "cli.js" @@ -5507,9 +5546,9 @@ } }, "node_modules/playwright-core": { - "version": "1.53.2", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.53.2.tgz", - "integrity": "sha512-ox/OytMy+2w1jcYEYlOo1Hhp8hZkLCximMTUTMBXjGUA1KoFfiSZ+DU+3a739jsPY0yoKH2TFy9S2fsJas8yAw==", + "version": "1.54.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.54.1.tgz", + "integrity": "sha512-Nbjs2zjj0htNhzgiy5wu+3w09YetDx5pkrpI/kZotDlDUaYk0HVA5xrBVPdow4SAUIlhgKcJeJg4GRKW6xHusA==", "dev": true, "license": "Apache-2.0", "bin": { @@ -5670,15 +5709,15 @@ } }, "node_modules/prettier-plugin-organize-imports": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.1.0.tgz", - "integrity": "sha512-5aWRdCgv645xaa58X8lOxzZoiHAldAPChljr/MT0crXVOWTZ+Svl4hIWlz+niYSlO6ikE5UXkN1JrRvIP2ut0A==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.2.0.tgz", + "integrity": "sha512-Zdy27UhlmyvATZi67BTnLcKTo8fm6Oik59Sz6H64PgZJVs6NJpPD1mT240mmJn62c98/QaL+r3kx9Q3gRpDajg==", "dev": true, "license": "MIT", "peerDependencies": { "prettier": ">=2.0", "typescript": ">=2.9", - "vue-tsc": "^2.1.0" + "vue-tsc": "^2.1.0 || 3" }, "peerDependenciesMeta": { "vue-tsc": { @@ -5993,9 +6032,9 @@ "license": "ISC" }, "node_modules/sharp": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.2.tgz", - "integrity": "sha512-lszvBmB9QURERtyKT2bNmsgxXK0ShJrL/fvqlonCo7e6xBF8nT8xU6pW+PMIbLsz0RxQk3rgH9kd8UmvOzlMJg==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.3.tgz", + "integrity": "sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", @@ -6011,27 +6050,28 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.2", - "@img/sharp-darwin-x64": "0.34.2", - "@img/sharp-libvips-darwin-arm64": "1.1.0", - "@img/sharp-libvips-darwin-x64": "1.1.0", - "@img/sharp-libvips-linux-arm": "1.1.0", - "@img/sharp-libvips-linux-arm64": "1.1.0", - "@img/sharp-libvips-linux-ppc64": "1.1.0", - "@img/sharp-libvips-linux-s390x": "1.1.0", - "@img/sharp-libvips-linux-x64": "1.1.0", - "@img/sharp-libvips-linuxmusl-arm64": "1.1.0", - "@img/sharp-libvips-linuxmusl-x64": "1.1.0", - "@img/sharp-linux-arm": "0.34.2", - "@img/sharp-linux-arm64": "0.34.2", - "@img/sharp-linux-s390x": "0.34.2", - "@img/sharp-linux-x64": "0.34.2", - "@img/sharp-linuxmusl-arm64": "0.34.2", - "@img/sharp-linuxmusl-x64": "0.34.2", - "@img/sharp-wasm32": "0.34.2", - "@img/sharp-win32-arm64": "0.34.2", - "@img/sharp-win32-ia32": "0.34.2", - "@img/sharp-win32-x64": "0.34.2" + "@img/sharp-darwin-arm64": "0.34.3", + "@img/sharp-darwin-x64": "0.34.3", + "@img/sharp-libvips-darwin-arm64": "1.2.0", + "@img/sharp-libvips-darwin-x64": "1.2.0", + "@img/sharp-libvips-linux-arm": "1.2.0", + "@img/sharp-libvips-linux-arm64": "1.2.0", + "@img/sharp-libvips-linux-ppc64": "1.2.0", + "@img/sharp-libvips-linux-s390x": "1.2.0", + "@img/sharp-libvips-linux-x64": "1.2.0", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.0", + "@img/sharp-libvips-linuxmusl-x64": "1.2.0", + "@img/sharp-linux-arm": "0.34.3", + "@img/sharp-linux-arm64": "0.34.3", + "@img/sharp-linux-ppc64": "0.34.3", + "@img/sharp-linux-s390x": "0.34.3", + "@img/sharp-linux-x64": "0.34.3", + "@img/sharp-linuxmusl-arm64": "0.34.3", + "@img/sharp-linuxmusl-x64": "0.34.3", + "@img/sharp-wasm32": "0.34.3", + "@img/sharp-win32-arm64": "0.34.3", + "@img/sharp-win32-ia32": "0.34.3", + "@img/sharp-win32-x64": "0.34.3" } }, "node_modules/shebang-command": { @@ -6430,35 +6470,35 @@ } }, "node_modules/superagent": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.2.2.tgz", - "integrity": "sha512-vWMq11OwWCC84pQaFPzF/VO3BrjkCeewuvJgt1jfV0499Z1QSAWN4EqfMM5WlFDDX9/oP8JjlDKpblrmEoyu4Q==", + "version": "10.2.3", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.2.3.tgz", + "integrity": "sha512-y/hkYGeXAj7wUMjxRbB21g/l6aAEituGXM9Rwl4o20+SX3e8YOSV6BxFXl+dL3Uk0mjSL3kCbNkwURm8/gEDig==", "dev": true, "license": "MIT", "dependencies": { - "component-emitter": "^1.3.0", + "component-emitter": "^1.3.1", "cookiejar": "^2.1.4", - "debug": "^4.3.4", + "debug": "^4.3.7", "fast-safe-stringify": "^2.1.1", - "form-data": "^4.0.0", + "form-data": "^4.0.4", "formidable": "^3.5.4", "methods": "^1.1.2", "mime": "2.6.0", - "qs": "^6.11.0" + "qs": "^6.11.2" }, "engines": { "node": ">=14.18.0" } }, "node_modules/supertest": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.1.3.tgz", - "integrity": "sha512-ORY0gPa6ojmg/C74P/bDoS21WL6FMXq5I8mawkEz30/zkwdu0gOeqstFy316vHG6OKxqQ+IbGneRemHI8WraEw==", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.1.4.tgz", + "integrity": "sha512-tjLPs7dVyqgItVFirHYqe2T+MfWc2VOBQ8QFKKbWTA3PU7liZR8zoSpAi/C1k1ilm9RsXIKYf197oap9wXGVYg==", "dev": true, "license": "MIT", "dependencies": { "methods": "^1.1.2", - "superagent": "^10.2.2" + "superagent": "^10.2.3" }, "engines": { "node": ">=14.18.0" @@ -6778,15 +6818,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.36.0.tgz", - "integrity": "sha512-fTCqxthY+h9QbEgSIBfL9iV6CvKDFuoxg6bHPNpJ9HIUzS+jy2lCEyCmGyZRWEBSaykqcDPf1SJ+BfCI8DRopA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.38.0.tgz", + "integrity": "sha512-FsZlrYK6bPDGoLeZRuvx2v6qrM03I0U0SnfCLPs/XCCPCFD80xU9Pg09H/K+XFa68uJuZo7l/Xhs+eDRg2l3hg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.36.0", - "@typescript-eslint/parser": "8.36.0", - "@typescript-eslint/utils": "8.36.0" + "@typescript-eslint/eslint-plugin": "8.38.0", + "@typescript-eslint/parser": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" diff --git a/e2e/package.json b/e2e/package.json index 24b97bb4b7..8b8540e9ba 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -1,6 +1,6 @@ { "name": "immich-e2e", - "version": "1.135.3", + "version": "1.137.3", "description": "", "main": "index.js", "type": "module", @@ -26,14 +26,14 @@ "@playwright/test": "^1.44.1", "@socket.io/component-emitter": "^3.1.2", "@types/luxon": "^3.4.2", - "@types/node": "^22.15.33", + "@types/node": "^22.16.5", "@types/oidc-provider": "^9.0.0", "@types/pg": "^8.15.1", "@types/pngjs": "^6.0.4", "@types/supertest": "^6.0.2", "@vitest/coverage-v8": "^3.0.0", "eslint": "^9.14.0", - "eslint-config-prettier": "^10.0.0", + "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-unicorn": "^59.0.0", "exiftool-vendored": "^28.3.1", @@ -54,6 +54,6 @@ "vitest": "^3.0.0" }, "volta": { - "node": "22.17.0" + "node": "22.17.1" } } diff --git a/e2e/src/api/specs/album.e2e-spec.ts b/e2e/src/api/specs/album.e2e-spec.ts index eedf70dc58..af9b17cc13 100644 --- a/e2e/src/api/specs/album.e2e-spec.ts +++ b/e2e/src/api/specs/album.e2e-spec.ts @@ -470,7 +470,7 @@ describe('/albums', () => { .send({ ids: [asset.id] }); expect(status).toBe(400); - expect(body).toEqual(errorDto.badRequest('Not found or no album.addAsset access')); + expect(body).toEqual(errorDto.badRequest('Not found or no albumAsset.create access')); }); it('should add duplicate assets only once', async () => { @@ -599,7 +599,7 @@ describe('/albums', () => { .send({ ids: [user1Asset1.id] }); expect(status).toBe(400); - expect(body).toEqual(errorDto.badRequest('Not found or no album.removeAsset access')); + expect(body).toEqual(errorDto.badRequest('Not found or no albumAsset.delete access')); }); it('should remove duplicate assets only once', async () => { diff --git a/e2e/src/utils.ts b/e2e/src/utils.ts index 3fcc4ab552..0f8ceeabfc 100644 --- a/e2e/src/utils.ts +++ b/e2e/src/utils.ts @@ -186,18 +186,6 @@ export const utils = { } }, - resetFilesystem: async () => { - const mediaInternal = '/usr/src/app/upload'; - const dirs = [ - `"${mediaInternal}/thumbs"`, - `"${mediaInternal}/upload"`, - `"${mediaInternal}/library"`, - `"${mediaInternal}/encoded-video"`, - ].join(' '); - - await execPromise(`docker exec -i "immich-e2e-server" /bin/bash -c "rm -rf ${dirs} && mkdir ${dirs}"`); - }, - unzip: async (input: string, output: string) => { await execPromise(`unzip -o -d "${output}" "${input}"`); }, diff --git a/e2e/src/web/specs/auth.e2e-spec.ts b/e2e/src/web/specs/auth.e2e-spec.ts index 0fde9a6ec6..173131ec5e 100644 --- a/e2e/src/web/specs/auth.e2e-spec.ts +++ b/e2e/src/web/specs/auth.e2e-spec.ts @@ -37,6 +37,7 @@ test.describe('Registration', () => { await page.getByRole('button', { name: 'Server Privacy' }).click(); await page.getByRole('button', { name: 'User Privacy' }).click(); await page.getByRole('button', { name: 'Storage Template' }).click(); + await page.getByRole('button', { name: 'Backups' }).click(); await page.getByRole('button', { name: 'Done' }).click(); // success diff --git a/e2e/test-assets b/e2e/test-assets index 18736fc27a..37f60ea537 160000 --- a/e2e/test-assets +++ b/e2e/test-assets @@ -1 +1 @@ -Subproject commit 18736fc27a80c99c68e856cdb4f842bc81ed3445 +Subproject commit 37f60ea537c0228f5f92e4f42dc42f0bb39a6d7f diff --git a/i18n/af.json b/i18n/af.json index 55555c8398..b3729903ec 100644 --- a/i18n/af.json +++ b/i18n/af.json @@ -4,6 +4,7 @@ "account_settings": "Rekeninginstellings", "acknowledge": "Erken", "action": "Aksie", + "action_common_update": "Opdateur", "actions": "Aksies", "active": "Aktief", "activity": "Aktiwiteite", @@ -13,6 +14,7 @@ "add_a_location": "Voeg 'n ligging by", "add_a_name": "Voeg 'n naam by", "add_a_title": "Voeg 'n titel by", + "add_endpoint": "Voeg Koppelvlakpunt by", "add_exclusion_pattern": "Voeg uitsgluitingspatrone by", "add_import_path": "Voeg invoerpad by", "add_location": "Voeg ligging by", @@ -20,26 +22,30 @@ "add_partner": "Voeg vennoot by", "add_path": "Voeg pad by", "add_photos": "Voeg foto's by", + "add_tag": "Voeg tag by", "add_to": "Voeg by…", "add_to_album": "Voeg na album", - "add_to_shared_album": "Voeg na gedeelde album", + "add_to_album_bottom_sheet_added": "By {album} bygevoeg", + "add_to_album_bottom_sheet_already_exists": "Reeds in {album}", + "add_to_shared_album": "Voeg toe aan gedeelde album", "add_url": "Voeg URL by", - "added_to_archive": "By argief gevoeg", - "added_to_favorites": "By gunstelinge gevoeg", - "added_to_favorites_count": "Het {count, number} by gunstelinge gevoeg", + "added_to_archive": "By argief toegevoegd", + "added_to_favorites": "By gunstelinge toegevoegd", + "added_to_favorites_count": "Het {count, number} by gunstelinge toegevoegd", "admin": { "add_exclusion_pattern_description": "Voeg uitsluitingspatrone by. Globbing met *, ** en ? word ondersteun. Om alle lêers in enige lêergids genaamd \"Raw\" te ignoreer, gebruik \"**/Raw/**\". Om alle lêers wat op \".tif\" eindig, te ignoreer, gebruik \"**/*.tif\". Om 'n absolute pad te ignoreer, gebruik \"/path/to/ignore/**\".", + "admin_user": "Admin gebruiker", "asset_offline_description": "Hierdie eksterne biblioteekbate word nie meer op skyf gevind nie en is na die asblik geskuif. As die lêer binne die biblioteek geskuif is, gaan jou tydlyn na vir die nuwe ooreenstemmende bate. Om hierdie bate te herstel, maak asseblief seker dat die lêerpad hieronder deur Immich verkry kan word en skandeer die biblioteek.", "authentication_settings": "Verifikasie instellings", "authentication_settings_description": "Bestuur wagwoord, OAuth en ander verifikasie instellings", "authentication_settings_disable_all": "Is jy seker jy wil alle aanmeldmetodes deaktiveer? Aanmelding sal heeltemal gedeaktiveer word.", "authentication_settings_reenable": "Om te heraktiveer, gebruik 'n Server Command.", "background_task_job": "Agtergrondtake", - "backup_database": "Rugsteun databasis", + "backup_database": "Skep Datastortlêer", "backup_database_enable_description": "Aktiveer databasisrugsteun", "backup_keep_last_amount": "Aantal vorige rugsteune om te hou", "backup_settings": "Rugsteun instellings", - "backup_settings_description": "Bestuur databasis rugsteun instellings", + "backup_settings_description": "Bestuur databasis rugsteun instellings.", "cleared_jobs": "Poste gevee vir: {job}", "config_set_by_file": "Config word tans deur 'n konfigurasielêer gestel", "confirm_delete_library": "Is jy seker jy wil {library}-biblioteek uitvee?", @@ -47,6 +53,7 @@ "confirm_email_below": "Om te bevestig, tik \"{email}\" hieronder", "confirm_reprocess_all_faces": "Is jy seker jy wil alle gesigte herverwerk? Dit sal ook genoemde mense skoonmaak.", "confirm_user_password_reset": "Is jy seker jy wil {user} se wagwoord terugstel?", + "confirm_user_pin_code_reset": "Is jy seker jy wil {user} se PIN kode herstel?", "create_job": "Skep werk", "cron_expression": "Cron uitdrukking", "cron_expression_description": "Stel die skanderingsinterval in met die cron-formaat. Vir meer inligting verwys asseblief na bv. Crontab Guru", @@ -56,10 +63,14 @@ "exclusion_pattern_description": "Met uitsluitingspatrone kan jy lêers en vouers ignoreer wanneer jy jou biblioteek skandeer. Dit is nuttig as jy vouers het wat lêers bevat wat jy nie wil invoer nie, soos RAW-lêers.", "external_library_management": "Eksterne Biblioteekbestuur", "face_detection": "Gesig deteksie", + "face_detection_description": "Detecteer die gesigte in media deur middel van masjienleer. Vir videos word slegs die duimnaelskets oorweeg. “Herlaai” (ver)werk al die media weer. “Stel terug” verwyder boonop alle huidige gesigdata. “Onverwerk” plaas bates in die tou wat nog nie verwerk is nie. Gedekte gesigte sal ná voltooiing van Gesigdetectie vir Gesigherkenning in die tou geplaas word, om hulle in bestaande of nuwe persone te groepeer.", + "facial_recognition_job_description": "Groepeer gesigte in mense in. Die stap is vinniger nadat Gesig Deteksie klaar is. \"Herstel\" (her-)groepeer alle gesigte. \"Vermiste\" plaas gesigte in ry wat nie 'n persoon gekoppel het nie.", "failed_job_command": "Opdrag {command} het misluk vir werk: {job}", "force_delete_user_warning": "WAARSKUWING: Dit sal onmiddellik die gebruiker en alle bates verwyder. Dit kan nie ontdoen word nie en die lêers kan nie herstel word nie.", "image_format": "Formaat", "image_format_description": "WebP produseer kleiner lêers as JPEG, maar is stadiger om te enkodeer.", + "image_fullsize_description": "Vol grote prent met geen metadata, gebruik wanner ingezoem", + "image_fullsize_enabled": "Skakel aan vol grote prent generasie", "image_prefer_embedded_preview": "Verkies ingebedde voorskou", "image_prefer_wide_gamut": "Verkies wide gamut", "image_prefer_wide_gamut_setting_description": "Gebruik Display P3 vir kleinkiekies. Dit behou die lewendheid van beelde met wye kleurruimtes beter, maar beelde kan anders verskyn op ou apparate met 'n ou blaaierweergawe. sRGB-beelde gebruik steeds sRGB om kleurverskuiwings te voorkom.", @@ -77,8 +88,99 @@ "job_concurrency": "{job} gelyktydigheid", "job_created": "Taak gemaak", "job_not_concurrency_safe": "Hierdie taak kan nie gelyktydig uitgevoer word nie.", - "job_settings": "Agtergrondtaakinstellings" + "job_settings": "Agtergrondtaakinstellings", + "job_settings_description": "Bestuur werkgelyktydigheid", + "job_status": "Werkstatus", + "library_created": "Biblioteek geskep: {library}", + "library_deleted": "Biblioteek verwyder", + "library_import_path_description": "Spesifiseer 'n leer om in te neem. Hierdie leer, en al die sub leers, gaan geskandeer for vir prente en videos.", + "library_scanning": "Periodieke Skandering", + "library_scanning_description": "Stel periodieke skandering van biblioteek in", + "library_scanning_enable_description": "Aktiveer periodieke biblioteekskandering", + "library_settings": "Eksterne Biblioteek", + "map_settings": "Kaart", + "migration_job": "Migrasie", + "oauth_settings": "OAuth", + "transcoding_acceleration_vaapi": "VAAPI" }, + "administration": "Administrasie", + "advanced": "Gevorderde", + "albums": "Albums", + "all": "Alle", + "anti_clockwise": "Anti-kloksgewys", + "archive": "Argief", + "asset_skipped": "Oorgeslaan", + "asset_uploaded": "Opgelaai", + "asset_uploading": "Oplaai…", + "assets": "Bates", + "back": "Terug", + "backward": "Agteruit", + "build": "Bou", + "camera": "Kamera", + "cancel": "Kanselleer", + "city": "Stad", + "clockwise": "Kloksgewys", + "close": "Maak toe", + "color": "Kleur", + "confirm": "Bevestig", + "contain": "Bevat", + "context": "Konteks", + "continue": "Gaan voort", + "country": "Land", + "cover": "Bedek", + "create": "Skep", + "created": "Geskep", + "dark": "Donker", + "day": "Dag", + "delete": "Verwyder", + "description": "Beskrywing", + "details": "Besonderhede", + "direction": "Rigting", + "discover": "Ontdek", + "documentation": "Dokumentasie", + "done": "Klaar", + "download": "Aflaai", + "download_settings": "Aflaai", + "duplicates": "Duplikate", + "duration": "Duur", + "edit": "Wysig", + "edited": "Gewysigd", "search_by_description": "Soek by beskrywing", - "search_by_description_example": "Stapdag in Sapa" + "search_by_description_example": "Stapdag in Sapa", + "version": "Weergawe", + "version_announcement_closing": "Jou friend, Alex", + "version_history": "Weergawegeskiedenis", + "version_history_item": "{version} geinstaleerd op {date}", + "video": "Video", + "videos": "Video's", + "view": "Bekyk", + "view_album": "Bekyk Album", + "view_all": "Bekyk alle", + "view_all_users": "Bekyk alle gebruikers", + "view_in_timeline": "Bekyk in tydlyn", + "view_link": "Bekyk skakel", + "view_links": "Bekyk skakels", + "view_name": "Bekyk", + "view_next_asset": "Bekyk volgende bate", + "view_previous_asset": "Bekyk vorige bate", + "view_qr_code": "Bekyk QR-kode", + "view_stack": "Bekyk stapel", + "view_user": "Bekyk gebruiker", + "viewer_remove_from_stack": "Verwyder van stapel", + "viewer_stack_use_as_main_asset": "Gebruik as hoofbate", + "viewer_unstack": "Ontstapel", + "visibility_changed": "Sigbaarheid verander voor {count, plural, one {# person} other {# people}}", + "waiting": "Wag", + "warning": "Waaskuwing", + "week": "Week", + "welcome": "Welkom", + "welcome_to_immich": "Welkom by Immich", + "wifi_name": "Wi-Fi Naam", + "wrong_pin_code": "Verkeerde PIN-kode", + "year": "Jaar", + "years_ago": "{years, plural, one {# year} other {# years}} gelede", + "yes": "Ja", + "you_dont_have_any_shared_links": "Jy het geen gedeelde skakels", + "your_wifi_name": "Jou Wi-Fi naam", + "zoom_image": "Vergroot Prent" } diff --git a/i18n/ar.json b/i18n/ar.json index 67f7752ae1..13e785668d 100644 --- a/i18n/ar.json +++ b/i18n/ar.json @@ -1,19 +1,20 @@ { - "about": "من نحن", - "account": "الحساب", + "about": "عن", + "account": "حساب", "account_settings": "إعدادات الحساب", "acknowledge": "أُدرك ذلك", - "action": "التحكم", + "action": "عملية", "action_common_update": "تحديث", - "actions": "العمليات", + "actions": "عمليات", "active": "نشط", - "activity": "النشاط", + "activity": "نشاط", "activity_changed": "النشاط {enabled, select, true {مُفْعل} other {معطّل}}", "add": "إضافة", "add_a_description": "إضافة وصف", "add_a_location": "إضافة موقع", "add_a_name": "إضافة إسم", "add_a_title": "إضافة عنوان", + "add_endpoint": "اضف نقطة نهاية", "add_exclusion_pattern": "إضافة نمط إستثناء", "add_import_path": "إضافة مسار الإستيراد", "add_location": "إضافة موقع", @@ -21,28 +22,30 @@ "add_partner": "أضف شريكًا", "add_path": "إضافة مسار", "add_photos": "إضافة صور", + "add_tag": "اضف علامة", "add_to": "إضافة إلى…", "add_to_album": "إضافة إلى ألبوم", "add_to_album_bottom_sheet_added": "تمت الاضافة{album}", "add_to_album_bottom_sheet_already_exists": "موجودة مسبقا {album}", - "add_to_shared_album": "إضافة إلى ألبوم مشترك", + "add_to_shared_album": "إضافة إلى ألبوم مشارك", "add_url": "إضافة رابط", "added_to_archive": "أُضيفت للأرشيف", "added_to_favorites": "أُضيفت للمفضلات", "added_to_favorites_count": "تم إضافة {count, number} إلى المفضلات", "admin": { "add_exclusion_pattern_description": "إضافة أنماط الاستبعاد. يدعم التمويه باستخدام *، **، و؟. لتجاهل جميع الملفات في أي دليل يسمى \"Raw\"، استخدم \"**/Raw/**\". لتجاهل جميع الملفات التي تنتهي بـ \".tif\"، استخدم \"**/*.tif\". لتجاهل مسار مطلق، استخدم \"/path/to/ignore/**\".", + "admin_user": "مستخدم مدير", "asset_offline_description": "لم يعد هذا الأصل الخاص بالمكتبة الخارجية موجودًا على القرص وتم نقله إلى سلة المهملات. إذا تم نقل الملف داخل المكتبة، فتحقق من الجدول الزمني الخاص بك لمعرفة الأصل الجديد المقابل. لاستعادة هذا الأصل، يرجى التأكد من إمكانية الوصول إلى مسار الملف أدناه بواسطة Immich ومن ثم قم بمسح المكتبة.", "authentication_settings": "إعدادات المصادقة", "authentication_settings_description": "إدارة كلمة المرور وOAuth وإعدادات المصادقة الأُخرى", "authentication_settings_disable_all": "هل أنت متأكد أنك تريد تعطيل جميع وسائل تسجيل الدخول؟ سيتم تعطيل تسجيل الدخول بالكامل.", "authentication_settings_reenable": "لإعادة التفعيل، استخدم أمر الخادم.", "background_task_job": "المهام الخلفية", - "backup_database": "قاعدة البيانات الاحتياطية", - "backup_database_enable_description": "تمكين النسخ الاحتياطي لقاعدة البيانات", - "backup_keep_last_amount": "مقدار النسخ الاحتياطية السابقة للاحتفاظ بها", - "backup_settings": "إعدادات النسخ الاحتياطي", - "backup_settings_description": "إدارة إعدادات النسخ الاحتياطي لقاعدة البيانات", + "backup_database": "انشاء تفريغ قاعدة البيانات", + "backup_database_enable_description": "تمكين تفريغ قاعدة البيانات", + "backup_keep_last_amount": "مقدار التفريغات السابقة للاحتفاظ بها", + "backup_settings": "إعدادات تفريغ قاعدة البيانات", + "backup_settings_description": "إدارة إعدادات تفريغ قاعدة البيانات.", "cleared_jobs": "تم إخلاء مهام: {job}", "config_set_by_file": "الإعدادات حاليًا معينة عن طريق ملف الاعدادات", "confirm_delete_library": "هل أنت متأكد أنك تريد حذف مكتبة {library}؟", @@ -50,6 +53,7 @@ "confirm_email_below": "للتأكيد، اكتب \"{email}\" بالأسفل", "confirm_reprocess_all_faces": "هل أنت متأكد أنك تريد إعادة معالجة جميع الوجوه؟ سيخلي هذا كل الأشخاص الذين سَميتَهم.", "confirm_user_password_reset": "هل أنت متأكد أنك تريد إعادة تعيين كلمة مرور {user}؟", + "confirm_user_pin_code_reset": "هل انت متاكد من اعادة ضبط رمز PIN الخاص ب {user}؟", "create_job": "إنشاء وظيفة", "cron_expression": "تعبير Cron", "cron_expression_description": "اضبط الفاصل الزمني للفحص باستخدام تنسيق cron. لمزيد من المعلومات يُرجى الرجوع إلى Crontab Guru على سبيل المثال", @@ -65,10 +69,15 @@ "force_delete_user_warning": "تحذير: سيؤدي ذلك إلى إزالة المستخدم وجميع محتوياته على الفور. لا يمكن التراجع عن هذا الإجراء ولا يمكن استرداد الملفات.", "image_format": "التنسيق", "image_format_description": "يُنتج WebP ملفات أصغر حجمًا من ملفات JPEG، ولكنه أبطأ في عملية الترميز.", + "image_fullsize_description": "صورة بحجم كامل مع ازالة البيانات الوصفية، تستخدم عند التكبير", + "image_fullsize_enabled": "تمكين توليد الصور بحجم كامل", + "image_fullsize_enabled_description": "توليد صور بحجم كامل للصيغ الغير صديقة للويب. عند تفعيل \"تفضيل العرض المدمج\" ، العروض المدمجه تستخدم بشكل مباشر بدون تحويل. لا يؤثر على الصيغ الصديقة للويب مثل JPEG.", + "image_fullsize_quality_description": "صور بدقة كاملة من ١-١٠٠. الاعلى افضل ولكن ينتج ملفات بحجم اكبر.", + "image_fullsize_title": "اعدادات الصور بحجم كامل", "image_prefer_embedded_preview": "تفضيل المعاينة المدمجة", - "image_prefer_embedded_preview_setting_description": "استخدم المعاينات المضمنة في صور RAW كمدخل لمعالجة الصور عندما تكون متاحة. يؤدي لإنتاج ألوان أكثر دقة لبعض الصور، لكن جودة المعاينة تعتمد على الكاميرا وقد تحتوي الصورة على شوائب ضغطٍ أكثر.", - "image_prefer_wide_gamut": "تفضيل نطاق الألوان الواسع", - "image_prefer_wide_gamut_setting_description": "استخدم Display P3 للصور المصغرة. يحافظ هذا على حيوية الصور ذات مساحات الألوان الواسعة بشكل أفضل، ولكن قد تظهر الصور بشكل مختلف على الأجهزة القديمة ذات إصدار متصفح قديم. يتم الاحتفاظ بصور sRGB بتنسيق sRGB لتجنب تغيرات اللون.", + "image_prefer_embedded_preview_setting_description": "استخدم المعاينات المضمنة في صور RAW كمدخل لمعالجة الصور عندما تكون متاحة. ينتج عنه انتاج ألوان أكثر دقة لبعض الصور، لكن جودة المعاينة تعتمد على الكاميرا وقد تحتوي الصورة على شوائب ضغطٍ أكثر.", + "image_prefer_wide_gamut": "تفضيل تدرج الألوان الواسع", + "image_prefer_wide_gamut_setting_description": "استخدم عرض P3 للصور المصغرة. يحافظ هذا على حيوية الصور ذات مساحات الألوان الواسعة بشكل أفضل، ولكن قد تظهر الصور بشكل مختلف على الأجهزة القديمة ذات إصدار متصفح قديم. يتم الاحتفاظ بصور sRGB بتنسيق sRGB لتجنب تغيرات اللون.", "image_preview_description": "صورة متوسطة الحجم مع بيانات وصفية مجردة، تُستخدم عند عرض أصل واحد وللتعلم الآلي", "image_preview_quality_description": "جودة المعاينة من 1 إلى 100. كلما كانت القيمة أعلى كان ذلك أفضل، ولكنها تنتج ملفات أكبر وقد تقلل من استجابة التطبيق. قد يؤثر ضبط قيمة منخفضة على جودة التعلم الآلي.", "image_preview_title": "إعدادات المعاينة", @@ -91,18 +100,18 @@ "library_created": "تم إنشاء المكتبة: {library}", "library_deleted": "تم حذف المكتبة", "library_import_path_description": "حدد مجلدًا للاستيراد. سيتم فحص هذا المجلد، بما في ذلك المجلدات الفرعية، بحثًا عن الصور ومقاطع الفيديو.", - "library_scanning": "الفحص الدوري", - "library_scanning_description": "إعداد فحص المكتبة الدوري", - "library_scanning_enable_description": "تفعيل فحص المكتبة الدوري", + "library_scanning": "المسح الدوري", + "library_scanning_description": "إعداد مسح المكتبة الدوري", + "library_scanning_enable_description": "تفعيل مسح المكتبة الدوري", "library_settings": "المكتبة الخارجية", "library_settings_description": "إدارة إعدادات المكتبة الخارجية", "library_tasks_description": "مسح المكتبات الخارجية للعثور على الأصول الجديدة و/أو المتغيرة", - "library_watching_enable_description": "راقب المكتبات الخارجية لتتبع تغييرات الملفات", + "library_watching_enable_description": "راقب المكتبات الخارجية لتغييرات الملفات", "library_watching_settings": "مراقبة المكتبات (تجريبي)", "library_watching_settings_description": "راقب تلقائيًا التغييرات في الملفات", "logging_enable_description": "تفعيل تسجيل الأحداث", "logging_level_description": "عند التفعيل، أي مستوى تسجيل سيستخدم.", - "logging_settings": "تسجيل الأحداث", + "logging_settings": "تسجيل الاحداث", "machine_learning_clip_model": "نموذج CLIP", "machine_learning_clip_model_description": "اسم نموذج CLIP مدرجٌ هنا. يرجى ملاحظة أنه يجب إعادة تشغيل وظيفة \"البحث الذكي\" لجميع الصور بعد تغيير النموذج.", "machine_learning_duplicate_detection": "كشف التكرار", @@ -142,10 +151,10 @@ "map_light_style": "النمط الفاتح", "map_manage_reverse_geocoding_settings": "إدارة إعدادات التكوين الجغرافي المعكوس", "map_reverse_geocoding": "عكس الترميز الجغرافي", - "map_reverse_geocoding_enable_description": "تفعيل عكس الترميز الجغرافي", - "map_reverse_geocoding_settings": "إعدادات عكس الترميز الجغرافي", - "map_settings": "الخريطة", - "map_settings_description": "إدارة إعدادات الخريطة", + "map_reverse_geocoding_enable_description": "تفعيل الترميز الجغرافي العكسي", + "map_reverse_geocoding_settings": "إعدادات الترميز الجغرافي العكسي", + "map_settings": "الخارطة", + "map_settings_description": "إدارة إعدادات الخارطة", "map_style_description": "عنوان URL لسمة الخريطة style.json", "memory_cleanup_job": "تنظيف الذاكرة", "memory_generate_job": "توليد الذاكرة", @@ -157,12 +166,26 @@ "metadata_settings_description": "إدارة إعدادات البيانات الوصفية", "migration_job": "ترحيل", "migration_job_description": "ترحيل الصور المصغرة للمحتويات والوجوه إلى أحدث هيكل مجلدات", + "nightly_tasks_cluster_faces_setting_description": "قم بتشغيل التعرف على الوجه على الوجوه المكتشفة حديثا", + "nightly_tasks_cluster_new_faces_setting": "مجموعة الوجوه الجديدة", + "nightly_tasks_database_cleanup_setting": "مهام تنظيف قاعدة البيانات", + "nightly_tasks_database_cleanup_setting_description": "قم بتنظيف البيانات القديمة منتهية الصلاحية من قاعدة البيانات", + "nightly_tasks_generate_memories_setting": "إنشاء الذكريات", + "nightly_tasks_generate_memories_setting_description": "إنشاء ذكريات جديدة من الأصول", + "nightly_tasks_missing_thumbnails_setting": "إنشاء صور مصغرة مفقودة", + "nightly_tasks_missing_thumbnails_setting_description": "أصول قائمة الانتظار بدون صور مصغرة لإنشاء الصور المصغرة", + "nightly_tasks_settings": "إعدادات المهام الليلية", + "nightly_tasks_settings_description": "إدارة المهام الليلية", + "nightly_tasks_start_time_setting": "وقت البدء", + "nightly_tasks_start_time_setting_description": "الوقت الذي يبدأ فيه الخادم في تشغيل المهام الليلية", + "nightly_tasks_sync_quota_usage_setting": "مزامنة حصة الاستخدام", + "nightly_tasks_sync_quota_usage_setting_description": "تحديث حصة تخزين المستخدم، بناء على الاستخدام الحالي", "no_paths_added": "لم يتم إضافة أي مسارات", "no_pattern_added": "لم يتم إضافة أي أنماط", - "note_apply_storage_label_previous_assets": "ملاحظة: لتطبيق تسمية التخزين على المحتويات التي تم رفعها سابقًا، قم بتشغيل", + "note_apply_storage_label_previous_assets": "ملاحظة: لتطبيق سمية التخزين على المحتويات التي تم رفعها سابقًا، قم بتشغيل", "note_cannot_be_changed_later": "ملاحظة: لا يمكن تغيير هذا لاحقًا!", "notification_email_from_address": "عنوان المرسل", - "notification_email_from_address_description": "عنوان البريد الإلكتروني للمرسل، على سبيل المثال: \"Immich Photo Server noreply@example.com\"", + "notification_email_from_address_description": "عنوان البريد الإلكتروني للمرسل، على سبيل المثال: \"Immich Photo Server noreply@example.com\". تاكد من استخدام عنوان بريد الكتروني يسمح لك بارسال البريد الالكتروني منه.", "notification_email_host_description": "مضيف خادم البريد الإلكتروني (مثلًا: smtp.immich.app)", "notification_email_ignore_certificate_errors": "تجاهل أخطاء الشهادة", "notification_email_ignore_certificate_errors_description": "تجاهل أخطاء التحقق من صحة شهادة TLS (غير مستحسن)", @@ -182,6 +205,7 @@ "oauth_auto_register": "التسجيل التلقائي", "oauth_auto_register_description": "التسجيل التلقائي للمستخدمين الجدد بعد تسجيل الدخول باستخدام OAuth", "oauth_button_text": "نص الزر", + "oauth_client_secret_description": "مطلوب اذاPKCE(مفتاح الاثبات لتبادل الكود) لم يتم توفيره من مزود OAuth", "oauth_enable_description": "تسجيل الدخول باستخدام OAuth", "oauth_mobile_redirect_uri": "عنوان URI لإعادة التوجيه على الهاتف", "oauth_mobile_redirect_uri_override": "تجاوز عنوان URI لإعادة التوجيه على الهاتف", @@ -189,12 +213,14 @@ "oauth_settings": "OAuth", "oauth_settings_description": "إدارة إعدادات تسجيل الدخول OAuth", "oauth_settings_more_details": "لمزيد من التفاصيل حول هذه الميزة، يرجى الرجوع إلى الوثائق.", - "oauth_storage_label_claim": "المطالبة بتصنيف التخزين", - "oauth_storage_label_claim_description": "قم تلقائيًا بتعيين تصنيف التخزين الخاص بالمستخدم على قيمة هذه المطالبة.", + "oauth_storage_label_claim": "المطالبة بسمة التخزين", + "oauth_storage_label_claim_description": "قم تلقائيًا بتعيين سمة التخزين الخاص بالمستخدم على قيمة هذه المطالبة.", "oauth_storage_quota_claim": "المطالبة بحصة التخزين", "oauth_storage_quota_claim_description": "قم تلقائيًا بتعيين حصة التخزين للمستخدم على قيمة هذه المطالبة.", "oauth_storage_quota_default": "حصة التخزين الافتراضية (جيجابايت)", - "oauth_storage_quota_default_description": "الحصة بالجيجابايت التي سيتم استخدامها عندما لا يتم توفير مطالبة (أدخل 0 لحصة غير محدودة).", + "oauth_storage_quota_default_description": "الحصة بالجيجابايت التي سيتم استخدامها عندما لا يتم توفير مطالبة.", + "oauth_timeout": "نفاذ وقت الطلب", + "oauth_timeout_description": "نفاذ وقت الطلب بالميلي ثانية", "password_enable_description": "تسجيل الدخول باستخدام البريد الكتروني وكلمة المرور", "password_settings": "تسجيل الدخول بكلمة المرور", "password_settings_description": "إدارة تسجيل الدخول بكلمة المرور", @@ -229,13 +255,14 @@ "storage_template_hash_verification_enabled_description": "تفعيل التحقق من الهاش، لا تعطل هذا إلا إذا كنت متأكدًا من تأثيراته", "storage_template_migration": "تهجير قالب التخزين", "storage_template_migration_description": "قم بتطبيق القالب الحالي {template} على المحتويات التي تم رفعها سابقًا", - "storage_template_migration_info": "تغييرات القالب ستنطبق فقط على المحتويات الجديدة. لتطبيق القالب على المحتويات التي تم رفعها سابقًا، قم بتشغيل {job}.", + "storage_template_migration_info": "تغييرات النموذج الخزني ستغير جميع الصيغ الى احرف صغيرة. تغييرات النموذج ستنطبق فقط على المحتويات الجديدة. لتطبيق النموذج على المحتويات التي تم رفعها سابقًا، قم بتشغيل {job}.", "storage_template_migration_job": "وظيفة تهجير قالب التخزين", "storage_template_more_details": "لمزيد من التفاصيل حول هذه الميزة، يرجى الرجوع إلى Storage Template وimplications", + "storage_template_onboarding_description_v2": "عند التفعيل. هذه الخاصية ستقوم بالترتيب التلقائي للملفات بناء على نموذج معرف من قبل المستخدم. رجاء اطلع على التوثيق.", "storage_template_path_length": "الحد التقريبي لطول المسار: {length, number}/{limit, number}", "storage_template_settings": "قالب التخزين", "storage_template_settings_description": "إدارة هيكل المجلد واسم الملف للأصول المرفوعة", - "storage_template_user_label": "{label} هو تسمية التخزين الخاصة بالمستخدم", + "storage_template_user_label": "{label} هو سمة التخزين الخاصة بالمستخدم", "system_settings": "إعدادات النظام", "tag_cleanup_job": "تنظيف العلامة", "template_email_available_tags": "يمكنك استخدام المتغيرات التالية في القالب الخاص بك: {tags}", @@ -246,15 +273,15 @@ "template_email_update_album": "تحديث قالب الألبوم", "template_email_welcome": "قالب البريد الإلكتروني الترحيبي", "template_settings": "قوالب الإشعارات", - "template_settings_description": "إدارة القوالب المخصصة للإشعارات.", + "template_settings_description": "إدارة القوالب المخصصة للإشعارات", "theme_custom_css_settings": "CSS مخصص", "theme_custom_css_settings_description": "أوراق الأنماط المتتالية تسمح بتخصيص تصميم Immich.", "theme_settings": "إعدادات السمة", "theme_settings_description": "إدارة تخصيص واجهة ويب Immich", "thumbnail_generation_job": "إنشاء الصور المصغرة", "thumbnail_generation_job_description": "إنشاء صور مصغرة كبيرة وصغيرة وغير واضحة لكل أصل، بالإضافة إلى صور مصغرة لكل شخص", - "transcoding_acceleration_api": "واجهة برمجة التطبيقات للتسريع", - "transcoding_acceleration_api_description": "الواجهة البرمجية التي ستتفاعل مع جهازك لتسريع التحويل. هذا الإعداد هو \"أفضل محاولة\": سيعود إلى التحويل البرمجي في حالة الفشل. قد لا يعمل VP9 اعتمادًا على عتادك.", + "transcoding_acceleration_api": "تسريع API", + "transcoding_acceleration_api_description": "API التي ستتفاعل مع جهازك لتسريع التحويل. هذا الإعداد هو \"أفضل محاولة\": سيعود إلى التحويل البرمجي في حالة الفشل. قد لا يعمل VP9 اعتمادًا على عتادك.", "transcoding_acceleration_nvenc": "NVENC (يتطلب GPU من NVIDIA)", "transcoding_acceleration_qsv": "Quick Sync (يتطلب معالج Intel من الجيل السابع أو أحدث)", "transcoding_acceleration_rkmpp": "RKMPP (فقط على شرائح Rockchip SOC)", @@ -278,13 +305,13 @@ "transcoding_encoding_options": "خيارات الترميز", "transcoding_encoding_options_description": "اضبط برامج الترميز والدقة والجودة والخيارات الأخرى لمقاطع الفيديو المشفرة", "transcoding_hardware_acceleration": "التسريع العتادي", - "transcoding_hardware_acceleration_description": "تجريبي؛ أسرع بكثير، ولكن ستكون جودتها أقل عند نفس معدل البت", + "transcoding_hardware_acceleration_description": "تجريبي: ترميز اسرع لكن قد يقلل من الجودة مع معدل بت اقل", "transcoding_hardware_decoding": "فك تشفير الأجهزة", "transcoding_hardware_decoding_setting_description": "ينطبق ذلك فقط على NVENC، QSV، و RKMPP. يمكن التسريع من طرف لطرف بدلاً من تسريع الترميز فقط. قد لا يعمل على جميع مقاطع الفيديو.", "transcoding_max_b_frames": "أقصى عدد من الإطارات B", "transcoding_max_b_frames_description": "القيم الأعلى تعزز كفاءة الضغط، ولكنها تبطئ عملية الترميز. قد لا تكون متوافقة مع التسريع العتادي على الأجهزة القديمة. قيمة 0 تعطل إطارات B، بينما تضبط القيمة -1 هذا القيمة تلقائيًا.", "transcoding_max_bitrate": "الحد الأقصى لمعدل البت", - "transcoding_max_bitrate_description": "يمكن أن يؤدي تعيين الحد الأقصى لمعدل البت إلى جعل أحجام الملفات أكثر قابلية للتنبؤ بها بتكلفة بسيطة بالنسبة للجودة. عند دقة 720 بكسل، تكون القيم النموذجية 2600 كيلو بايت لـ VP9 أو HEVC، أو 4500 كيلو بايت لـ H.264. معطل إذا تم ضبطه على 0.", + "transcoding_max_bitrate_description": "يمكن أن يؤدي تعيين الحد الأقصى لمعدل البت إلى جعل أحجام الملفات أكثر قابلية للتنبؤ بها بتكلفة بسيطة بالنسبة للجودة. عند دقة 720 بكسل، تكون القيم النموذجية 2600 كيلو بت لـ VP9 أو HEVC، أو 4500 كيلو بت لـ H.264. معطل إذا تم ضبطه على 0.", "transcoding_max_keyframe_interval": "الحد الأقصى للفاصل الزمني للإطار الرئيسي", "transcoding_max_keyframe_interval_description": "يضبط الحد الأقصى لمسافة الإطار بين الإطارات الرئيسية. تؤدي القيم المنخفضة إلى زيادة سوء كفاءة الضغط، ولكنها تعمل على تحسين أوقات البحث وقد تعمل على تحسين الجودة في المشاهد ذات الحركة السريعة. 0 يضبط هذه القيمة تلقائيًا.", "transcoding_optimal_description": "مقاطع الفيديو ذات الدقة الأعلى من الدقة المستهدفة أو بتنسيق غير مقبول", @@ -324,6 +351,7 @@ "user_delete_delay_settings_description": "عدد الأيام بعد الإزالة لحذف حساب المستخدم ومحتوياته بشكل دائم. تقوم وظيفة حذف المستخدم بالتشغيل في منتصف الليل للتحقق من المستخدمين الجاهزين للحذف. سيتم تقييم التغييرات على هذا الإعداد في التنفيذ القادم.", "user_delete_immediately": "سيتم وضع حساب {user} ومحتوياته في قائمة الانتظار للحذف الدائم على الفور.", "user_delete_immediately_checkbox": "قائمة انتظار المستخدم والمحتويات للحذف الفوري", + "user_details": "تفاصيل المستخدم", "user_management": "إدارة المستخدم", "user_password_has_been_reset": "تمت إعادة تعيين كلمة المرور الخاصة بالمستخدم:", "user_password_reset_description": "يرجى تزويد المستخدم بكلمة المرور المؤقتة وإبلاغه بأنه سيحتاج إلى تغيير كلمة المرور عند تسجيل الدخول التالي.", @@ -343,9 +371,17 @@ "admin_password": "كلمة سر المشرف", "administration": "الإدارة", "advanced": "متقدم", + "advanced_settings_enable_alternate_media_filter_subtitle": "استخدم هذا الخيار لتصفية الوسائط اثناء المزامنه بناء على معايير بديلة. جرب هذا الخيار فقط كان لديك مشاكل مع التطبيق بالكشف عن جميع الالبومات.", + "advanced_settings_enable_alternate_media_filter_title": "[تجريبي] استخدم جهاز تصفية مزامنه البومات بديل", + "advanced_settings_log_level_title": "مستوى السجل: {level}", "advanced_settings_prefer_remote_subtitle": "تكون بعض الأجهزة بطيئة للغاية في تحميل الصور المصغرة من الأصول الموجودة على الجهاز. قم بتنشيط هذا الإعداد لتحميل الصور البعيدة بدلاً من ذلك.", "advanced_settings_prefer_remote_title": "تفضل الصور البعيدة", + "advanced_settings_proxy_headers_subtitle": "عرف عناوين الوكيل التي يستخدمها Immich لارسال كل طلب شبكي", + "advanced_settings_proxy_headers_title": "عناوين الوكيل", + "advanced_settings_self_signed_ssl_subtitle": "تخطي التحقق من شهادة SSL لخادم النقطة النهائي. مكلوب للشهادات الموقعة ذاتيا.", "advanced_settings_self_signed_ssl_title": "السماح بشهادات SSL الموقعة ذاتيًا", + "advanced_settings_sync_remote_deletions_subtitle": "حذف او استعادة تلقائي للاصول على هذا الجهاز عند تنفيذ العملية على الويب", + "advanced_settings_sync_remote_deletions_title": "مزامنة عمليات الحذف عن بعد [تجريبي]", "advanced_settings_tile_subtitle": "إعدادات المستخدم المتقدمة", "advanced_settings_troubleshooting_subtitle": "تمكين الميزات الإضافية لاستكشاف الأخطاء وإصلاحها", "advanced_settings_troubleshooting_title": "استكشاف الأخطاء وإصلاحها", @@ -357,6 +393,7 @@ "album_cover_updated": "تم تحديث غلاف الألبوم", "album_delete_confirmation": "هل أنت متأكد أنك تريد حذف الألبوم {album}؟", "album_delete_confirmation_description": "إذا تمت مشاركة هذا الألبوم، فلن يتمكن المستخدمون الآخرون من الوصول إليه بعد الآن.", + "album_deleted": "تم حذف الالبوم", "album_info_card_backup_album_excluded": "مستبعد", "album_info_card_backup_album_included": "متضمنة", "album_info_updated": "تم تحديث معلومات الألبوم", @@ -366,6 +403,7 @@ "album_options": "إعدادات الألبوم", "album_remove_user": "هل ترغب في إزالة المستخدم؟", "album_remove_user_confirmation": "هل أنت متأكد أنك تريد إزالة {user}؟", + "album_search_not_found": "لم يتم ايجاد البوم مطابق لبحثك", "album_share_no_users": "يبدو أنك قمت بمشاركة هذا الألبوم مع جميع المستخدمين أو ليس لديك أي مستخدم للمشاركة معه.", "album_updated": "تم تحديث الألبوم", "album_updated_setting_description": "تلقي إشعارًا عبر البريد الإلكتروني عندما يحتوي الألبوم المشترك على محتويات جديدة", @@ -382,6 +420,10 @@ "album_with_link_access": "السماح لأي شخص لديه الرابط برؤية الصور والأشخاص الموجودين في هذا الألبوم.", "albums": "الألبومات", "albums_count": "{count, plural, one {{count, number} ألبوم} other {{count, number} ألبومات}}", + "albums_default_sort_order": "ترتيب الألبوم الافتراضي", + "albums_default_sort_order_description": "ترتيب فرز الأصول الأولي عند إنشاء ألبومات جديدة.", + "albums_feature_description": "مجموعة من الأصول التي يمكن مشاركتها مع مستخدمين آخرين.", + "albums_on_device_count": "عدد الالبومات على الجهاز ({count})", "all": "الكل", "all_albums": "جميع الألبومات", "all_people": "جميع الأشخاص", @@ -392,20 +434,23 @@ "allow_public_user_to_upload": "السماح للمستخدم العام بالرفع", "alt_text_qr_code": "صورة رمز الاستجابة السريعة (QR)", "anti_clockwise": "عكس اتجاه عقارب الساعة", - "api_key": "مفتاح واجهة برمجة التطبيقات", + "api_key": "مفتاح API", "api_key_description": "سيتم عرض هذه القيمة مرة واحدة فقط. يرجى التأكد من نسخها قبل إغلاق النافذة.", "api_key_empty": "يجب ألا يكون اسم مفتاح API فارغًا", - "api_keys": "مفاتيح واجهة برمجة التطبيقات", - "app_bar_signout_dialog_content": "هل أنت متأكد أنك تريد الخروج", + "api_keys": "مفاتيح API", + "app_bar_signout_dialog_content": "هل أنت متأكد أنك تريد تسجيل الخروج؟", "app_bar_signout_dialog_ok": "نعم", "app_bar_signout_dialog_title": "خروج", "app_settings": "إعدادات التطبيق", "appears_in": "يظهر في", "archive": "الأرشيف", + "archive_action_prompt": "{count} اضيف إلى الارشيف", "archive_or_unarchive_photo": "أرشفة الصورة أو إلغاء أرشفتها", "archive_page_no_archived_assets": "لم يتم العثور على الأصول المؤرشفة", + "archive_page_title": "ارشيف ({count})", "archive_size": "حجم الأرشيف", "archive_size_description": "تكوين حجم الأرشيف للتنزيلات (بالجيجابايت)", + "archived": "مؤرشفة", "archived_count": "{count, plural, other {الأرشيف #}}", "are_these_the_same_person": "هل هؤلاء هم نفس الشخص؟", "are_you_sure_to_do_this": "هل انت متأكد من أنك تريد أن تفعل هذا؟", @@ -427,37 +472,56 @@ "asset_list_settings_title": "شبكة الصور", "asset_offline": "المحتوى غير اتصال", "asset_offline_description": "لم يعد هذا الأصل الخارجي موجودًا على القرص. يرجى الاتصال بمسؤول Immich للحصول على المساعدة.", + "asset_restored_successfully": "تم استعادة الاصل بنجاح", "asset_skipped": "تم تخطيه", "asset_skipped_in_trash": "في سلة المهملات", "asset_uploaded": "تم الرفع", "asset_uploading": "جارٍ الرفع…", + "asset_viewer_settings_subtitle": "إدارة إعدادات عارض المعرض الخاص بك", "asset_viewer_settings_title": "عارض الأصول", "assets": "المحتويات", "assets_added_count": "تمت إضافة {count, plural, one {# محتوى} other {# محتويات}}", "assets_added_to_album_count": "تمت إضافة {count, plural, one {# الأصل} other {# الأصول}} إلى الألبوم", - "assets_added_to_name_count": "تم إضافة {count, plural, one {# محتوى} other {# محتويات }} إلى {hasName, select, true {{name}} other {ألبوم جديد}}", + "assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} لايمكن اضافته الى الالبوم", "assets_count": "{count, plural, one {# محتوى} other {# محتويات}}", + "assets_deleted_permanently": "{count} الاص(و)ل المحذوف(ه) بشكل دائم", + "assets_deleted_permanently_from_server": "{count} الاص(و)ل المحذوف(ه) بشكل دائمي من خادم Immich", + "assets_downloaded_failed": "{count, plural, one {تم التحميل # ملف - {error} ملف فشل} other {تم التحميل # ملفات - {error} ملفات فشلت}}", + "assets_downloaded_successfully": "{count, plural, one {تم التحميل # ملف بنجاح} other {تم التحميل # ملفات بنجاح}}", "assets_moved_to_trash_count": "تم نقل {count, plural, one {# محتوى} other {# محتويات}} إلى سلة المهملات", "assets_permanently_deleted_count": "تم حذف {count, plural, one {# هذا المحتوى} other {# هذه المحتويات}} بشكل دائم", "assets_removed_count": "تمت إزالة {count, plural, one {# محتوى} other {# محتويات}}", + "assets_removed_permanently_from_device": "{count} الاص(و)ل محذوف(ه) من الجهاز", "assets_restore_confirmation": "هل أنت متأكد من أنك تريد استعادة جميع الأصول المحذوفة؟ لا يمكنك التراجع عن هذا الإجراء! لاحظ أنه لا يمكن استعادة أي أصول غير متصلة بهذه الطريقة.", "assets_restored_count": "تمت استعادة {count, plural, one {# محتوى} other {# محتويات}}", + "assets_restored_successfully": "{count} الاص(و)ل المستعاد(ه) بنجاح", + "assets_trashed": "{count} الاصل(و) ل المنقوله الى سلة المهملات", "assets_trashed_count": "تم إرسال {count, plural, one {# محتوى} other {# محتويات}} إلى سلة المهملات", + "assets_trashed_from_server": "{count} الاص(و)ل المنقولة الى سلة المهملات من خادم Immich", "assets_were_part_of_album_count": "{count, plural, one {هذا المحتوى} other {هذه المحتويات}} في الألبوم بالفعل", "authorized_devices": "الأجهزه المخولة", + "automatic_endpoint_switching_subtitle": "اتصل محليا من خلال شبكه Wi-Fi عند توفرها و استخدم اتصالات بديله في الاماكن الاخرى", + "automatic_endpoint_switching_title": "تبديل URL تلقائي", + "autoplay_slideshow": "تشغيل تلقائي لعرض الشرائح", "back": "خلف", "back_close_deselect": "الرجوع أو الإغلاق أو إلغاء التحديد", + "background_location_permission": "اذن الوصول للموقع في الخلفية", + "background_location_permission_content": "للتمكن من تبديل الشبكه بالخلفية، Immich يحتاج*دائما* للحصول على موقع دقيق ليتمكن التطبيق من قرائة اسم شبكة الWi-Fi", + "backup": "دعم", + "backup_album_selection_page_albums_device": "الالبومات على الجهاز ({count})", "backup_album_selection_page_albums_tap": "انقر للتضمين، وانقر نقرًا مزدوجًا للاستثناء", "backup_album_selection_page_assets_scatter": "يمكن أن تنتشر الأصول عبر ألبومات متعددة. وبالتالي، يمكن تضمين الألبومات أو استبعادها أثناء عملية النسخ الاحتياطي.", "backup_album_selection_page_select_albums": "حدد الألبومات", "backup_album_selection_page_selection_info": "معلومات الاختيار", "backup_album_selection_page_total_assets": "إجمالي الأصول الفريدة", "backup_all": "الجميع", - "backup_background_service_backup_failed_message": "فشل في النسخ الاحتياطي للأصول. جارٍ إعادة المحاولة...", - "backup_background_service_connection_failed_message": "فشل في الاتصال بالخادم. جارٍ إعادة المحاولة...", - "backup_background_service_default_notification": "التحقق من الأصول الجديدة ...", + "backup_background_service_backup_failed_message": "فشل في النسخ الاحتياطي للأصول. جارٍ إعادة المحاولة…", + "backup_background_service_connection_failed_message": "فشل في الاتصال بالخادم. جارٍ إعادة المحاولة…", + "backup_background_service_current_upload_notification": "تحميل {filename}", + "backup_background_service_default_notification": "التحقق من الأصول الجديدة…", "backup_background_service_error_title": "خطأ في النسخ الاحتياطي", - "backup_background_service_in_progress_notification": "النسخ الاحتياطي للأصول الخاصة بك...", + "backup_background_service_in_progress_notification": "النسخ الاحتياطي للأصول الخاصة بك…", + "backup_background_service_upload_failure_notification": "فشل تحميل {filename}", "backup_controller_page_albums": "ألبومات احتياطية", "backup_controller_page_background_app_refresh_disabled_content": "قم بتمكين تحديث تطبيق الخلفية في الإعدادات > عام > تحديث تطبيق الخلفية لاستخدام النسخ الاحتياطي في الخلفية.", "backup_controller_page_background_app_refresh_disabled_title": "تم تعطيل تحديث التطبيق في الخلفية", @@ -468,17 +532,22 @@ "backup_controller_page_background_battery_info_title": "تحسين البطارية", "backup_controller_page_background_charging": "فقط أثناء الشحن", "backup_controller_page_background_configure_error": "فشل في تكوين خدمة الخلفية", + "backup_controller_page_background_delay": "تاخير الخزن التلقائي للاصول: {duration}", "backup_controller_page_background_description": "قم بتشغيل خدمة الخلفية لإجراء نسخ احتياطي لأي أصول جديدة تلقائيًا دون الحاجة إلى فتح التطبيق", "backup_controller_page_background_is_off": "تم إيقاف النسخ الاحتياطي التلقائي للخلفية", "backup_controller_page_background_is_on": "النسخ الاحتياطي التلقائي للخلفية قيد التشغيل", "backup_controller_page_background_turn_off": "قم بإيقاف تشغيل خدمة الخلفية", "backup_controller_page_background_turn_on": "قم بتشغيل خدمة الخلفية", - "backup_controller_page_background_wifi": "فقط على واي فاي", + "backup_controller_page_background_wifi": "فقط على Wi-Fi", "backup_controller_page_backup": "دعم", "backup_controller_page_backup_selected": "المحدد: ", "backup_controller_page_backup_sub": "النسخ الاحتياطي للصور ومقاطع الفيديو", + "backup_controller_page_created": "انشئ في :{date}", "backup_controller_page_desc_backup": "قم بتشغيل النسخ الاحتياطي الأمامي لتحميل الأصول الجديدة تلقائيًا إلى الخادم عند فتح التطبيق.", "backup_controller_page_excluded": "مستبعد: ", + "backup_controller_page_failed": "فشل ({count})", + "backup_controller_page_filename": "اسم الملف : {filename} [{size}]", + "backup_controller_page_id": "هوية: {id}", "backup_controller_page_info": "معلومات النسخ الاحتياطي", "backup_controller_page_none_selected": "لم يتم التحديد", "backup_controller_page_remainder": "بقية", @@ -487,7 +556,8 @@ "backup_controller_page_start_backup": "بدء النسخ الاحتياطي", "backup_controller_page_status_off": "النسخة الاحتياطية التلقائية غير فعالة", "backup_controller_page_status_on": "النسخة الاحتياطية التلقائية فعالة", - "backup_controller_page_to_backup": "الألبومات الاحتياطية", + "backup_controller_page_storage_format": "{used} من {total} مستخدم", + "backup_controller_page_to_backup": "الألبومات التي سيتم نسخها احتياطيا", "backup_controller_page_total_sub": "جميع الصور ومقاطع الفيديو الفريدة من ألبومات مختارة", "backup_controller_page_turn_off": "قم بإيقاف تشغيل النسخ الاحتياطي المقدمة", "backup_controller_page_turn_on": "قم بتشغيل النسخ الاحتياطي المقدمة", @@ -499,7 +569,14 @@ "backup_manual_success": "نجاح", "backup_manual_title": "حالة التحميل", "backup_options_page_title": "خيارات النسخ الاحتياطي", + "backup_setting_subtitle": "ادارة اعدادات التحميل في الخلفية والمقدمة", "backward": "الى الوراء", + "beta_sync": "حالة المزامنة التجريبية", + "beta_sync_subtitle": "ادارة نظام المزامنة الجديد", + "biometric_auth_enabled": "المصادقة البايومترية مفعله", + "biometric_locked_out": "لقد قفلت عنك المصادقة البيومترية", + "biometric_no_options": "لا توجد خيارات بايومترية متوفرة", + "biometric_not_available": "االمصادقة البيومترية غير متاحة على هذا الجهاز", "birthdate_saved": "تم حفظ تاريخ الميلاد بنجاح", "birthdate_set_description": "يتم استخدام تاريخ الميلاد لحساب عمر هذا الشخص وقت التقاط الصورة.", "blurred_background": "خلفية مشوشة", @@ -513,13 +590,14 @@ "cache_settings_clear_cache_button": "مسح ذاكرة التخزين المؤقت", "cache_settings_clear_cache_button_title": "يقوم بمسح ذاكرة التخزين المؤقت للتطبيق.سيؤثر هذا بشكل كبير على أداء التطبيق حتى إعادة بناء ذاكرة التخزين المؤقت.", "cache_settings_duplicated_assets_clear_button": "واضح", - "cache_settings_duplicated_assets_subtitle": "الصور ومقاطع الفيديو اللتي تم تجاهلها المدرجة في التطبيق", + "cache_settings_duplicated_assets_subtitle": "الصور والفيديوهات اللتي تم تجاهلها في التطبيق", + "cache_settings_duplicated_assets_title": "الاصول المكررة ({count})", "cache_settings_statistics_album": "مكتبه الصور المصغره", "cache_settings_statistics_full": "صور كاملة", "cache_settings_statistics_shared": "صورة ألبوم مشتركة", "cache_settings_statistics_thumbnail": "الصورة المصغرة", "cache_settings_statistics_title": "استخدام ذاكرة التخزين المؤقت", - "cache_settings_subtitle": "تحكم في سلوك التخزين المؤقت لتطبيق الجوال.", + "cache_settings_subtitle": "تحكم في سلوك التخزين المؤقت لتطبيق Immich الجوال", "cache_settings_tile_subtitle": "التحكم في سلوك التخزين المحلي", "cache_settings_tile_title": "التخزين المحلي", "cache_settings_title": "إعدادات التخزين المؤقت", @@ -527,11 +605,17 @@ "camera_brand": "علامة الكاميرا التجارية", "camera_model": "طراز الكاميرا", "cancel": "إلغاء", - "cancel_search": "الغي البحث", + "cancel_search": "الغاء البحث", + "canceled": "تم الالغاء", + "canceling": "جارِ الالغاء", "cannot_merge_people": "لا يمكن دمج الأشخاص", "cannot_undo_this_action": "لا يمكنك التراجع عن هذا الإجراء!", "cannot_update_the_description": "لا يمكن تحديث الوصف", + "cast": "بث", + "cast_description": "ضبط وجهات البث المتوفرة", "change_date": "غيّر التاريخ", + "change_description": "تغيير الوصف", + "change_display_order": "تغيير ترتيب العرض", "change_expiration_time": "تغيير وقت انتهاء الصلاحية", "change_location": "غيّر الموقع", "change_name": "تغيير الإسم", @@ -539,13 +623,16 @@ "change_password": "تغيير كلمة المرور", "change_password_description": "هذه إما هي المرة الأولى التي تقوم فيها بتسجيل الدخول إلى النظام أو أنه تم تقديم طلب لتغيير كلمة المرور الخاصة بك. الرجاء إدخال كلمة المرور الجديدة أدناه.", "change_password_form_confirm_password": "تأكيد كلمة المرور", - "change_password_form_description": "مرحبًا ،هذه هي المرة الأولى التي تقوم فيها بالتسجيل في النظام أو تم تقديم طلب لتغيير كلمة المرور الخاصة بك.الرجاء إدخال كلمة المرور الجديدة أدناه", + "change_password_form_description": "مرحبًا {name}،\n\nاما ان تكون هذه هي المرة الأولى التي تقوم فيها بالتسجيل في النظام أو تم تقديم طلب لتغيير كلمة المرور الخاصة بك. الرجاء إدخال كلمة المرور الجديدة أدناه.", "change_password_form_new_password": "كلمة المرور الجديدة", "change_password_form_password_mismatch": "كلمة المرور غير مطابقة", "change_password_form_reenter_new_password": "أعد إدخال كلمة مرور جديدة", - "change_pin_code": "تغيير الرقم السري", + "change_pin_code": "تغيير رمز PIN", "change_your_password": "غير كلمة المرور الخاصة بك", "changed_visibility_successfully": "تم تغيير الرؤية بنجاح", + "check_corrupt_asset_backup": "التحقق من وجود نسخ احتياطية فاسدة للاصول", + "check_corrupt_asset_backup_button": "اجراء فحص", + "check_corrupt_asset_backup_description": "قم بإجراء هذا الفحص فقط عبر شبكة Wi-Fi وبعد نسخ جميع الأصول احتياطيًا. قد يستغرق الإجراء بضع دقائق.", "check_logs": "تحقق من السجلات", "choose_matching_people_to_merge": "اختر الأشخاص المتطابقين لدمجهم", "city": "المدينة", @@ -554,6 +641,14 @@ "clear_all_recent_searches": "مسح جميع عمليات البحث الأخيرة", "clear_message": "إخلاء الرسالة", "clear_value": "إخلاء القيمة", + "client_cert_dialog_msg_confirm": "حسنا", + "client_cert_enter_password": "ادخل كلمة سر", + "client_cert_import": "استيراد", + "client_cert_import_success_msg": "تم استيراد شهادة العميل", + "client_cert_invalid_msg": "ملف شهادة عميل غير صالحة او كلمة سر غير صحيحة", + "client_cert_remove_msg": "تم ازالة شهادة العميل", + "client_cert_subtitle": "يدعم صيغ PKCS12 (.p12, .pfx)فقط. استيراد/ازالة الشهادات متاح فقط قبل تسجيل الدخول", + "client_cert_title": "شهادة مستخدم SSL", "clockwise": "باتجاه عقارب الساعة", "close": "إغلاق", "collapse": "طي", @@ -566,21 +661,27 @@ "comments_are_disabled": "التعليقات معطلة", "common_create_new_album": "إنشاء ألبوم جديد", "common_server_error": "يرجى التحقق من اتصال الشبكة الخاص بك ، والتأكد من أن الجهاز قابل للوصول وإصدارات التطبيق/الجهاز متوافقة.", + "completed": "اكتمل", "confirm": "تأكيد", "confirm_admin_password": "تأكيد كلمة مرور المسؤول", "confirm_delete_face": "هل أنت متأكد من حذف وجه {name} من الأصول؟", "confirm_delete_shared_link": "هل أنت متأكد أنك تريد حذف هذا الرابط المشترك؟", "confirm_keep_this_delete_others": "سيتم حذف جميع الأصول الأخرى في المجموعة باستثناء هذا الأصل. هل أنت متأكد من أنك تريد المتابعة؟", - "confirm_new_pin_code": "ثبت الرقم السري الجديد", + "confirm_new_pin_code": "ثبت رمز PIN الجديد", "confirm_password": "تأكيد كلمة المرور", + "confirm_tag_face": "هل تريد وضع علامة على هذا الوجه {name}؟", + "confirm_tag_face_unnamed": "هل تريد وضع علامة على هذا الوجه؟", + "connected_device": "جهاز متصل", + "connected_to": "متصل ب", "contain": "محتواة", "context": "السياق", "continue": "متابعة", "control_bottom_app_bar_create_new_album": "إنشاء ألبوم جديد", - "control_bottom_app_bar_delete_from_immich": " حذف منال تطبيق", + "control_bottom_app_bar_delete_from_immich": "حذف من Immich", "control_bottom_app_bar_delete_from_local": "حذف من الجهاز", "control_bottom_app_bar_edit_location": "تحديد الوجهة", "control_bottom_app_bar_edit_time": "تحرير التاريخ والوقت", + "control_bottom_app_bar_share_link": "مشاركة رابط", "control_bottom_app_bar_share_to": "مشاركة إلى", "control_bottom_app_bar_trash_from_immich": "حذفه ونقله في سله المهملات", "copied_image_to_clipboard": "تم نسخ الصورة إلى الحافظة.", @@ -602,6 +703,7 @@ "create_link": "إنشاء رابط", "create_link_to_share": "إنشاء رابط للمشاركة", "create_link_to_share_description": "السماح لأي شخص لديه الرابط بمشاهدة الصورة (الصور) المحددة", + "create_new": "انشاء جديد", "create_new_person": "إنشاء شخص جديد", "create_new_person_hint": "تعيين المحتويات المحددة لشخص جديد", "create_new_user": "إنشاء مستخدم جديد", @@ -611,14 +713,18 @@ "create_tag_description": "أنشئ علامة جديدة. بالنسبة للعلامات المتداخلة، يرجى إدخال المسار الكامل للعلامة بما في ذلك الخطوط المائلة للأمام.", "create_user": "إنشاء مستخدم", "created": "تم الإنشاء", + "created_at": "مخلوق", + "crop": "قص", "curated_object_page_title": "أشياء", "current_device": "الجهاز الحالي", - "current_pin_code": "الرقم السري الحالي", + "current_pin_code": "رمز PIN الحالي", + "current_server_address": "عنوان الخادم الحالي", "custom_locale": "لغة مخصصة", "custom_locale_description": "تنسيق التواريخ والأرقام بناءً على اللغة والمنطقة", "daily_title_text_date": "E ، MMM DD", "daily_title_text_date_year": "E ، MMM DD ، yyyy", "dark": "معتم", + "dark_theme": "تبديل المظهر الداكن", "date_after": "التارخ بعد", "date_and_time": "التاريخ و الوقت", "date_before": "التاريخ قبل", @@ -634,11 +740,13 @@ "default_locale": "اللغة الافتراضية", "default_locale_description": "تنسيق التواريخ والأرقام بناءً على لغة المتصفح الخاص بك", "delete": "حذف", + "delete_action_confirmation_message": "هل انت متأكد من حذف هذا الملف؟ هذا سؤدي الى نقل الملف الى سلة مهملات الخادم وسيتم اشعارك ان كنت تريد حذفه على الجهاز", + "delete_action_prompt": "تم حذف {count}", "delete_album": "حذف الألبوم", "delete_api_key_prompt": "هل أنت متأكد أنك تريد حذف مفتاح API هذا؟", - "delete_dialog_alert": " هذه العناصر سيتم حذفها بشكل دائم من جهازك ومن تطبيق", - "delete_dialog_alert_local": " العناصر التي تم حذفها من جهازك ولكنها موجوده في تطبيق", - "delete_dialog_alert_local_non_backed_up": "بعض العناصر التي سيتم حذفها بشكل دائم ولا يوجد لها نسخه احتياطيه في تطبيق ", + "delete_dialog_alert": "هذه العناصر سيتم حذفها بشكل دائم من Immich و من جهازك", + "delete_dialog_alert_local": "العناصر التي سيتم حذفها من جهازك ولكن تبقى موجوده في خادم Immich", + "delete_dialog_alert_local_non_backed_up": "بعض العناصر غير مدعومة بنسخة احتياطية على Immich وسيتم إزالتها نهائيًا من جهازك", "delete_dialog_alert_remote": "العناصر التي سيتم حذفها بشكل دائم من تطبيق", "delete_dialog_ok_force": "احذف على أي حال", "delete_dialog_title": "الحذف بشكل نهائي", @@ -647,9 +755,12 @@ "delete_key": "حذف المفتاح", "delete_library": "حذف المكتبة", "delete_link": "حذف الرابط", + "delete_local_action_prompt": "تم حذف {count} من الجهاز", "delete_local_dialog_ok_backed_up_only": "حذف النسخة الاحتياطية فقط", "delete_local_dialog_ok_force": "احذف على أي حال", "delete_others": "حذف الأخرى", + "delete_permanently": "حذف بشكل نهائي", + "delete_permanently_action_prompt": "تم حذف {count} بشكل نهائي", "delete_shared_link": "حذف الرابط المشترك", "delete_shared_link_dialog_title": "حذف الرابط المشترك", "delete_tag": "حذف العلامة", @@ -660,11 +771,14 @@ "description": "وصف", "description_input_hint_text": "اضف وصفا...", "description_input_submit_error": "خطأ تحديث الوصف ، تحقق من السجل لمزيد من التفاصيل", + "deselect_all": "الغاء تحديد الكل", "details": "تفاصيل", "direction": "الإتجاه", "disabled": "معطل", "disallow_edits": "منع التعديلات", + "discord": "دسكورد", "discover": "اكتشف", + "discovered_devices": "اجهزة مكتشفة", "dismiss_all_errors": "تجاهل كافة الأخطاء", "dismiss_error": "تجاهل الخطأ", "display_options": "عرض الخيارات", @@ -675,12 +789,26 @@ "documentation": "الوثائق", "done": "تم", "download": "تنزيل", + "download_action_prompt": "يتم تنزيل {count} ملف", + "download_canceled": "الغي التنزيل", + "download_complete": "اكتمل التنزيل", + "download_enqueue": "تنزيل في قائمة الانتظار", + "download_error": "خطا في التنزيل", + "download_failed": "فشل التنزيل", + "download_finished": "انتهى التنزيل", "download_include_embedded_motion_videos": "مقاطع الفيديو المدمجة", "download_include_embedded_motion_videos_description": "تضمين مقاطع الفيديو المضمنة في الصور المتحركة كملف منفصل", + "download_notfound": "لم يعثر على التنزيل", + "download_paused": "اوقف التنزيل", "download_settings": "التنزيلات", "download_settings_description": "إدارة الإعدادات المتعلقة بتنزيل المحتويات", + "download_started": "بدا التنزيل", + "download_sucess": "نجح التنزيل", + "download_sucess_android": "تم تحميل الوسائط الى DCIM/Immich", + "download_waiting_to_retry": "الانتظار للمحاولة", "downloading": "جارٍ التنزيل", "downloading_asset_filename": "{filename} قيد التنزيل", + "downloading_media": "تحميل الوسائط", "drop_files_to_upload": "قم بإسقاط الملفات في أي مكان لرفعها", "duplicates": "التكرارات", "duplicates_description": "قم بحل كل مجموعة من خلال الإشارة إلى التكرارات، إن وجدت", @@ -690,6 +818,8 @@ "edit_avatar": "تعديل الصورة الشخصية", "edit_date": "تعديل التاريخ", "edit_date_and_time": "تعديل التاريخ والوقت", + "edit_description": "تعديل الوصف", + "edit_description_prompt": "الرجاء اختيار وصف جديد:", "edit_exclusion_pattern": "تعديل نمط الاستبعاد", "edit_faces": "تعديل الوجوه", "edit_import_path": "تعديل مسار الاستيراد", @@ -697,6 +827,7 @@ "edit_key": "تعديل المفتاح", "edit_link": "تغيير الرابط", "edit_location": "تعديل الموقع", + "edit_location_action_prompt": "{count} موقع تم تعديله", "edit_location_dialog_title": "موقع", "edit_name": "تعديل الاسم", "edit_people": "تعديل الأشخاص", @@ -710,15 +841,25 @@ "editor_crop_tool_h2_aspect_ratios": "نسب العرض إلى الارتفاع", "editor_crop_tool_h2_rotation": "التدوير", "email": "البريد الإلكتروني", + "email_notifications": "تنبيهات البريد الالكتروني", + "empty_folder": "هذا المجلد فارغ", "empty_trash": "أفرغ سلة المهملات", "empty_trash_confirmation": "هل أنت متأكد أنك تريد إفراغ سلة المهملات؟ سيؤدي هذا إلى إزالة جميع المحتويات الموجودة في سلة المهملات بشكل نهائي من Immich.\nلا يمكنك التراجع عن هذا الإجراء!", "enable": "تفعيل", + "enable_backup": "تشغيل النسخ الاحتياطي", + "enable_biometric_auth_description": "أدخل رمز PIN الخاص بك لتمكين المصادقة البيومترية", "enabled": "مفعل", "end_date": "تاريخ الإنتهاء", - "enter_wifi_name": "Enter WiFi name", + "enqueued": "مُدرج في الطابور", + "enter_wifi_name": "ادخل اسم Wi-Fi", + "enter_your_pin_code": "أدخل رمز PIN الخاص بك", + "enter_your_pin_code_subtitle": "أدخل رمز PIN الخاص بك للوصول إلى المجلد المقفل", "error": "خطأ", + "error_change_sort_album": "فشل في تغيير ترتيب الألبوم", "error_delete_face": "حدث خطأ في حذف الوجه من الأصول", "error_loading_image": "حدث خطأ أثناء تحميل الصورة", + "error_saving_image": "خطأ: {error}", + "error_tag_face_bounding_box": "خطأ في وضع علامة على الوجه - لا يمكن الحصول على إحداثيات المربع المحيط", "error_title": "خطأ - حدث خللٌ ما", "errors": { "cannot_navigate_next_asset": "لا يمكن الانتقال إلى المحتوى التالي", @@ -746,10 +887,12 @@ "failed_to_keep_this_delete_others": "فشل في الاحتفاظ بهذا الأصل وحذف الأصول الأخرى", "failed_to_load_asset": "فشل تحميل المحتوى", "failed_to_load_assets": "فشل تحميل المحتويات", + "failed_to_load_notifications": "فشل تحميل الإشعارات", "failed_to_load_people": "فشل تحميل الأشخاص", "failed_to_remove_product_key": "تعذر إزالة مفتاح المنتج", "failed_to_stack_assets": "فشل في تكديس المحتويات", "failed_to_unstack_assets": "فشل في فصل المحتويات", + "failed_to_update_notification_status": "فشل في تحديث حالة الإشعار", "import_path_already_exists": "مسار الاستيراد هذا موجود مسبقًا.", "incorrect_email_or_password": "بريد أو كلمة مرور غير صحيحة", "paths_validation_failed": "فشل في التحقق من {paths, plural, one {# مسار} other {# مسارات}}", @@ -766,6 +909,7 @@ "unable_to_archive_unarchive": "تعذر {archived, select, true {الأرشفة} other {الإخراج من الأرشيف}}", "unable_to_change_album_user_role": "غير قادر على تغيير دور مستخدم الألبوم", "unable_to_change_date": "غير قادر على تغيير التاريخ", + "unable_to_change_description": "غير قادر على تغيير الوصف", "unable_to_change_favorite": "غير قادر على تغيير المفضلة لمحتوى", "unable_to_change_location": "غير قادر على تغيير الموقع", "unable_to_change_password": "غير قادر على تغيير كلمة المرور", @@ -809,6 +953,7 @@ "unable_to_remove_partner": "غير قادر على إزالة الشريك", "unable_to_remove_reaction": "غير قادر على إزالة رد الفعل", "unable_to_reset_password": "غير قادر على إعادة تعيين كلمة المرور", + "unable_to_reset_pin_code": "غير قادر على إعادة تعيين رمز PIN", "unable_to_resolve_duplicate": "غير قادر على حل التكرارات", "unable_to_restore_assets": "غير قادر على استعادة المحتويات", "unable_to_restore_trash": "غير قادر على استعادة سلة المهملات", @@ -842,6 +987,9 @@ "exif_bottom_sheet_location": "موقع", "exif_bottom_sheet_people": "الناس", "exif_bottom_sheet_person_add_person": "اضف اسما", + "exif_bottom_sheet_person_age_months": "العمر {months} اشهر", + "exif_bottom_sheet_person_age_year_months": "العمر ١ سنة،{months} اشهر", + "exif_bottom_sheet_person_age_years": "العمر {years}", "exit_slideshow": "خروج من العرض التقديمي", "expand_all": "توسيع الكل", "experimental_settings_new_asset_list_subtitle": "أعمال جارية", @@ -855,13 +1003,20 @@ "explorer": "المستكشف", "export": "تصدير", "export_as_json": "تصدير كـ JSON", + "export_database": "تصدير قاعدة البيانات", + "export_database_description": "تصدير قاعدة البيانات من نوع SQLite", "extension": "الإمتداد", "external": "خارجي", "external_libraries": "المكتبات الخارجية", - "external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom", + "external_network": "شبكة خارجية", + "external_network_sheet_info": "عندما لا يتواجد على شبكة Wi-Fi المفضلة، فإنه سيتصل بالخادم من خلال أول عناوين URL أدناه التي يمكنه الوصول إليها، بدءًا من الأعلى إلى الأسفل", "face_unassigned": "غير معين", + "failed": "فشل", + "failed_to_authenticate": "فشل في المصادقة", "failed_to_load_assets": "فشل تحميل الأصول", + "failed_to_load_folder": "فشل تحميل المجلد", "favorite": "مفضل", + "favorite_action_prompt": "{count} اضيف إلى المفضلات", "favorite_or_unfavorite_photo": "تفضيل أو إلغاء تفضيل الصورة", "favorites": "المفضلة", "favorites_page_no_favorites": "لم يتم العثور على الأصول المفضلة", @@ -872,18 +1027,26 @@ "file_name_or_extension": "اسم الملف أو امتداده", "filename": "اسم الملف", "filetype": "نوع الملف", + "filter": "تصفية", "filter_people": "تصفية الاشخاص", + "filter_places": "تصفية الاماكن", "find_them_fast": "يمكنك العثور عليها بسرعة بالاسم من خلال البحث", "fix_incorrect_match": "إصلاح المطابقة غير الصحيحة", + "folder": "مجلد", + "folder_not_found": "لم يتم العثور على المجلد", "folders": "المجلدات", "folders_feature_description": "تصفح عرض المجلد للصور ومقاطع الفيديو الموجودة على نظام الملفات", "forward": "إلى الأمام", + "gcast_enabled": "كوكل كاست", + "gcast_enabled_description": "تقوم هذه الميزة بتحميل الموارد الخارجية من Google حتى تعمل.", "general": "عام", "get_help": "الحصول على المساعدة", + "get_wifiname_error": "تعذر الحصول على اسم شبكة Wi-Fi. تأكد من منح الأذونات اللازمة واتصالك بشبكة Wi-Fi", "getting_started": "البدء", "go_back": "الرجوع للخلف", "go_to_folder": "اذهب إلى المجلد", "go_to_search": "اذهب إلى البحث", + "grant_permission": "منح الاذن", "group_albums_by": "تجميع الألبومات حسب...", "group_country": "مجموعة البلد", "group_no": "بدون تجميع", @@ -893,6 +1056,12 @@ "haptic_feedback_switch": "تمكين ردود الفعل اللمسية", "haptic_feedback_title": "ردود فعل لمسية", "has_quota": "محدد بحصة", + "header_settings_add_header_tip": "اضاف راس", + "header_settings_field_validator_msg": "القيمة لا يمكن ان تكون فارغة", + "header_settings_header_name_input": "اسم الرأس", + "header_settings_header_value_input": "قيمة الرأس", + "headers_settings_tile_subtitle": "قم بتعريف رؤوس الوكيل التي يجب أن يرسلها التطبيق مع كل طلب شبكة", + "headers_settings_tile_title": "رؤوس وكيل مخصصة", "hi_user": "مرحبا {name} ({email})", "hide_all_people": "إخفاء جميع الأشخاص", "hide_gallery": "اخفاء المعرض", @@ -911,11 +1080,16 @@ "home_page_delete_remote_err_local": "الأصول المحلية في التحديد البعيد المحذوف، سوف يتخطى", "home_page_favorite_err_local": "لا يمكن تفضيل الأصول المحلية بعد، سوف يتخطى", "home_page_favorite_err_partner": "لا يمكن الأصول الشريكة المفضلة بعد ، سوف يتخطى", - "home_page_first_time_notice": "إذا كانت هذه هي المرة الأولى التي تستخدم فيها التطبيق، فيرجى التأكد من اختيار ألبوم (ألبومات) احتياطية حتى يتمكن المخطط الزمني من ملء الصور ومقاطع الفيديو في الألبوم (الألبومات).", + "home_page_first_time_notice": "إذا كانت هذه هي المرة الأولى التي تستخدم فيها التطبيق، فيرجى التأكد من اختيار ألبوم (ألبومات) احتياطية حتى يتمكن المخطط الزمني من ملء الصور ومقاطع الفيديو فيه", + "home_page_locked_error_local": "لا يمكن نقل الأصول المحلية إلى المجلد المقفل، يتم التخطي", + "home_page_locked_error_partner": "لا يمكن نقل أصول الشريك إلى المجلد المقفل، يتم التخطي", "home_page_share_err_local": "لا يمكن مشاركة الأصول المحلية عبر الرابط ، سوف يتخطى", "home_page_upload_err_limit": "لا يمكن إلا تحميل 30 أحد الأصول في وقت واحد ، سوف يتخطى", "host": "المضيف", "hour": "ساعة", + "id": "المعرف", + "ignore_icloud_photos": "تجاهل صور iCloud", + "ignore_icloud_photos_description": "الصور المخزنة في Cloud لن يتم تحميلها إلى خادم Immich", "image": "صورة", "image_alt_text_date": "{isVideo, select, true {Video} other {Image}} تم التقاطها في {date}", "image_alt_text_date_1_person": "{isVideo, select, true {Video} other {Image}} تم التقاطها مع {person1} في {date}", @@ -927,6 +1101,7 @@ "image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {Image}} تم التقاطها في {city}، {country} مع {person1} و{person2} في {date}", "image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {Image}} تم التقاطها في {city}، {country} مع {person1}، {person2}، و{person3} في {date}", "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {Image}} تم التقاطها في {city}, {country} with {person1}, {person2}, مع {additionalCount, number} آخرين في {date}", + "image_saved_successfully": "الصور حُفظت", "image_viewer_page_state_provider_download_started": "بدأ التنزيل", "image_viewer_page_state_provider_download_success": "تم التنزيل بنجاح", "image_viewer_page_state_provider_share_error": "خطأ في المشاركة", @@ -948,8 +1123,16 @@ "night_at_midnight": "كل ليلة عند منتصف الليل", "night_at_twoam": "كل ليلة الساعة 2 صباحا" }, + "invalid_date": "تاريخ غير صالح", + "invalid_date_format": "صيغة تاريخ غير صالحة", "invite_people": "دعوة الأشخاص", "invite_to_album": "دعوة إلى الألبوم", + "ios_debug_info_fetch_ran_at": "جرت عملية الجلب في {dateTime}", + "ios_debug_info_last_sync_at": "اخر مزامنة {dateTime}", + "ios_debug_info_no_processes_queued": "لا توجد عمليات خلفية في قائمة الانتظار", + "ios_debug_info_no_sync_yet": "لم يتم تشغيل أي مهمة مزامنة في الخلفية حتى الآن", + "ios_debug_info_processes_queued": "{count, plural, one {{count} عملية خلفية ادخلتةفي طابور} other {{count} عمليات خلفية ادخلت في طابور}}", + "ios_debug_info_processing_ran_at": "المعالجة جرت في {dateTime}", "items_count": "{count, plural, one {# عنصر} other {# عناصر}}", "jobs": "الوظائف", "keep": "احتفظ", @@ -958,6 +1141,9 @@ "kept_this_deleted_others": "تم الاحتفاظ بهذا الأصل وحذف {count, plural, one {# asset} other {# assets}}", "keyboard_shortcuts": "اختصارات لوحة المفاتيح", "language": "اللغة", + "language_no_results_subtitle": "حاول تعديل مصطلح البحث", + "language_no_results_title": "لم يتم العثور على لغات", + "language_search_hint": "البحث عن لغات...", "language_setting_description": "اختر لغتك المفضلة", "last_seen": "اخر ظهور", "latest_version": "احدث اصدار", @@ -977,27 +1163,34 @@ "light": "المضيئ", "like_deleted": "تم حذف الإعجاب", "link_motion_video": "رابط فيديو الحركة", - "link_options": "خيارات الرابط", "link_to_oauth": "الربط مع OAuth", "linked_oauth_account": "حساب مرتبط بـ OAuth", "list": "قائمة", "loading": "تحميل", "loading_search_results_failed": "فشل تحميل نتائج البحث", - "location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current WiFi network's name", + "local_asset_cast_failed": "غير قادر على بث أصل لم يتم تحميله إلى الخادم", + "local_network": "شبكة محلية", + "local_network_sheet_info": "سيتصل التطبيق بالخادم من خلال عنوان URL هذا عند استخدام شبكة Wi-Fi المحددة", + "location_permission": "اذن الموقع", + "location_permission_content": "من أجل استخدام ميزة التبديل التلقائي، يحتاج Immich إلى إذن موقع دقيق حتى يتمكن من قراءة اسم شبكة Wi-Fi الحالية", "location_picker_choose_on_map": "اختر على الخريطة", "location_picker_latitude_error": "أدخل خط عرض صالح", "location_picker_latitude_hint": "أدخل خط العرض الخاص بك هنا", "location_picker_longitude_error": "أدخل خط الطول الصحيح", "location_picker_longitude_hint": "أدخل خط الطول هنا", + "lock": "قفل", + "locked_folder": "مجلد مقفول", "log_out": "تسجيل خروج", "log_out_all_devices": "تسجيل الخروج من كافة الأجهزة", + "logged_in_as": "تم تسجيل الدخول باسم {user}", "logged_out_all_devices": "تم تسجيل الخروج من جميع الأجهزة", "logged_out_device": "تم تسجيل الخروج من الجهاز", "login": "تسجيل الدخول", "login_disabled": "تم تعطيل تسجيل الدخول", - "login_form_api_exception": " استثناء برمجة التطبيقات. يرجى التحقق من عنوان الخادم والمحاولة مرة أخرى ", + "login_form_api_exception": "استثناء API. يرجى التحقق من عنوان URL الخادم والمحاولة مرة أخرى.", "login_form_back_button_text": "الرجوع للخلف", "login_form_email_hint": "yoursemail@email.com", + "login_form_endpoint_hint": "http://المنفذ:عنوان‫-ip-الخادم", "login_form_endpoint_url": "url نقطة نهاية الخادم", "login_form_err_http": "يرجى تحديد http:// أو https://", "login_form_err_invalid_email": "بريد إلكتروني خاطئ", @@ -1021,7 +1214,8 @@ "look": "الشكل", "loop_videos": "تكرار مقاطع الفيديو", "loop_videos_description": "فَعْل لتكرار مقطع فيديو تلقائيًا في عارض التفاصيل.", - "main_branch_warning": "أنت تستخدم إصداراً تطويرياً؛ ونحن نوصي بشدة باستخدام إصدار النشر!", + "main_branch_warning": "أنت تستخدم إصداراً قيد التطوير؛ ونحن نوصي بشدة باستخدام إصدار النشر!", + "main_menu": "القائمة الرئيسية", "make": "صنع", "manage_shared_links": "إدارة الروابط المشتركة", "manage_sharing_with_partners": "إدارة المشاركة مع الشركاء", @@ -1031,6 +1225,7 @@ "manage_your_devices": "إدارة الأجهزة التي تم تسجيل الدخول إليها", "manage_your_oauth_connection": "إدارة اتصال OAuth الخاص بك", "map": "الخريطة", + "map_assets_in_bounds": "{count} صور", "map_cannot_get_user_location": "لا يمكن الحصول على موقع المستخدم", "map_location_dialog_yes": "نعم", "map_location_picker_page_use_location": "استخدم هذا الموقع", @@ -1044,13 +1239,18 @@ "map_settings": "إعدادات الخريطة", "map_settings_dark_mode": "الوضع المظلم", "map_settings_date_range_option_day": "24 ساعة الماضية", + "map_settings_date_range_option_days": "الايام {days} الماضية", "map_settings_date_range_option_year": "السنة الفائتة", + "map_settings_date_range_option_years": "السنوات {years} الماضية", "map_settings_dialog_title": "إعدادات الخريطة", "map_settings_include_show_archived": "تشمل الأرشفة", "map_settings_include_show_partners": "تضمين الشركاء", "map_settings_only_show_favorites": "اظهار المفضلة فقط", "map_settings_theme_settings": "مظهر الخريطة", "map_zoom_to_see_photos": "قم بتصغيرها لرؤية الصور", + "mark_all_as_read": "تحديد الكل كمقروء", + "mark_as_read": "تحديد كمقروء", + "marked_all_as_read": "تم تحديد الكل كمقروء", "matches": "تطابقات", "media_type": "نوع الوسائط", "memories": "الذكريات", @@ -1075,6 +1275,13 @@ "month": "شهر", "monthly_title_text_date_format": "ط ط ط", "more": "المزيد", + "move": "تحريك", + "move_off_locked_folder": "تحريك خارج المجلد المقفل", + "move_to_lock_folder_action_prompt": "{count} اضيف إلى المجلد المقفل", + "move_to_locked_folder": "النقل الى مجلد مغلق", + "move_to_locked_folder_confirmation": "هذه الصور والفديوات ستتم ازالتها من جميع الالبومات، ويمكنان تتم مشاهدتها فقط من خلال المجلد المقفل", + "moved_to_archive": "تم نقل {count, plural, one {# اصل} other {# اصول}} الى الارشيف", + "moved_to_library": "تم نقل {count, plural, one {# اصل} other {# اصول}} الى المكتبة", "moved_to_trash": "تم النقل إلى سلة المهملات", "multiselect_grid_edit_date_time_err_read_only": "لا يمكن تعديل تاريخ الأصول (المواد) للقراءة فقط، سوف يتخطى", "multiselect_grid_edit_gps_err_read_only": "لا يمكن تعديل موقع الأصول (المواد) للقراءة فقط، سوف يتخطى", @@ -1082,12 +1289,15 @@ "my_albums": "ألبوماتي", "name": "الاسم", "name_or_nickname": "الاسم أو اللقب", + "networking_settings": "الشبكات", + "networking_subtitle": "إدارة إعدادات نقطة الخادم النهائية", "never": "أبداً", "new_album": "البوم جديد", "new_api_key": "مفتاح API جديد", "new_password": "كلمة المرور الجديدة", "new_person": "شخص جديد", - "new_pin_code": "الرقم السري الجديد", + "new_pin_code": "رمز PIN الجديد", + "new_pin_code_subtitle": "هذه أول مرة تدخل فيها إلى المجلد المقفل. أنشئ رمزًا PIN للوصول بامان إلى هذه الصفحة", "new_user_created": "تم إنشاء مستخدم جديد", "new_version_available": "إصدار جديد متاح", "newest_first": "الأحدث أولاً", @@ -1100,19 +1310,25 @@ "no_archived_assets_message": "أرشفة الصور ومقاطع الفيديو لإخفائها من عرض الصور لديك", "no_assets_message": "انقر لتحميل صورتك الأولى", "no_assets_to_show": "لا توجد أصول لعرضها", + "no_cast_devices_found": "لم يتم ايجاد جهاز بث", "no_duplicates_found": "لم يتم العثور على أي تكرارات.", "no_exif_info_available": "لا تتوفر معلومات exif", "no_explore_results_message": "قم برفع المزيد من الصور لاستكشاف مجموعتك.", "no_favorites_message": "أضف المفضلة للعثور بسرعة على أفضل الصور ومقاطع الفيديو", "no_libraries_message": "إنشاء مكتبة خارجية لعرض الصور ومقاطع الفيديو الخاصة بك", + "no_locked_photos_message": "الصور والفديوهات في المجلد المقفل مخفية ولن تصهر في التصفح او البحث في مكتبتك.", "no_name": "لا اسم", + "no_notifications": "لا توجد تنبيهات", + "no_people_found": "لم يتم العثور على اشخاص مطابقين", "no_places": "لا أماكن", "no_results": "لا يوجد نتائج", "no_results_description": "جرب كلمة رئيسية مرادفة أو أكثر عمومية", "no_shared_albums_message": "قم بإنشاء ألبوم لمشاركة الصور ومقاطع الفيديو مع الأشخاص في شبكتك", "not_in_any_album": "ليست في أي ألبوم", - "note_apply_storage_label_to_previously_uploaded assets": "ملاحظة: لتطبيق تسمية التخزين على المحتويات التي تم رفعها مسبقًا، قم بتشغيل", + "not_selected": "لم يختار", + "note_apply_storage_label_to_previously_uploaded assets": "ملاحظة: لتطبيق سمة التخزين على المحتويات التي تم رفعها مسبقًا، قم بتشغيل", "notes": "ملاحظات", + "nothing_here_yet": "لا يوجد شيء هنا بعد", "notification_permission_dialog_content": "لتمكين الإخطارات ، انتقل إلى الإعدادات و اختار السماح.", "notification_permission_list_tile_content": "منح إذن لتمكين الإخطارات.", "notification_permission_list_tile_enable_button": "تمكين الإخطارات", @@ -1120,16 +1336,22 @@ "notification_toggle_setting_description": "تفعيل إشعارات البريد الإلكتروني", "notifications": "إشعارات", "notifications_setting_description": "إدارة الإشعارات", + "oauth": "OAuth", "official_immich_resources": "الموارد الرسمية لشركة Immich", "offline": "غير متصل", "ok": "نعم", "oldest_first": "الأقدم أولا", + "on_this_device": "على هذا الجهاز", "onboarding": "الإعداد الأولي", - "onboarding_privacy_description": "تعتمد الميزات التالية (اختياري) على خدمات خارجية، ويمكن تعطيلها في أي وقت في إعدادات الإدارة.", + "onboarding_locale_description": "اختر لغتك المفضلة. يمكنك تغييرها فيما بعد في الاعدادات.", + "onboarding_privacy_description": "تعتمد الميزات التالية (اختياري) على خدمات خارجية، ويمكن تعطيلها في أي وقت في الاعدادات.", + "onboarding_server_welcome_description": "لنقم باعداد نسختك من البرنامج مع بعض الاعدادات الشائعة.", "onboarding_theme_description": "اختر نسق الألوان للنسخة الخاصة بك. يمكنك تغيير ذلك لاحقًا في إعداداتك.", + "onboarding_user_welcome_description": "لنساعدك على البدء!", "onboarding_welcome_user": "مرحبا، {user}", "online": "متصل", "only_favorites": "المفضلة فقط", + "open": "فتح", "open_in_map_view": "فتح في عرض الخريطة", "open_in_openstreetmap": "فتح في OpenStreetMap", "open_the_search_filters": "افتح مرشحات البحث", @@ -1146,12 +1368,14 @@ "partner_can_access": "يستطيع {partner} الوصول", "partner_can_access_assets": "جميع الصور ومقاطع الفيديو الخاصة بك باستثناء تلك الموجودة في المؤرشفة والمحذوفة", "partner_can_access_location": "الموقع الذي تم التقاط صورك فيه", + "partner_list_user_photos": "صور {user}", "partner_list_view_all": "عرض الكل", "partner_page_empty_message": "لم يتم مشاركة صورك بعد مع أي شريك.", "partner_page_no_more_users": "لا مزيد من المستخدمين لإضافة", "partner_page_partner_add_failed": "فشل في إضافة شريك", "partner_page_select_partner": "حدد شريكًا", "partner_page_shared_to_title": "مشترك ل", + "partner_page_stop_sharing_content": "{partner} لن يعود قادرا على الوصوف الى صورك.", "partner_sharing": "مشاركة الشركاء", "partners": "الشركاء", "password": "كلمة المرور", @@ -1180,14 +1404,16 @@ "permanently_delete_assets_prompt": "هل أنت متأكد أنك تريد حذف {count, plural, one {هذا العنصر؟} other {هذه العناصر #؟}} سيتم أيضًا إزالته {count, plural, one {من ألبومه} other {من ألبوماتهم}}.", "permanently_deleted_asset": "تم حذف الأصل بشكل نهائي", "permanently_deleted_assets_count": "تم حذف {count, plural, one {# محتوى} other {# المحتويات}} نهائيًا", + "permission": "اذن", + "permission_empty": "الاذن الخاص بك يجب ان لا يكون فارغا", "permission_onboarding_back": "خلف", "permission_onboarding_continue_anyway": "تواصل على أي حال", "permission_onboarding_get_started": "البدء", "permission_onboarding_go_to_settings": "اذهب للاعدادات", - "permission_onboarding_permission_denied": "تم رفض الإذن. لاستخدام التطبيق، قم بمنح أذونات الصور والفيديو في الإعدادات ", + "permission_onboarding_permission_denied": "تم رفض الإذن. لاستخدام التطبيق، قم بمنح أذونات الصور والفيديو في الإعدادات.", "permission_onboarding_permission_granted": "تم تأمين التصريح! وضعك تمام.", "permission_onboarding_permission_limited": "إذن محدود. للسماح بالنسخ الاحتياطي للتطبيق وإدارة مجموعة المعرض بالكامل، امنح أذونات الصور والفيديو في الإعدادات.", - "permission_onboarding_request": "يتطلب التطبيق إذنًا لعرض الصور ومقاطع الفيديو الخاصة بك", + "permission_onboarding_request": "يتطلب التطبيق إذنًا لعرض الصور ومقاطع الفيديو الخاصة بك.", "person": "شخص", "person_birthdate": "تاريخ الميلاد {التاريخ}", "person_hidden": "{name}{hidden, select, true { (مخفي)} other {}}", @@ -1197,9 +1423,10 @@ "photos_count": "{count, plural, one {{count, number} صورة} other {{count, number} صور}}", "photos_from_previous_years": "صور من السنوات السابقة", "pick_a_location": "اختر موقعًا", - "pin_code_changed_successfully": "تم تغير الرقم السري", - "pin_code_reset_successfully": "تم اعادة تعيين الرقم السري", - "pin_code_setup_successfully": "تم انشاء رقم سري", + "pin_code_changed_successfully": "تم تغير رمز PIN بنجاح", + "pin_code_reset_successfully": "تم اعادة تعيين رمز PIN بنجاح", + "pin_code_setup_successfully": "تم انشاء رمز PIN بنجاح", + "pin_verification": "التحقق برمز PIN", "place": "مكان", "places": "الأماكن", "places_count": "{count, plural, one {{count, number} مكان} other {{count, number} أماكن}}", @@ -1207,15 +1434,21 @@ "play_memories": "تشغيل الذكريات", "play_motion_photo": "تشغيل الصور المتحركة", "play_or_pause_video": "تشغيل الفيديو أو إيقافه مؤقتًا", + "please_auth_to_access": "الرجاء القيام بالمصادقة للوصول", "port": "المنفذ", + "preferences_settings_subtitle": "ادارة تفضيلات التطبيق", "preferences_settings_title": "التفضيلات", "preset": "الإعداد المسبق", "preview": "معاينة", "previous": "السابق", "previous_memory": "الذكرى السابقة", - "previous_or_next_photo": "الصورة السابقة أو التالية", + "previous_or_next_day": "يوم تالي/سابق", + "previous_or_next_month": "شهر تالي/سابق", + "previous_or_next_photo": "صورة تالية/سابقة", + "previous_or_next_year": "سنة تالية/سابقة", "primary": "أساسي", "privacy": "الخصوصية", + "profile": "حساب تعريفي", "profile_drawer_app_logs": "السجلات", "profile_drawer_client_out_of_date_major": "تطبيق الهاتف المحمول قديم.يرجى التحديث إلى أحدث إصدار رئيسي.", "profile_drawer_client_out_of_date_minor": "تطبيق الهاتف المحمول قديم.يرجى التحديث إلى أحدث إصدار صغير.", @@ -1247,7 +1480,7 @@ "purchase_lifetime_description": "الشراء لمدى الحياة", "purchase_option_title": "خيارات الشراء", "purchase_panel_info_1": "يتطلب بناء Immich الكثير من الوقت والجهد، ولدينا مهندسون يعملون بدوام كامل لجعله أفضل ما يمكن. مهمتنا هي أن تصبح البرمجيات مفتوحة المصدر وممارسات العمل الأخلاقية مصدر دخل مستدام للمطورين وإنشاء نظام بيئي يحترم الخصوصية مع بدائل حقيقية للخدمات السحابية الاستغلالية.", - "purchase_panel_info_2": "نظرًا لأننا ملتزمون بعدم إضافة نظام حظر الاشتراك غير المدفوع، فإن هذا الشراء لن يمنحك أي ميزات إضافية في Immich. نحن نعتمد على المستخدمين مثلك لدعم التطوير المستمر لـ Immich.", + "purchase_panel_info_2": "نظرًا لأننا ملتزمون بعدم إضافة حاجز دفع، فإن هذا الشراء لن يمنحك أي ميزات إضافية في Immich. نحن نعتمد على المستخدمين مثلك لدعم التطوير المستمر لـ Immich.", "purchase_panel_title": "ادعم المشروع", "purchase_per_server": "لكل خادم", "purchase_per_user": "لكل مستخدم", @@ -1272,13 +1505,16 @@ "recent": "حديث", "recent-albums": "ألبومات الحديثة", "recent_searches": "عمليات البحث الأخيرة", + "recently_added": "اضيف مؤخرا", "recently_added_page_title": "أضيف مؤخرا", + "recently_taken": "تم التقاطها مؤخرًا", + "recently_taken_page_title": "تم التقاطها مؤخرًا", "refresh": "تحديث", "refresh_encoded_videos": "تحديث مقاطع الفيديو المشفرة", "refresh_faces": "تحديث الوجوه", "refresh_metadata": "تحديث البيانات الوصفية", "refresh_thumbnails": "تحديث الصور المصغرة", - "refreshed": "تم التحديث", + "refreshed": "اعادة تحميل", "refreshes_every_file": "إعادة قراءة كافة الملفات الموجودة والجديدة", "refreshing_encoded_video": "جارٍ تحديث الفيديو المرمز", "refreshing_faces": "جاري تحديث الوجوه", @@ -1292,12 +1528,16 @@ "remove_deleted_assets": "إزالة الملفات الغير متصلة", "remove_from_album": "إزالة من الألبوم", "remove_from_favorites": "إزالة من المفضلة", + "remove_from_lock_folder_action_prompt": "{count} أويل من المجلد المقفل", + "remove_from_locked_folder": "ازالة من المجلد المقفل", + "remove_from_locked_folder_confirmation": "هل انت متأكد من ازالة هذه الصور والفيديوهات من المجلد المقفل؟ سيكونون مرئيين في المكتبة الخاصة بك.", "remove_from_shared_link": "إزالة من الرابط المشترك", "remove_memory": "إزالة الذاكرة", "remove_photo_from_memory": "إزالة الصورة من هذه الذكرى", + "remove_tag": "ازالة علامة", "remove_url": "إزالة عنوان URL", "remove_user": "إزالة المستخدم", - "removed_api_key": "تم إزالة مفتاح API: {name}", + "removed_api_key": "تم إزاة مفتاح API: ‪‫{name}", "removed_from_archive": "تمت إزالتها من الأرشيف", "removed_from_favorites": "تمت الإزالة من المفضلة", "removed_from_favorites_count": "{count, plural, other {أُزيلت #}} من التفضيلات", @@ -1315,6 +1555,7 @@ "reset": "إعادة ضبط", "reset_password": "إعادة تعيين كلمة المرور", "reset_people_visibility": "إعادة ضبط ظهور الأشخاص", + "reset_pin_code": "اعادة تعيين رمز PIN", "reset_to_default": "إعادة التعيين إلى الافتراضي", "resolve_duplicates": "معالجة النسخ المكررة", "resolved_all_duplicates": "تم حل جميع التكرارات", @@ -1329,6 +1570,7 @@ "role_editor": "المحرر", "role_viewer": "العارض", "save": "حفظ", + "save_to_gallery": "حفظ الى المعرض", "saved_api_key": "تم حفظ مفتاح الـ API", "saved_profile": "تم حفظ الملف", "saved_settings": "تم حفظ الإعدادات", @@ -1349,19 +1591,33 @@ "search_camera_model": "البحث حسب موديل الكاميرا...", "search_city": "البحث حسب المدينة...", "search_country": "البحث حسب الدولة...", - "search_filter_apply": "اختار الفلتر ", + "search_filter_apply": "اختار الفلتر", + "search_filter_camera_title": "اختر نوع الكاميرا", + "search_filter_date": "تاريخ", + "search_filter_date_interval": "{start} الى {end}", + "search_filter_date_title": "حدد نطاق التاريخ", "search_filter_display_option_not_in_album": "ليس في الألبوم", + "search_filter_display_options": "خيارات العرض", + "search_filter_filename": "بحث باستخدام الاسم", + "search_filter_location": "الموقع", + "search_filter_location_title": "اختر الموقع", + "search_filter_media_type": "نوع الوسائط", + "search_filter_media_type_title": "اختر نوع الوسائط", + "search_filter_people_title": "اختر الاشخاص", "search_for": "البحث عن", "search_for_existing_person": "البحث عن شخص موجود", + "search_no_more_result": "لا توجد نتائج اضافية", "search_no_people": "لا يوجد أشخاص", "search_no_people_named": "لا يوجد أشخاص بالاسم \"{name}\"", + "search_no_result": "لا توجد نتائج، حاول استخدام مصطلح بحث مختلف او تركيبة", "search_options": "خيارات البحث", "search_page_categories": "فئات", "search_page_motion_photos": "الصور المتحركه", "search_page_no_objects": "لا توجد معلومات عن أشياء متاحة", "search_page_no_places": "لا توجد معلومات متوفرة للأماكن", "search_page_screenshots": "لقطات الشاشة", - "search_page_selfies": " صور ذاتيه", + "search_page_search_photos_videos": "ابحث عن صورك او فديوهاتك", + "search_page_selfies": "صور ذاتيه", "search_page_things": "أشياء", "search_page_view_all_button": "عرض الكل", "search_page_your_activity": "نشاطك", @@ -1372,7 +1628,7 @@ "search_result_page_new_search_hint": "بحث جديد", "search_settings": "إعدادات البحث", "search_state": "البحث حسب الولاية...", - "search_suggestion_list_smart_search_hint_1": "يتم تمكين البحث الذكي افتراضيًا ، للبحث عن البيانات الوصفية ، استخدم بناء الجملة", + "search_suggestion_list_smart_search_hint_1": "يتم تمكين البحث الذكي افتراضيًا ، للبحث عن البيانات الوصفية ، استخدم بناء الجملة. ", "search_suggestion_list_smart_search_hint_2": "م: البحث الخاص بك", "search_tags": "البحث عن العلامات...", "search_timezone": "البحث حسب المنطقة الزمنية...", @@ -1385,6 +1641,7 @@ "select_album_cover": "تحديد غلاف الألبوم", "select_all": "تحديد الكل", "select_all_duplicates": "تحديد جميع النسخ المكررة", + "select_all_in": "اختر الكل في {group}", "select_avatar_color": "تحديد لون الصورة الشخصية", "select_face": "تحديد وجه", "select_featured_photo": "تحديد الصورة المميزة", @@ -1392,6 +1649,7 @@ "select_keep_all": "تحديد الأحتفاظ بالكل", "select_library_owner": "تحديد مالِك المكتبة", "select_new_face": "تحديد وجه جديد", + "select_person_to_tag": "اختر شخص لوضع علامة", "select_photos": "تحديد الصور", "select_trash_all": "تحديد حذف الكلِ", "select_user_for_sharing_page_err_album": "فشل في إنشاء ألبوم", @@ -1399,10 +1657,12 @@ "selected_count": "{count, plural, other {# محددة }}", "send_message": "‏إرسال رسالة", "send_welcome_email": "إرسال بريدًا إلكترونيًا ترحيبيًا", + "server_endpoint": "نقطة نهاية الخادم", "server_info_box_app_version": "نسخة التطبيق", "server_info_box_server_url": "عنوان URL الخادم", "server_offline": "الخادم غير متصل", "server_online": "الخادم متصل", + "server_privacy": "خصوصية الخادم", "server_stats": "إحصائيات الخادم", "server_version": "إصدار الخادم", "set": "‏تحديد", @@ -1412,6 +1672,7 @@ "set_date_of_birth": "تحديد تاريخ الميلاد", "set_profile_picture": "تحديد صورة الملف الشخصي", "set_slideshow_to_fullscreen": "تحديد عرض الشرائح على وضع ملء الشاشة", + "set_stack_primary_asset": "تعيين كأصل اساسي", "setting_image_viewer_help": "يقوم عارض التفاصيل بتحميل الصورة المصغرة الصغيرة أولاً ، ثم يقوم بتحميل المعاينة متوسطة الحجم (إذا تم تمكينها) ، ويقوم أخيرًا بتحميل الأصل (إذا تم تمكينه).", "setting_image_viewer_original_subtitle": "تمكين تحميل الصورة الكاملة الدقة الأصلية (كبيرة!).تعطيل لتقليل استخدام البيانات (كل من الشبكة وعلى ذاكرة التخزين المؤقت للجهاز).", "setting_image_viewer_original_title": "تحميل الصورة الأصلية", @@ -1419,21 +1680,30 @@ "setting_image_viewer_preview_title": "تحميل صورة معاينة", "setting_image_viewer_title": "الصور", "setting_languages_apply": "تغيير الإعدادات", + "setting_languages_subtitle": "تغيير لغة التطبيق", + "setting_notifications_notify_failures_grace_period": "التنبيه بفشل النسخ الاحتياطي في الخلفية: {duration}", + "setting_notifications_notify_hours": "{count} ساعات", "setting_notifications_notify_immediately": "في الحال", + "setting_notifications_notify_minutes": "{count} دقائق", "setting_notifications_notify_never": "أبداً", + "setting_notifications_notify_seconds": "{count} ثواني", "setting_notifications_single_progress_subtitle": "معلومات التقدم التفصيلية تحميل لكل أصل", "setting_notifications_single_progress_title": "إظهار تقدم التفاصيل الاحتياطية الخلفية", "setting_notifications_subtitle": "اضبط تفضيلات الإخطار", "setting_notifications_total_progress_subtitle": "التقدم التحميل العام (تم القيام به/إجمالي الأصول)", "setting_notifications_total_progress_title": "إظهار النسخ الاحتياطي الخلفية التقدم المحرز", "setting_video_viewer_looping_title": "تكرار مقطع فيديو تلقائيًا", + "setting_video_viewer_original_video_subtitle": "عند بث فيديو من الخادم، شغّل النسخة الأصلية حتى مع توفر ترميز بديل. قد يؤدي ذلك إلى تقطيع اثناء العرض . تُشغّل الفيديوهات المتوفرة محليًا بجودة أصلية بغض النظر عن هذا الإعداد.", + "setting_video_viewer_original_video_title": "اجبار عرض الفديو الاصلي", "settings": "الإعدادات", "settings_require_restart": "يرجى إعادة تشغيل لتطبيق هذا الإعداد", "settings_saved": "تم حفظ الإعدادات", - "setup_pin_code": "تحديد رقم سري", + "setup_pin_code": "تحديد رمز PIN", "share": "مشاركة", "share_add_photos": "إضافة الصور", + "share_assets_selected": "اختيار {count}", "share_dialog_preparing": "تحضير...", + "share_link": "مشاركة رابط", "shared": "مُشتَرك", "shared_album_activities_input_disable": "التعليق معطل", "shared_album_activity_remove_content": "هل تريد حذف هذا النشاط؟", @@ -1446,22 +1716,40 @@ "shared_by_user": "تمت المشاركة بواسطة {user}", "shared_by_you": "تمت مشاركته من قِبلك", "shared_from_partner": "صور من {partner}", + "shared_intent_upload_button_progress_text": "{current} / {total} تم رفع", "shared_link_app_bar_title": "روابط مشتركة", "shared_link_clipboard_copied_massage": "نسخ إلى الحافظة", + "shared_link_clipboard_text": "رابط: {link}\nكلمة المرور: {password}", "shared_link_create_error": "خطأ أثناء إنشاء رابط مشترك", "shared_link_edit_description_hint": "أدخل وصف المشاركة", "shared_link_edit_expire_after_option_day": "يوم 1", + "shared_link_edit_expire_after_option_days": "{count} ايام", "shared_link_edit_expire_after_option_hour": "1 ساعة", + "shared_link_edit_expire_after_option_hours": "{count} ساعات", "shared_link_edit_expire_after_option_minute": "1 دقيقة", + "shared_link_edit_expire_after_option_minutes": "{count} دقائق", + "shared_link_edit_expire_after_option_months": "{count} اشهر", + "shared_link_edit_expire_after_option_year": "{count} سنة", "shared_link_edit_password_hint": "أدخل كلمة مرور المشاركة", "shared_link_edit_submit_button": "تحديث الرابط", "shared_link_error_server_url_fetch": "لا يمكن جلب عنوان الخادم", + "shared_link_expires_day": "تنتهي صلاحيته في {count} يوم", + "shared_link_expires_days": "تنتهي صلاحيته في {count} ايام", + "shared_link_expires_hour": "تنتهي صلاحية في {count} ساعة", + "shared_link_expires_hours": "تنتهي صلاحيته في {count} ساعات", + "shared_link_expires_minute": "تنتهي صلاحيته في {count} دقيقة", + "shared_link_expires_minutes": "تنتهي صلاحيته في {count} دقائق", "shared_link_expires_never": "تنتهي ∞", + "shared_link_expires_second": "تنتهي صلاحيته في {count} ثانية", + "shared_link_expires_seconds": "تنتهي صلاحيته في {count} ثواني", + "shared_link_individual_shared": "مشاركة فردية", + "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "إدارة الروابط المشتركة", "shared_link_options": "خيارات الرابط المشترك", "shared_links": "روابط مشتركة", "shared_links_description": "وصف الروابط المشتركة", "shared_photos_and_videos_count": "{assetCount, plural, other {# الصور ومقاطع الفيديو المُشارَكة.}}", + "shared_with_me": "تمت مشاركتها معي", "shared_with_partner": "تمت المشاركة مع {partner}", "sharing": "مشاركة", "sharing_enter_password": "الرجاء إدخال كلمة المرور لعرض هذه الصفحة.", @@ -1522,12 +1810,14 @@ "start_date": "تاريخ البدء", "state": "الولاية", "status": "الحالة", + "stop_casting": "ايقاف البث", "stop_motion_photo": "إيقاف حركة الصورة", "stop_photo_sharing": "توقف عن مشاركة صورك؟", "stop_photo_sharing_description": "لن يتمكن {partner} من الوصول إلى صورك بعد الآن.", "stop_sharing_photos_with_user": "توقف عن مشاركة صورك مع هذا المستخدم", "storage": "مساحة التخزين", - "storage_label": "تسمية التخزين", + "storage_label": "سمة التخزين", + "storage_quota": "حصة الخزن", "storage_usage": "{used} من {available} مُستخْدم", "submit": "إرسال", "suggestions": "اقتراحات", @@ -1537,6 +1827,9 @@ "support_third_party_description": "تم حزم تثبيت immich الخاص بك بواسطة جهة خارجية. قد تكون المشكلات التي تواجهها ناجمة عن هذه الحزمة، لذا يرجى طرح المشكلات معهم في المقام الأول باستخدام الروابط أدناه.", "swap_merge_direction": "تبديل اتجاه الدمج", "sync": "مزامنة", + "sync_albums": "مزامنة الالبومات", + "sync_albums_manual_subtitle": "مزامنة جميع الفديوهات والصور المرفوعة الى البومات الخزن الاحتياطي المختارة", + "sync_upload_album_setting_subtitle": "انشئ و ارفع صورك و فديوهاتك الالبومات المختارة في Immich", "tag": "العلامة", "tag_assets": "أصول العلامة", "tag_created": "تم إنشاء العلامة: {tag}", @@ -1551,8 +1844,14 @@ "theme_selection": "اختيار السمة", "theme_selection_description": "قم بتعيين السمة تلقائيًا على اللون الفاتح أو الداكن بناءً على تفضيلات نظام المتصفح الخاص بك", "theme_setting_asset_list_storage_indicator_title": "عرض مؤشر التخزين على بلاط الأصول", + "theme_setting_asset_list_tiles_per_row_title": "عدد الاصول في كل صف({count})", + "theme_setting_colorful_interface_subtitle": "تطبيق اللون الأساسي على الأسطح في الخلفية.", + "theme_setting_colorful_interface_title": "واجهه ملونة", "theme_setting_image_viewer_quality_subtitle": "اضبط جودة عارض الصورة التفصيلية", "theme_setting_image_viewer_quality_title": "جودة عارض الصورة", + "theme_setting_primary_color_subtitle": "اختر لون للعمليات الاساسية والمكمله.", + "theme_setting_primary_color_title": "اللون الاساسي", + "theme_setting_system_primary_color_title": "استخدم لون النظام", "theme_setting_system_theme_switch": "تلقائي (اتبع إعداد النظام)", "theme_setting_theme_subtitle": "اختر إعدادات مظهر التطبيق", "theme_setting_three_stage_loading_subtitle": "قد يزيد التحميل من ثلاث مراحل من أداء التحميل ولكنه يسبب تحميل شبكة أعلى بكثير", @@ -1572,22 +1871,29 @@ "total": "الإجمالي", "total_usage": "الاستخدام الإجمالي", "trash": "المهملات", + "trash_action_prompt": "{count} نقل الى سلة المهملات", "trash_all": "نقل الكل إلى سلة المهملات", "trash_count": "سلة المحملات {count, number}", "trash_delete_asset": "حذف/نقل المحتوى إلى سلة المهملات", + "trash_emptied": "سبة مهملا مفرغة", "trash_no_results_message": "ستظهر هنا الصور ومقاطع الفيديو المحذوفة.", "trash_page_delete_all": "حذف الكل", "trash_page_empty_trash_dialog_content": "هل تريد تفريغ أصولك المهملة؟ ستتم إزالة هذه العناصر نهائيًا من التطبيق", + "trash_page_info": "العناصر المنقولة الى سلة المهملات سيتم حذفها بشكل نهائي بعد {days} ايام", "trash_page_no_assets": "لا توجد اصول في سله المهملات", "trash_page_restore_all": "استعادة الكل", - "trash_page_select_assets_btn": "اختر الأصول ", + "trash_page_select_assets_btn": "اختر الأصول", + "trash_page_title": "سلة المهملات ({count})", "trashed_items_will_be_permanently_deleted_after": "سيتم حذفُ العناصر المحذوفة نِهائيًا بعد {days, plural, one {# يوم} other {# أيام }}.", "type": "النوع", - "unable_to_change_pin_code": "تفيير الرقم السري غير ممكن", - "unable_to_setup_pin_code": "انشاء الرقم السري غير ممكن", + "unable_to_change_pin_code": "تفيير رمز PIN غير ممكن", + "unable_to_setup_pin_code": "انشاء رمز PIN غير ممكن", "unarchive": "أخرج من الأرشيف", + "unarchive_action_prompt": "{count} ازيل من الارشيف", "unarchived_count": "{count, plural, other {غير مؤرشفة #}}", + "undo": "تراجع", "unfavorite": "أزل التفضيل", + "unfavorite_action_prompt": "{count} ازيل من المفضلات", "unhide_person": "أظهر الشخص", "unknown": "غير معروف", "unknown_country": "بلد غير معروف", @@ -1603,9 +1909,11 @@ "unsaved_change": "تغيير غير محفوظ", "unselect_all": "إلغاء تحديد الكل", "unselect_all_duplicates": "إلغاء تحديد كافة النسخ المكررة", + "unselect_all_in": "إلغاء تحديد الكل في {group}", "unstack": "فك الكومه", "unstacked_assets_count": "تم إخراج {count, plural, one {# الأصل} other {# الأصول}} من التكديس", "up_next": "التالي", + "updated_at": "تم التحديث", "updated_password": "تم تحديث كلمة المرور", "upload": "رفع", "upload_concurrency": "الرفع المتزامن", @@ -1618,14 +1926,20 @@ "upload_status_errors": "الأخطاء", "upload_status_uploaded": "تم الرفع", "upload_success": "تم الرفع بنجاح، قم بتحديث الصفحة لرؤية المحتويات المرفوعة الجديدة.", + "upload_to_immich": "الرفع الىImmich ‎ ‏ ({count})", + "uploading": "جاري الرفع", "url": "عنوان URL", "usage": "الاستخدام", + "use_biometric": "استخدم البايومتري", + "use_current_connection": "استخدم الاتصال الحالي", "use_custom_date_range": "استخدم النطاق الزمني المخصص بدلاً من ذلك", "user": "مستخدم", + "user_has_been_deleted": "هذا المستخدم تم حذفه.", "user_id": "معرف المستخدم", "user_liked": "قام {user} بالإعجاب {type, select, photo {بهذه الصورة} video {بهذا الفيديو} asset {بهذا المحتوى} other {بها}}", - "user_pin_code_settings": "الرقم السري", - "user_pin_code_settings_description": "تغير الرقم السري", + "user_pin_code_settings": "رمز PIN", + "user_pin_code_settings_description": "تغير رمز PIN", + "user_privacy": "خصوصية المستخدم", "user_purchase_settings": "الشراء", "user_purchase_settings_description": "إدارة عملية الشراء الخاصة بك", "user_role_set": "قم بتعيين {user} كـ {role}", @@ -1636,6 +1950,7 @@ "users": "المستخدمين", "utilities": "أدوات", "validate": "تحقْق", + "validate_endpoint_error": "الرجاء ادخال عنوان URL صالح", "variables": "المتغيرات", "version": "الإصدار", "version_announcement_closing": "صديقك، أليكس", @@ -1657,7 +1972,9 @@ "view_name": "عرض", "view_next_asset": "عرض المحتوى التالي", "view_previous_asset": "عرض المحتوى السابق", + "view_qr_code": "­عرض رمز الاستجابة السريعة", "view_stack": "عرض التكديس", + "view_user": "عرض المستخدم", "viewer_remove_from_stack": "حذف من الكومه أو المجموعة", "viewer_stack_use_as_main_asset": "استخدم كأصل رئيسي", "viewer_unstack": "فك الكومه", @@ -1667,11 +1984,12 @@ "week": "أسبوع", "welcome": "مرحباً", "welcome_to_immich": "مرحباً بك في Immich", - "wifi_name": "WiFi Name", + "wifi_name": "اسم شبكة Wi-Fi", + "wrong_pin_code": "رمز PIN خاطئ", "year": "سنة", "years_ago": "منذ {years, plural, one {# سنة} other {# سنوات}}", "yes": "نعم", "you_dont_have_any_shared_links": "ليس لديك أي روابط مشتركة", - "your_wifi_name": "Your WiFi name", + "your_wifi_name": "اسم شبكة Wi-Fi الخاص بك", "zoom_image": "تكبير الصورة" } diff --git a/i18n/be.json b/i18n/be.json index 470030b6f6..669ec09849 100644 --- a/i18n/be.json +++ b/i18n/be.json @@ -6,7 +6,7 @@ "action": "Дзеянне", "action_common_update": "Абнавіць", "actions": "Дзеянні", - "active": "Актыўны", + "active": "Актыўных", "activity": "Актыўнасць", "activity_changed": "Актыўнасць {enabled, select, true {уключана} other {адключана}}", "add": "Дадаць", @@ -22,6 +22,7 @@ "add_partner": "Дадаць партнёра", "add_path": "Дадаць шлях", "add_photos": "Дадаць фота", + "add_tag": "Дадаць тэг", "add_to": "Дадаць у…", "add_to_album": "Дадаць у альбом", "add_to_album_bottom_sheet_added": "Дададзена да {album}", @@ -33,28 +34,30 @@ "added_to_favorites_count": "Дададзена {count, number} да абранага", "admin": { "add_exclusion_pattern_description": "Дадайце шаблоны выключэнняў. Падтрымліваецца выкарыстанне сімвалаў * , ** і ?. Каб ігнараваць усе файлы ў любой дырэкторыі з назвай \"Raw\", выкарыстоўвайце \"**/Raw/**\". Каб ігнараваць усе файлы, якія заканчваюцца на \".tif\", выкарыстоўвайце \"**/.tif\". Каб ігнараваць абсолютны шлях, выкарыстоўвайце \"/path/to/ignore/**\".", + "admin_user": "Адміністратар", "asset_offline_description": "Гэты знешні бібліятэчны актыў больш не знойдзены на дыску і быў перамешчаны ў сметніцу. Калі файл быў перамешчаны ў межах бібліятэкі, праверце вашу хроніку для новага адпаведнага актыва. Каб аднавіць гэты актыў, пераканайцеся, што шлях да файла ніжэй даступны для Immich і адскануйце бібліятэку.", "authentication_settings": "Налады праверкі сапраўднасці", "authentication_settings_description": "Кіраванне паролямі, OAuth, і іншыя налады праверкі сапраўднасці", "authentication_settings_disable_all": "Вы ўпэўнены, што жадаеце адключыць усе спосабы логіну? Логін будзе цалкам адключаны.", "authentication_settings_reenable": "Каб зноў уключыць, выкарыстайце Каманду сервера.", "background_task_job": "Фонавыя заданні", - "backup_database": "Рэзервовая копія базы даных", + "backup_database": "Стварыць рэзервовую копію базы даных", "backup_database_enable_description": "Уключыць рэзерваванне базы даных", "backup_keep_last_amount": "Колькасць папярэдніх рэзервовых копій для захавання", "backup_settings": "Налады рэзервовага капіявання", - "backup_settings_description": "Кіраванне наладамі дампа базы дадзеных. Заўвага: гэтыя задачы не кантралююцца, і ў выпадку няўдачы паведамленне адпраўлена не будзе.", + "backup_settings_description": "Кіраванне наладамі рэзервавання базы даных.", "cleared_jobs": "Ачышчаны заданні для: {job}", - "config_set_by_file": "Канфігурацыя ў зараз усталявана праз файл канфігурацыі", - "confirm_delete_library": "Вы ўпэўнены што жадаеце выдаліць {library} бібліятэку?", + "config_set_by_file": "Канфігурацыя зараз усталявана праз файл канфігурацыі", + "confirm_delete_library": "Вы ўпэўнены што жадаеце выдаліць бібліятэку {library}?", "confirm_delete_library_assets": "Вы ўпэўнены, што хочаце выдаліць гэтую бібліятэку? Гэта прывядзе да выдалення {count, plural, one {# актыву} other {усіх # актываў}}, якія змяшчаюцца ў Immich, і гэта дзеянне немагчыма будзе адмяніць. Файлы застануцца на дыску.", "confirm_email_below": "Каб пацвердзіць, увядзіце \"{email}\" ніжэй", "confirm_reprocess_all_faces": "Вы ўпэўнены, што хочаце пераапрацаваць усе твары? Гэта таксама прывядзе да выдалення імя людзей.", "confirm_user_password_reset": "Вы ўпэўнены ў тым, што жадаеце скінуць пароль {user}?", + "confirm_user_pin_code_reset": "Вы ўпэўнены ў тым, што жадаеце скінуць PIN-код {user}?", "create_job": "Стварыць заданне", "cron_expression": "Выраз Cron", "cron_expression_description": "Усталюйце інтэрвал сканавання, выкарыстоўваючы фармат cron. Для атрымання дадатковай інфармацыі, калі ласка, звярніцеся, напрыклад, да Crontab Guru", - "cron_expression_presets": "Прадустановкі выразаў Cron", + "cron_expression_presets": "Прадустаноўкі выразаў Cron", "disable_login": "Адключыць уваход", "duplicate_detection_job_description": "Запусціць машыннае навучанне на актывах для выяўлення падобных выяў. Залежыць ад Smart Search", "exclusion_pattern_description": "Шаблоны выключэння дазваляюць ігнараваць файлы і папкі пры сканаванні вашай бібліятэкі. Гэта карысна, калі ў вас ёсць папкі, якія змяшчаюць файлы, якія вы не хочаце імпартаваць, напрыклад, файлы RAW.", @@ -71,15 +74,361 @@ "image_fullsize_enabled_description": "Ствараць выяву ў поўным памеры для фарматаў, што не прыдатныя для вэб. Калі ўключана опцыя \"Аддаваць перавагу ўбудаванай праяве\", прагляды выкарыстоўваюцца непасрэдна без канвертацыі. Не ўплывае на вэб-прыдатныя фарматы, такія як JPEG.", "image_fullsize_quality_description": "Якасць выявы ў поўным памеры ад 1 да 100. Больш высокае значэнне лепшае, але прыводзіць да павелічэння памеру файла.", "image_fullsize_title": "Налады выявы ў поўным памеры", + "image_prefer_embedded_preview": "Аддаваць перавагу ўбудаванай праяве", + "image_prefer_embedded_preview_setting_description": "Выкарыстоўваць убудаваныя праявы ў RAW-фотаздымках ў якасці ўваходных дадзеных для апрацоўкі малюнкаў, калі магчыма. Гэта дазваляе атрымаць больш дакладныя колеры для некаторых відарысаў, але ж якасць праяў залежыць ад камеры, і на відарысе можа быць больш артэфактаў сціску.", + "image_prefer_wide_gamut": "Аддаць перавагу шырокай гаме", + "image_preview_description": "Відарыс сярэдняга памеру з выдаленымі метададзенымі, выкарыстоўваецца пры праглядзе асобнага рэсурсу і для машыннага навучання", + "image_preview_quality_description": "Якасць праявы ад 1 да 100. Чым вышэй, тым лепш, але пры гэтым ствараюцца файлы большага памеру і можа знізіцца хуткасць водгуку прыкладання. Ўстаноўка нізкага значэння можа паўплываць на якасць машыннага навучання.", "image_preview_title": "Налады папярэдняга прагляду", "image_quality": "Якасць", "image_resolution": "Раздзяляльнасць", "image_settings": "Налады відарыса", - "image_settings_description": "Кіруйце якасцю і раздзяляльнасцю сгенерыраваных відарысаў" + "image_settings_description": "Кіруйце якасцю і раздзяляльнасцю сгенерыраваных відарысаў", + "library_created": "Створана бібліятэка: {library}", + "library_deleted": "Бібліятэка выдалена", + "map_dark_style": "Цёмны стыль", + "map_enable_description": "Уключыць функцыі карты", + "map_gps_settings": "Налады карты і GPS", + "map_light_style": "Светлы стыль", + "map_settings": "Карта", + "map_settings_description": "Кіраванне наладамі карты", + "map_style_description": "URL-адрас style.json тэмы карты", + "metadata_settings": "Налады метаданых", + "oauth_button_text": "Тэкст кнопкі", + "oauth_settings": "OAuth", + "refreshing_all_libraries": "Абнаўленне ўсіх бібліятэк", + "registration": "Рэгістрацыя адміністратара", + "registration_description": "Вы з'яўляецеся першым карыстальнікам сістэмы, таму вы будзеце прызначаны адміністратарам. Вы будзеце адказваць за адміністрацыйныя задачы, а таксама ствараць новых карыстальнікаў.", + "require_password_change_on_login": "Патрабаваць змяніць пароль пры першым уваходзе ў сістэму", + "reset_settings_to_default": "Скінуць налады да прадвызначаных", + "reset_settings_to_recent_saved": "Скінуць налады да нядаўна захаваных", + "scanning_library": "Сканіраванне бібліятэкі", + "server_external_domain_settings": "Знешні дамен", + "server_settings": "Налады сервера", + "server_settings_description": "Кіраванне наладамі сервера", + "server_welcome_message": "Прывітальнае паведамленне", + "server_welcome_message_description": "Паведамленне, якое адлюстроўваецца на старонцы ўваходу.", + "system_settings": "Сістэмныя налады", + "tag_cleanup_job": "Ачыстка тэгаў", + "template_email_preview": "Перадпрагляд", + "theme_settings": "Налады тэмы", + "transcoding_acceleration_nvenc": "NVENC (патрабуецца відэакарта NVIDIA)", + "transcoding_acceleration_vaapi": "VAAPI", + "transcoding_accepted_containers": "Прынятыя кантэйнеры", + "transcoding_accepted_video_codecs": "Прынятыя відэакодэкі", + "transcoding_advanced_options_description": "Параметры, якія большасці карыстальнікаў не трэба змяняць", + "transcoding_audio_codec": "Аудыякодэк", + "transcoding_encoding_options": "Параметры кадзіравання", + "transcoding_video_codec": "Відэакодэк", + "trash_enabled_description": "Уключыць функцыі сметніцы", + "trash_number_of_days": "Колькасць дзён", + "trash_settings": "Налады сметніцы", + "trash_settings_description": "Кіраванне наладамі сметніцы", + "user_cleanup_job": "Ачыстка карыстальніка", + "user_management": "Кіраванне карыстальнікамі", + "user_password_has_been_reset": "Пароль карыстальніка быў скінуты:", + "user_password_reset_description": "Задайце карыстальніку часовы пароль і паведаміце яму, што пры наступным уваходзе ў сістэму яму трэба будзе змяніць пароль.", + "user_restore_description": "Уліковы запіс карыстальніка {user} будзе адноўлены.", + "user_settings": "Налады карыстальніка", + "user_settings_description": "Кіраванне наладамі карыстальніка", + "user_successfully_removed": "Карыстальнік {email} быў паспяхова выдалены.", + "version_check_enabled_description": "Уключыць праверку версіі", + "version_check_implications": "Функцыі праверкі версіі перыядычна звяртаецца да github.com", + "version_check_settings": "Праверка версіі", + "version_check_settings_description": "Уключыць/адключыць апавяшчэнні аб новай версіі" }, + "admin_email": "Электронная пошта адміністратара", + "admin_password": "Пароль адміністратара", + "administration": "Кіраванне серверам", + "advanced": "Пашыраныя", + "advanced_settings_log_level_title": "Узровень вядзення журнала: {level}", + "advanced_settings_proxy_headers_title": "Загалоўкі проксі", + "advanced_settings_tile_subtitle": "Пашыраныя налады карыстальніка", + "advanced_settings_troubleshooting_subtitle": "Уключыць дадатковыя функцыі для выпраўлення непаладак", + "advanced_settings_troubleshooting_title": "Выпраўленне непаладак", + "age_months": "Узрост {months, plural, one {# месяц} few {# месяцы} many {# месяцаў} other {# месяцаў}}", + "age_year_months": "Узрост 1 год, {months, plural, one {# месяц} few {# месяцы} many {# месяцаў} other {# месяцаў}}", + "age_years": "{years, plural, other {Узрост #}}", + "album_added": "Альбом дададзены", + "album_cover_updated": "Вокладка альбома абноўлена", + "album_delete_confirmation": "Вы ўпэўнены, што хочаце выдаліць альбом {album}?", + "album_delete_confirmation_description": "Калі гэты альбом абагулены, іншыя карыстальнікі больш не змогуць атрымаць да яго доступ.", + "album_deleted": "Альбом выдалены", + "album_info_card_backup_album_excluded": "ВЫКЛЮЧАНЫ", + "album_info_card_backup_album_included": "УКЛЮЧАНЫ", + "album_info_updated": "Інфармацыя пра альбом абноўлена", + "album_leave": "Пакінуць альбом?", + "album_leave_confirmation": "Вы ўпэўнены, што хочаце пакінуць {album}?", + "album_name": "Назва альбома", + "album_options": "Параметры альбома", + "album_remove_user": "Выдаліць карыстальніка?", + "album_remove_user_confirmation": "Вы ўпэўнены, што хочаце выдаліць {user}?", + "album_search_not_found": "Па вашым запыце не знойдзена альбомаў", + "album_share_no_users": "Здаецца, вы падзяліліся гэтым альбомам з усімі карыстальнікамі, або ў вас няма ніводнага карыстальніка, з якім можна падзяліцца.", + "album_updated": "Альбом абноўлены", + "album_user_left": "Вы пакінулі {album}", + "album_user_removed": "Карыстальнік {user} выдалены", + "album_viewer_appbar_delete_confirm": "Вы ўпэўнены, што хочаце выдаліць гэты альбом са свайго ўліковага запісу?", + "album_viewer_appbar_share_err_delete": "Не ўдалося выдаліць альбом", + "album_viewer_appbar_share_err_leave": "Не ўдалося пакінуць альбом", + "album_viewer_appbar_share_err_title": "Не ўдалося змяніць назву альбома", + "album_viewer_appbar_share_leave": "Пакінуць альбом", + "album_viewer_appbar_share_to": "Абагуліць з", + "album_viewer_page_share_add_users": "Дадаць карыстальнікаў", + "album_with_link_access": "Дазволіць усім, хто мае спасылку, бачыць фота і людзей у гэтым альбоме.", + "albums": "Альбомы", + "albums_count": "{count, plural, one {1 альбом} few {{count, number} альбомы} many {{count, number} альбомаў} other {{count, number} альбомаў}}", + "albums_default_sort_order": "Прадвызначаны парадак сартавання альбомаў", + "albums_on_device_count": "Альбомы на прыладзе ({count})", + "all": "Усе", + "all_albums": "Усе альбомы", + "all_people": "Усе людзі", + "all_videos": "Усе відэа", + "allow_dark_mode": "Дазволіць цёмны рэжым", + "allow_edits": "Дазволіць рэдагаванне", + "alt_text_qr_code": "Відарыс QR-кода", + "anti_clockwise": "Супраць гадзіннікавай стрэлкі", + "api_key": "Ключ API", + "api_key_empty": "Назва ключа API не павінна быць пустой", + "api_keys": "Ключы API", + "app_bar_signout_dialog_content": "Вы ўпэўнены, што хочаце выйсці?", + "app_bar_signout_dialog_ok": "Так", + "app_bar_signout_dialog_title": "Выйсці", + "app_settings": "Налады праграмы", + "archive": "Архіў", + "archive_page_title": "Архіў ({count})", + "archive_size": "Памер архіва", + "are_these_the_same_person": "Ці гэта адзін і той жа чалавек?", + "are_you_sure_to_do_this": "Вы ўпэўнены, што хочаце гэта зрабіць?", + "asset_added_to_album": "Дададзена ў альбом", + "asset_adding_to_album": "Дадаванне ў альбом…", + "asset_skipped": "Прапушчана", + "asset_skipped_in_trash": "У сметніцы", + "asset_uploaded": "Запампавана", + "asset_uploading": "Запампоўванне…", + "authorized_devices": "Аўтарызаваныя прылады", + "back": "Назад", + "backup_album_selection_page_albums_device": "Альбомы на прыладзе ({count})", + "backup_all": "Усе", + "backup_controller_page_background_wifi": "Толькі праз Wi-Fi", + "buy": "Купіць Immich", + "cache_settings_clear_cache_button": "Ачысціць кэш", + "cache_settings_tile_title": "Лакальнае сховішча", + "cancel": "Скасаваць", + "cancel_search": "Скасаваць пошук", + "canceled": "Скасавана", + "city": "Горад", + "clear": "Ачысціць", + "clear_all": "Ачысціць усё", + "client_cert_dialog_msg_confirm": "ОК", + "client_cert_enter_password": "Увядзіце пароль", + "client_cert_import": "Імпарт", + "close": "Закрыць", + "collapse": "Згарнуць", + "collapse_all": "Згарнуць усё", + "color": "Колер", + "color_theme": "Колеравая тэма", + "continue": "Працягнуць", + "control_bottom_app_bar_create_new_album": "Стварыць новы альбом", + "control_bottom_app_bar_delete_from_immich": "Выдаліць з Immich", + "control_bottom_app_bar_delete_from_local": "Выдаліць з прылады", + "control_bottom_app_bar_edit_location": "Рэдагаваць месцазнаходжанне", + "country": "Краіна", + "cover": "Вокладка", + "covers": "Вокладкі", + "create": "Стварыць", + "create_album": "Стварыць альбом", + "create_album_page_untitled": "Без назвы", + "create_library": "Стварыць бібліятэку", + "create_link": "Стварыць спасылку", + "create_new_user": "Стварыць новага карыстальніка", + "create_tag": "Стварыць тэг", + "create_user": "Стварыць карыстальніка", + "dark": "Цёмная", + "day": "Дзень", + "delete": "Выдаліць", + "delete_album": "Выдаліць альбом", + "delete_dialog_ok_force": "Усё адно выдаліць", + "delete_dialog_title": "Выдаліць назаўжды", + "delete_face": "Выдаліць твар", + "delete_key": "Выдаліць ключ", + "delete_library": "Выдаліць бібліятэку", + "delete_link": "Выдаліць спасылку", + "delete_local_dialog_ok_force": "Усё адно выдаліць", + "delete_others": "Выдаліць іншыя", + "delete_tag": "Выдаліць тэг", + "delete_user": "Выдаліць карыстальніка", + "discord": "Discord", + "documentation": "Дакументацыя", + "done": "Гатова", + "download": "Спампаваць", + "download_canceled": "Спампоўванне скасавана", + "download_complete": "Спампоўванне завершана", + "download_enqueue": "Спампоўванне дададзена ў чаргу", + "downloading": "Спампоўванне", + "edit": "Рэдагаваць", + "edit_album": "Рэдагаваць альбом", + "edit_avatar": "Рэдагаваць аватар", + "edit_date": "Рэдагаваць дату", + "edit_date_and_time": "Рэдагаваь дату і час", + "edit_description": "Рэдагаваць апісанне", + "edit_description_prompt": "Выберыце новае апісанне:", + "edit_faces": "Рэдагаваць твары", + "edit_import_path": "Рэдагаваць шлях імпарту", + "edit_import_paths": "Рэдагаваць шляхі імпарту", + "edit_key": "Рэдагаваць ключ", + "edit_link": "Рэдагаваць спасылку", + "edit_location": "Рэдагаваць месцазнаходжанне", + "edit_location_dialog_title": "Месцазнаходжанне", + "edit_name": "Рэдагаваць назву", + "edit_people": "Рэдагаваць людзей", + "edit_tag": "Рэдагаваць тэг", + "edit_title": "Рэдагаваць загаловак", + "edit_user": "Рэдагаваць карыстальніка", + "edited": "Адрэдагавана", + "editor": "Рэдактар", + "editor_close_without_save_prompt": "Змены не будуць захаваны", + "editor_close_without_save_title": "Закрыць рэдактар?", + "editor_crop_tool_h2_aspect_ratios": "Суадносіны бакоў", + "editor_crop_tool_h2_rotation": "Паварот", + "error": "Памылка", + "error_saving_image": "Памылка: {error}", + "exif": "Exif", + "exif_bottom_sheet_description": "Дадаць апісанне...", + "favorite": "У абраным", + "favorite_or_unfavorite_photo": "Дадаць або выдаліць фота з абранага", + "favorites": "Абраныя", + "file_name": "Назва файла", + "filename": "Назва файла", + "filetype": "Тып файла", + "filter": "Фільтр", + "forward": "Наперад", + "gcast_enabled": "Google Cast", + "general": "Агульныя", + "go_back": "Назад", + "go_to_folder": "Перайсці да папкі", + "hi_user": "Вітаем, {name} ({email})", + "hide_all_people": "Схаваць усіх людзей", + "hide_gallery": "Схаваць галерэю", + "hide_named_person": "Схаваць {name}", + "hide_password": "Схаваць пароль", + "hide_person": "Схаваць чалавека", + "image_viewer_page_state_provider_download_started": "Спампоўванне пачалося", + "immich_logo": "Лагатып Immich", + "interval": { + "day_at_onepm": "Кожны дзень а 13-й гадзіне", + "hours": "{hours, plural, one {Кожную гадзіну} few {Кожныя {hours, number} гадзіны} many {Кожныя {hours, number} гадзін} other {Кожныя {hours, number} гадзін}}", + "night_at_midnight": "Кожную ноч апоўначы", + "night_at_twoam": "Кожную ноч а 2-й гадзіне" + }, + "language": "Мова", + "library": "Бібліятэка", + "light": "Светлая", + "login_form_back_button_text": "Назад", + "login_form_email_hint": "youremail@email.com", + "login_form_endpoint_hint": "http://your-server-ip:port", + "login_form_password_hint": "пароль", + "login_form_save_login": "Заставацца ў сістэме", + "main_menu": "Галоўнае меню", + "map_location_dialog_yes": "Так", + "map_settings_dark_mode": "Цёмны рэжым", + "map_settings_date_range_option_day": "Апошнія 24 гадзіны", + "map_settings_date_range_option_days": "Апошніх дзён: {days}", + "map_settings_date_range_option_year": "Апошні год", + "map_settings_date_range_option_years": "Апошніх год: {years}", + "map_settings_dialog_title": "Налады карты", + "map_settings_theme_settings": "Тэма карты", + "menu": "Меню", + "minute": "Хвіліна", + "month": "Месяц", + "monthly_title_text_date_format": "MMMM y", + "my_albums": "Мае альбомы", + "name": "Імя", + "name_or_nickname": "Імя або псеўданім", + "next": "Далей", + "no": "Не", + "offline": "Па-за сеткай", + "ok": "ОК", + "online": "У сетцы", + "open": "Адкрыць", + "or": "або", + "partner_list_user_photos": "Фота карыстальніка {user}", + "pause": "Прыпыніць", + "people": "Людзі", + "permission_onboarding_back": "Назад", + "permission_onboarding_continue_anyway": "Усё адно працягнуць", + "photos": "Фота", + "photos_and_videos": "Фота і відэа", + "place": "Месца", + "places": "Месцы", + "port": "Порт", + "previous": "Папярэдняе", + "profile": "Профіль", + "profile_drawer_app_logs": "Журналы", + "profile_drawer_github": "GitHub", + "purchase_button_buy": "Купіць", + "purchase_button_buy_immich": "Купіць Immich", + "purchase_button_select": "Выбраць", + "remove": "Выдаліць", + "remove_from_album": "Выдаліць з альбома", + "remove_from_favorites": "Выдаліць з абраных", + "remove_tag": "Выдаліць тэг", + "remove_url": "Выдаліць URL-адрас", + "remove_user": "Выдаліць карыстальніка", + "rename": "Перайменаваць", + "repository": "Рэпазіторый", + "reset": "Скінуць", + "reset_password": "Скінуць пароль", + "restore": "Аднавіць", + "restore_all": "Аднавіць усё", + "restore_user": "Аднавіць карыстальніка", + "resume": "Узнавіць", + "role": "Роля", + "role_editor": "Рэдактар", + "role_viewer": "Глядач", + "save": "Захаваць", + "save_to_gallery": "Захаваць у галерэю", + "search_filter_date": "Дата", + "search_filter_location": "Месцазнаходжанне", + "search_filter_location_title": "Выберыце месцазнаходжанне", + "search_filter_media_type": "Тып медыя", + "search_filter_media_type_title": "Выберыце тып медыя", + "search_page_screenshots": "Здымкі экрана", + "search_page_selfies": "Сэлфі", + "search_page_things": "Рэчы", + "search_page_your_map": "Ваша карта", + "second": "Секунда", + "send_message": "Адправіць паведамленне", + "setting_languages_apply": "Ужыць", + "setting_notifications_notify_never": "ніколі", + "settings": "Налады", + "share_add_photos": "Дадаць фота", + "shared_album_section_people_title": "ЛЮДЗІ", + "shared_link_info_chip_metadata": "EXIF", + "sharing_page_empty_list": "ПУСТЫ СПІС", + "sign_out": "Выйсці", + "sign_up": "Зарэгістравацца", + "size": "Памер", + "sort_title": "Загаловак", + "source": "Крыніца", + "tag": "Тэг", + "tags": "Тэгі", + "theme": "Тэма", + "theme_selection": "Выбар тэмы", "timeline": "Хроніка", "total": "Усяго", + "trash": "Сметніца", + "trash_page_delete_all": "Выдаліць усе", + "trash_page_restore_all": "Аднавіць усе", + "trash_page_title": "Сметніца ({count})", + "type": "Тып", + "undo": "Адрабіць", + "upload": "Запампаваць", + "upload_status_errors": "Памылкі", + "uploading": "Запампоўванне", + "url": "URL-адрас", "user": "Карыстальнік", + "user_has_been_deleted": "Гэты карыстальнік быў выдалены.", "user_id": "ID карыстальніка", "user_purchase_settings": "Купля", "user_purchase_settings_description": "Кіруйце пакупкамі", @@ -112,14 +461,14 @@ "view_next_asset": "Паказаць наступны аб'ект", "view_previous_asset": "Праглядзець папярэдні аб'ект", "view_stack": "Прагляд стэка", - "visibility_changed": "Відзімасць змянілася для {count, plural, one {# чалавек(-аў)} астатніх {# чалавек}}", + "visibility_changed": "Бачнасць змянілася для {count, plural, one {# чалавека} other {# чалавек}}", "waiting": "Чакаюць", "warning": "Папярэджанне", "week": "Тыдзень", "welcome": "Вітаем", "welcome_to_immich": "Вітаем у Immich", "year": "Год", - "years_ago": "{years, plural, one {# год} other {# гадоў}} таму", + "years_ago": "{years, plural, one {# год} few {# гады} many {# гадоў} other {# гадоў}} таму", "yes": "Так", "you_dont_have_any_shared_links": "У вас няма абагуленых спасылак", "zoom_image": "Павялічыць відарыс" diff --git a/i18n/bg.json b/i18n/bg.json index 2df3dd927d..cb5cc437b6 100644 --- a/i18n/bg.json +++ b/i18n/bg.json @@ -166,6 +166,20 @@ "metadata_settings_description": "Управление на настройките за метаданни", "migration_job": "Миграция", "migration_job_description": "Мигриране на миниатюрите за елементи и лица към най-новата структура на папките", + "nightly_tasks_cluster_faces_setting_description": "Изпълни разпознаване на лице за открити нови лица", + "nightly_tasks_cluster_new_faces_setting": "Разпознаване на нови лица", + "nightly_tasks_database_cleanup_setting": "Задачи по почистване на базата данни", + "nightly_tasks_database_cleanup_setting_description": "Премахни стари, ненужни записи от базата данни", + "nightly_tasks_generate_memories_setting": "Създаване на спомени", + "nightly_tasks_generate_memories_setting_description": "Създаване на нови спомени от съществуващи обекти", + "nightly_tasks_missing_thumbnails_setting": "Генериране на липсващи миниатюри", + "nightly_tasks_missing_thumbnails_setting_description": "Добавяне на обекти без миниатюра в опашката за създаване на миниатюра", + "nightly_tasks_settings": "Настройка на задачи за през нощта", + "nightly_tasks_settings_description": "Управление на задачите, изпълнявани през нощта", + "nightly_tasks_start_time_setting": "Време за начало", + "nightly_tasks_start_time_setting_description": "Време, когато сървъра ще започне изпълнение на нощни задачи", + "nightly_tasks_sync_quota_usage_setting": "Квота за синхронизация", + "nightly_tasks_sync_quota_usage_setting_description": "Обновяване на квотата според текущото потребление", "no_paths_added": "Няма добавени пътища", "no_pattern_added": "Няма добавен модел", "note_apply_storage_label_previous_assets": "Забележка: За да приложите етикета за съхранение към предварително качени файлове, стартирайте", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "URI за мобилно пренасочване", "oauth_mobile_redirect_uri_override": "URI пренасочване за мобилни устройства", "oauth_mobile_redirect_uri_override_description": "Разреши когато доставчика за OAuth удостоверяване не позволява за мобилни URI идентификатори, като ''{callback}''", + "oauth_role_claim": "Потвърждение на роля", + "oauth_role_claim_description": "Автоматично предоставяне на административни права при наличие на това потвържение. Потвърждението може да има стойност 'user' или 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Управление на настройките за вход с OAuth", "oauth_settings_more_details": "За повече информация за функционалността, се потърсете в docs.", @@ -357,6 +373,8 @@ "admin_password": "Администраторска парола", "administration": "Администрация", "advanced": "Разширено", + "advanced_settings_beta_timeline_subtitle": "Опитайте новите функции на приложението", + "advanced_settings_beta_timeline_title": "Бета версия на времевата линия", "advanced_settings_enable_alternate_media_filter_subtitle": "При синхронизация, използвайте тази опция като филтър, основан на промяна на даден критерии. Опитайте само в случай, че приложението има проблем с откриване на всички албуми.", "advanced_settings_enable_alternate_media_filter_title": "[ЕКСПЕРИМЕНТАЛНО] Използвай филтъра на алтернативното устройство за синхронизация на албуми", "advanced_settings_log_level_title": "Ниво на запис в дневника: {level}", @@ -427,6 +445,7 @@ "app_settings": "Настройки ма приложението", "appears_in": "Излиза в", "archive": "Архив", + "archive_action_prompt": "{count} са добавени в Архива", "archive_or_unarchive_photo": "Архивиране или деархивиране на снимка", "archive_page_no_archived_assets": "Не са намерени обекти в архива", "archive_page_title": "Архив ({count})", @@ -464,7 +483,6 @@ "assets": "Елементи", "assets_added_count": "Добавено {count, plural, one {# asset} other {# assets}}", "assets_added_to_album_count": "Добавен(и) са {count, plural, one {# актив} other {# актива}} в албума", - "assets_added_to_name_count": "Добавен(и) са {count, plural, one {# актив} other {# актива}} към {hasName, select, true {{name}} other {нов албум}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Обекта не може да се добави} other {Обектите не може да се добавят}} в албума", "assets_count": "{count, plural, one {# актив} other {# актива}}", "assets_deleted_permanently": "{count} обекта са изтрити завинаги", @@ -490,6 +508,7 @@ "back_close_deselect": "Назад, затваряне или премахване на избора", "background_location_permission": "Разрешение за достъп до местоположението във фонов режим", "background_location_permission_content": "За да може да чете имената на Wi-Fi мрежите и да ги превключва при работа във фонов режим, Immich трябва *винаги* да има достъп до точното местоположение", + "backup": "Архивиране", "backup_album_selection_page_albums_device": "Албуми на устройството ({count})", "backup_album_selection_page_albums_tap": "Натисни за да включиш, двойно за да изключиш", "backup_album_selection_page_assets_scatter": "Обектите могат да бъдат разпръснати в няколко албума. По този начин албумите могат да бъдат включени или изключени по време на процеса на архивиране.", @@ -703,7 +722,7 @@ "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM yyyy", "dark": "Тъмен", - "darkTheme": "Превключи на тъмна тема", + "dark_theme": "Тъмна тема", "date_after": "Дата след", "date_and_time": "Дата и час", "date_before": "Дата преди", @@ -719,6 +738,7 @@ "default_locale": "Локализация по подразбиране", "default_locale_description": "Форматиране на дати и числа в зависимост от езиковата настройка на браузъра", "delete": "Изтрий", + "delete_action_prompt": "{count} са изтрити завинаги", "delete_album": "Изтрий албум", "delete_api_key_prompt": "Сигурни ли сте, че искате да изтриете този API ключ?", "delete_dialog_alert": "Тези обекти ще бъдат изтрити завинаги и от Immich сървъра и от устройството", @@ -732,19 +752,20 @@ "delete_key": "Изтрий ключ", "delete_library": "Изтрий библиотека", "delete_link": "Изтрий линк", + "delete_local_action_prompt": "{count} са изтрити локално", "delete_local_dialog_ok_backed_up_only": "Изтрий локално само архивираните", "delete_local_dialog_ok_force": "Въпреки това изтрий", "delete_others": "Изтрий останалите", "delete_shared_link": "Изтриване на споделен линк", "delete_shared_link_dialog_title": "Изтрий споделената връзка", "delete_tag": "Изтрий таг", - "delete_tag_confirmation_prompt": "Сигурни ли сте, че искате да изтриете таг {tagName}?", + "delete_tag_confirmation_prompt": "Сигурни ли сте, че искате да изтриете тага {tagName}?", "delete_user": "Изтрий потребител", "deleted_shared_link": "Изтрит споделен линк", "deletes_missing_assets": "Изтрива файлове, които липсват на диска", "description": "Описание", "description_input_hint_text": "Добави описание...", - "description_input_submit_error": "Неуспешно обновяване на описанието. За подробности виж в дневника", + "description_input_submit_error": "Неуспешно обновяване на описанието. За подробности вижте в дневника", "details": "Детайли", "direction": "Посока", "disabled": "Изключено", @@ -762,6 +783,7 @@ "documentation": "Документация", "done": "Готово", "download": "Изтегли", + "download_action_prompt": "Зареждане на {count} обекта", "download_canceled": "Изтеглянето е отменено", "download_complete": "Изтеглянето завърши", "download_enqueue": "Изтеглянето е добавено в опашката", @@ -799,6 +821,7 @@ "edit_key": "Редактиране на ключ", "edit_link": "Редактиране на линк", "edit_location": "Редактиране на местоположението", + "edit_location_action_prompt": "{count} локации са редактирани", "edit_location_dialog_title": "Местоположение", "edit_name": "Редактиране на име", "edit_people": "Редактиране на хора", @@ -984,6 +1007,7 @@ "failed_to_load_assets": "Неуспешно зареждане на елементи", "failed_to_load_folder": "Неуспешно зареждане на папка", "favorite": "Любим", + "favorite_action_prompt": "{count} са добавени в Любими", "favorite_or_unfavorite_photo": "Добави или премахни снимка от Любими", "favorites": "Любими", "favorites_page_no_favorites": "Не са намерени любими обекти", @@ -1127,10 +1151,10 @@ "library_page_sort_created": "Дата на създаване", "library_page_sort_last_modified": "Последна промяна", "library_page_sort_title": "Заглавие на албума", + "licenses": "Лицензи", "light": "Светло", "like_deleted": "Като изтрит", "link_motion_video": "Линк към видео", - "link_options": "Опции на линк за споделяне", "link_to_oauth": "Линк към OAuth", "linked_oauth_account": "Свързан OAuth акаунт", "list": "Лист", @@ -1193,7 +1217,6 @@ "manage_your_devices": "Управление на влезлите в системата устройства", "manage_your_oauth_connection": "Управление на OAuth връзката", "map": "Карта", - "map_assets_in_bound": "{count} снимки", "map_assets_in_bounds": "{count} снимки", "map_cannot_get_user_location": "Не можах да получа местоположението", "map_location_dialog_yes": "Да", @@ -1246,6 +1269,7 @@ "more": "Още", "move": "Премести", "move_off_locked_folder": "Извади от заключената папка", + "move_to_lock_folder_action_prompt": "{count} са добавени в заключената папка", "move_to_locked_folder": "Премести в заключена папка", "move_to_locked_folder_confirmation": "Тези снимки и видеа ще бъдат изтрити от всички албуми и ще са достъпни само в заключената папка", "moved_to_archive": "{count, plural, one {# обект е преместен} many {# обекта са преместени} other {# обекта са преместени}} в архива", @@ -1495,7 +1519,9 @@ "remove_custom_date_range": "Премахни зададения диапазон от дати", "remove_deleted_assets": "Премахни Изтритите Елементи", "remove_from_album": "Премахни от албума", + "remove_from_album_action_prompt": "{count} са премахнати от албума", "remove_from_favorites": "Премахни от Любими", + "remove_from_lock_folder_action_prompt": "{count} са премахнати от заключената папка", "remove_from_locked_folder": "Махни от заключената папка", "remove_from_locked_folder_confirmation": "Сигурни ли си, че искате тези снимки и видеа да бъдат извадени от заключената папка? Те ще бъдат видими в библиотеката.", "remove_from_shared_link": "Премахни от споделения линк", @@ -1667,6 +1693,7 @@ "settings_saved": "Настройките са запазени", "setup_pin_code": "Задай PIN код", "share": "Споделяне", + "share_action_prompt": "{count} споделени обекта", "share_add_photos": "Добави снимки", "share_assets_selected": "{count} избрани", "share_dialog_preparing": "Подготовка...", @@ -1768,6 +1795,7 @@ "sort_title": "Заглавие", "source": "Код", "stack": "Събери", + "stack_action_prompt": "{count} са групирани", "stack_duplicates": "Подреждане на дубликати", "stack_select_one_photo": "Избери една главна снимка за събраните снимки", "stack_selected_photos": "Подреждане на избрани снимки", @@ -1838,6 +1866,7 @@ "total": "Общо", "total_usage": "Общо използвано", "trash": "Кошче", + "trash_action_prompt": "{count} са преместени в коша", "trash_all": "Изхвърли всички", "trash_count": "В Кошчето {count, number}", "trash_delete_asset": "Вкарай в Кошчето/Изтрий елемент", @@ -1855,9 +1884,11 @@ "unable_to_change_pin_code": "Невъзможна промяна на PIN кода", "unable_to_setup_pin_code": "Неуспешно задаване на PIN кода", "unarchive": "Разархивирай", + "unarchive_action_prompt": "{count} са премахнати от Архива", "unarchived_count": "{count, plural, other {Неархивирани #}}", "undo": "Отмени", "unfavorite": "Премахване от любимите", + "unfavorite_action_prompt": "{count} са премахнати от Любими", "unhide_person": "Покажи отново човека", "unknown": "Неизвестно", "unknown_country": "Непозната Държава", @@ -1875,7 +1906,9 @@ "unselect_all_duplicates": "От маркирай всички дубликати", "unselect_all_in": "Премахни избора на всички от групата {group}", "unstack": "Разкачи", + "unstack_action_prompt": "{count} са разгрупирани", "unstacked_assets_count": "Разкачени {count, plural, one {# елемент} other {# елементи}}", + "untagged": "Немаркирани", "up_next": "Следващ", "updated_at": "Обновено", "updated_password": "Паролата е актуализирана", diff --git a/i18n/bn.json b/i18n/bn.json index ed108652b7..9dce989af4 100644 --- a/i18n/bn.json +++ b/i18n/bn.json @@ -8,6 +8,7 @@ "actions": "কর্ম", "active": "সচল", "activity": "কার্যকলাপ", + "activity_changed": "একটিভিটি এখন {enabled, select, true {চালু} other {বন্ধ}} আছে", "add": "যোগ করুন", "add_a_description": "একটি বিবরণ যোগ করুন", "add_a_location": "একটি অবস্থান যোগ করুন", @@ -15,5 +16,84 @@ "add_a_title": "একটি শিরোনাম যোগ করুন", "add_endpoint": "এন্ডপয়েন্ট যোগ করুন", "add_exclusion_pattern": "বহির্ভূতকরণ নমুনা", - "add_url": "লিঙ্ক যোগ করুন" + "add_import_path": "ইমপোর্ট করার পাথ যুক্ত করুন", + "add_location": "অবস্থান যুক্ত করুন", + "add_more_users": "আরো ব্যবহারকারী যুক্ত করুন", + "add_partner": "অংশীদার যোগ করুন", + "add_path": "পাথ যুক্ত করুন", + "add_photos": "ছবি যুক্ত করুন", + "add_tag": "ট্যাগ যুক্ত করুন", + "add_to": "যুক্ত করুন…", + "add_to_album": "এলবাম এ যোগ করুন", + "add_to_album_bottom_sheet_added": "{album} এ যোগ করা হয়েছে", + "add_to_album_bottom_sheet_already_exists": "{album} এ আগে থেকেই আছে", + "add_to_shared_album": "শেয়ার করা অ্যালবামে যোগ করুন", + "add_url": "লিঙ্ক যোগ করুন", + "added_to_archive": "আর্কাইভ এ যোগ করা হয়েছে", + "added_to_favorites": "ফেভারিটে যোগ করা হয়েছে", + "added_to_favorites_count": "পছন্দের তালিকায় {count, number} যোগ করা হয়েছে", + "admin": { + "add_exclusion_pattern_description": "এক্সক্লুশন প্যাটার্ন যোগ করুন। *, **, এবং ? ব্যবহার করে গ্লোবিং করা সম্ভব। \"Raw\" নামের যেকোনো ডিরেক্টরিতে থাকা সমস্ত ফাইল বাদ দিতে \"**/Raw/**\" ব্যবহার করুন। \".tif\" দিয়ে শেষ হওয়া সমস্ত ফাইল বাদ দিতে \"**/*.tif\" ব্যবহার করুন। একটি সম্পূর্ণ পাথ বাদ দিতে, \"/path/to/ignore/**\" ব্যবহার করুন।", + "admin_user": "এডমিন ইউজার", + "asset_offline_description": "এই বহিরাগত লাইব্রেরি সম্পদটি আর ডিস্কে পাওয়া যাচ্ছে না এবং ট্র্যাশে সরানো হয়েছে। যদি ফাইলটি লাইব্রেরির মধ্যে সরানো হয়ে থাকে, তাহলে নতুন সংশ্লিষ্ট সম্পদের জন্য আপনার টাইমলাইন পরীক্ষা করুন। এই সম্পদটি পুনরুদ্ধার করতে, দয়া করে নিশ্চিত করুন যে নীচের ফাইল পাথটি Immich দ্বারা অ্যাক্সেস করা যেতে পারে এবং লাইব্রেরিটি স্ক্যান করুন।", + "authentication_settings": "প্রমাণীকরণ সেটিংস", + "authentication_settings_description": "পাসওয়ার্ড, OAuth এবং অন্যান্য প্রমাণীকরণ সেটিংস পরিচালনা করুন", + "authentication_settings_disable_all": "আপনি কি নিশ্চিত যে আপনি সমস্ত লগইন পদ্ধতি অক্ষম করতে চান? লগইন সম্পূর্ণরূপে অক্ষম করা হবে।", + "authentication_settings_reenable": "পুনরায় সক্ষম করতে, একটি সার্ভার কমান্ড ব্যবহার করুন।", + "background_task_job": "ব্যাকগ্রাউন্ড টাস্ক", + "backup_database": "ডাটাবেস ডাম্প তৈরি করুন", + "backup_database_enable_description": "ডাটাবেস ডাম্প সক্রিয় করুন", + "backup_keep_last_amount": "আগের ডাম্পের পরিমাণ রাখা হবে", + "backup_settings": "ডাটাবেস ডাম্প সেটিংস", + "backup_settings_description": "ডাটাবেস ডাম্প সেটিংস পরিচালনা করুন।", + "cleared_jobs": "{job} এর জন্য jobs খালি করা হয়েছে", + "config_set_by_file": "কনফিগ বর্তমানে একটি কনফিগ ফাইল দ্বারা সেট করা আছে", + "confirm_delete_library": "আপনি কি নিশ্চিত যে আপনি {library} লাইব্রেরি মুছে ফেলতে চান?", + "confirm_delete_library_assets": "আপনি কি নিশ্চিত যে আপনি এই লাইব্রেরিটি মুছে ফেলতে চান? এটি Immich থেকে {count, plural, one {# contained asset} other {all # contained asset}} মুছে ফেলবে এবং পূর্বাবস্থায় ফেরানো যাবে না। ফাইলগুলি ডিস্কে থাকবে।", + "confirm_email_below": "নিশ্চিত করতে, নিচে \"{email}\" টাইপ করুন", + "confirm_reprocess_all_faces": "আপনি কি নিশ্চিত যে আপনি সমস্ত মুখ পুনরায় প্রক্রিয়া করতে চান? এটি নামযুক্ত ব্যক্তিদেরও মুছে ফেলবে।", + "confirm_user_password_reset": "আপনি কি নিশ্চিত যে আপনি {user} এর পাসওয়ার্ড রিসেট করতে চান?", + "confirm_user_pin_code_reset": "আপনি কি নিশ্চিত যে আপনি {user} এর পিন কোড রিসেট করতে চান?", + "create_job": "job তৈরি করুন", + "cron_expression": "ক্রোন এক্সপ্রেশন", + "cron_expression_description": "ক্রোন ফর্ম্যাট ব্যবহার করে স্ক্যানিং ব্যবধান সেট করুন। আরও তথ্যের জন্য দয়া করে দেখুন যেমন Crontab Guru", + "cron_expression_presets": "ক্রোন এক্সপ্রেশন প্রিসেট", + "disable_login": "লগইন অক্ষম করুন", + "duplicate_detection_job_description": "অনুরূপ ছবি সনাক্ত করতে সম্পদগুলিতে মেশিন লার্নিং চালান। স্মার্ট অনুসন্ধানের উপর নির্ভর করে", + "exclusion_pattern_description": "এক্সক্লুশন প্যাটার্ন ব্যবহার করে আপনি আপনার লাইব্রেরি স্ক্যান করার সময় ফাইল এবং ফোল্ডারগুলিকে উপেক্ষা করতে পারবেন। যদি আপনার এমন ফোল্ডার থাকে যেখানে এমন ফাইল থাকে যা আপনি আমদানি করতে চান না, যেমন RAW ফাইল।", + "external_library_management": "বহিরাগত গ্রন্থাগার ব্যবস্থাপনা", + "face_detection": "মুখ সনাক্তকরণ", + "face_detection_description": "মেশিন লার্নিং ব্যবহার করে অ্যাসেটে থাকা মুখ/চেহারা গুলি সনাক্ত করুন। ভিডিও গুলির জন্য, শুধুমাত্র থাম্বনেইল বিবেচনা করা হয়। \"রিফ্রেশ\" (পুনরায়) সমস্ত অ্যাসেট প্রক্রিয়া করে। \"রিসেট\" করার মাধ্যমে অতিরিক্তভাবে সমস্ত বর্তমান মুখের ডেটা সাফ করে। \"অনুপস্থিত\" অ্যাসেটগুলিকে সারিবদ্ধ করে যা এখনও প্রক্রিয়া করা হয়নি। সনাক্ত করা মুখগুলিকে ফেসিয়াল রিকগনিশনের জন্য সারিবদ্ধ করা হবে, ফেসিয়াল ডিটেকশন সম্পূর্ণ হওয়ার পরে, বিদ্যমান বা নতুন ব্যক্তিদের মধ্যে গোষ্ঠীবদ্ধ করে।", + "facial_recognition_job_description": "শনাক্ত করা মুখগুলিকে মানুষের মধ্যে গোষ্ঠীভুক্ত করুন। মুখ সনাক্তকরণ সম্পূর্ণ হওয়ার পরে এই ধাপটি চলে। \"রিসেট\" (পুনরায়) সমস্ত মুখকে ক্লাস্টার করে। \"অনুপস্থিত\" মুখগুলিকে সারিতে রাখে যেখানে কোনও ব্যক্তিকে বরাদ্দ করা হয়নি।", + "failed_job_command": "কমান্ড {command} কাজের জন্য ব্যর্থ হয়েছে: {job}", + "force_delete_user_warning": "সতর্কতা: এটি ব্যবহারকারী এবং সমস্ত সম্পদ অবিলম্বে সরিয়ে ফেলবে। এটি পূর্বাবস্থায় ফেরানো যাবে না এবং ফাইলগুলি পুনরুদ্ধার করা যাবে না।", + "image_format": "ফরম্যাট", + "image_format_description": "WebP JPEG এর তুলনায় ছোট ফাইল তৈরি করে, কিন্তু এনকোড করতে ধীর।", + "image_fullsize_description": "জুম ইন করার সময় ব্যবহৃত স্ট্রিপড মেটাডেটা সহ পূর্ণ আকারের ছবি", + "image_fullsize_enabled": "পূর্ণ-আকারের ছবি তৈরি সক্ষম করুন", + "image_fullsize_enabled_description": "ওয়েব-বান্ধব নয় এমন ফর্ম্যাটের জন্য পূর্ণ-আকারের ছবি তৈরি করুন। \"এমবেডেড প্রিভিউ পছন্দ করুন\" সক্ষম করা থাকলে, রূপান্তর ছাড়াই এমবেডেড প্রিভিউ সরাসরি ব্যবহার করা হয়। JPEG-এর মতো ওয়েব-বান্ধব ফর্ম্যাটগুলিকে প্রভাবিত করে না।", + "image_fullsize_quality_description": "পূর্ণ-আকারের ছবির মান ১-১০০। উচ্চতর হলে ভালো, কিন্তু আরও বড় ফাইল তৈরি হয়।", + "image_fullsize_title": "পূর্ণ-আকারের চিত্র সেটিংস", + "image_prefer_embedded_preview": "এম্বেড করা প্রিভিউ পছন্দ করুন", + "image_prefer_embedded_preview_setting_description": "ছবি প্রক্রিয়াকরণের জন্য এবং যখনই উপলব্ধ থাকবে তখন RAW ফটোতে এমবেডেড প্রিভিউ ব্যবহার করুন। এটি কিছু ছবির জন্য আরও সঠিক রঙ তৈরি করতে পারে, তবে প্রিভিউয়ের মান ক্যামেরা-নির্ভর এবং ছবিতে আরও কম্প্রেশন আর্টিফ্যাক্ট থাকতে পারে।", + "image_prefer_wide_gamut": "প্রশস্ত পরিসর পছন্দ করুন", + "image_prefer_wide_gamut_setting_description": "থাম্বনেইলের জন্য ডিসপ্লে P3 ব্যবহার করুন। এটি প্রশস্ত রঙের স্থান সহ ছবির প্রাণবন্ততা আরও ভালভাবে সংরক্ষণ করে, তবে পুরানো ব্রাউজার সংস্করণ সহ পুরানো ডিভাইসগুলিতে ছবিগুলি ভিন্নভাবে প্রদর্শিত হতে পারে। রঙের পরিবর্তন এড়াতে sRGB ছবিগুলিকে sRGB হিসাবে রাখা হয়।", + "image_preview_description": "স্ট্রিপড মেটাডেটা সহ মাঝারি আকারের ছবি, একটি একক সম্পদ দেখার সময় এবং মেশিন লার্নিংয়ের জন্য ব্যবহৃত হয়", + "image_preview_quality_description": "১-১০০ এর মধ্যে প্রিভিউ কোয়ালিটি। বেশি হলে ভালো, কিন্তু বড় ফাইল তৈরি হয় এবং অ্যাপের প্রতিক্রিয়াশীলতা কমাতে পারে। কম মান সেট করলে মেশিন লার্নিং কোয়ালিটির উপর প্রভাব পড়তে পারে।", + "image_preview_title": "প্রিভিউ সেটিংস", + "image_quality": "গুণমান", + "image_resolution": "রেজোলিউশন", + "image_resolution_description": "উচ্চ রেজোলিউশনের ক্ষেত্রে আরও বিস্তারিত তথ্য সংরক্ষণ করা সম্ভব কিন্তু এনকোড করতে বেশি সময় লাগে, ফাইলের আকার বড় হয় এবং অ্যাপের প্রতিক্রিয়াশীলতা কমাতে পারে।", + "image_settings": "চিত্র সেটিংস", + "image_settings_description": "তৈরি করা ছবির মান এবং রেজোলিউশন পরিচালনা করুন", + "image_thumbnail_description": "মেটাডেটা বাদ দেওয়া ছোট থাম্বনেইল, মূল টাইমলাইনের মতো ছবির গ্রুপ দেখার সময় ব্যবহৃত হয়", + "image_thumbnail_quality_description": "থাম্বনেইলের মান ১-১০০। বেশি হলে ভালো, কিন্তু বড় ফাইল তৈরি হয় এবং অ্যাপের প্রতিক্রিয়াশীলতা কমাতে পারে।", + "image_thumbnail_title": "থাম্বনেল সেটিংস", + "job_concurrency": "{job} কনকারেন্সি", + "job_created": "Job তৈরি হয়েছে", + "job_not_concurrency_safe": "এই কাজটি সমকালীন-নিরাপদ নয়।", + "job_settings": "কাজের সেটিংস", + "job_settings_description": "কাজের সমান্তরালতা পরিচালনা করুন", + "job_status": "চাকরির অবস্থা" + } } diff --git a/i18n/ca.json b/i18n/ca.json index 5c53311a02..d24481f077 100644 --- a/i18n/ca.json +++ b/i18n/ca.json @@ -166,6 +166,10 @@ "metadata_settings_description": "Administrar la configuració de les metadades", "migration_job": "Migració", "migration_job_description": "Migra les miniatures d'elements i cares cap a la nova estructura de carpetes", + "nightly_tasks_cluster_new_faces_setting": "Agrupa cares noves", + "nightly_tasks_database_cleanup_setting": "Tasques de neteja de la base de dades", + "nightly_tasks_database_cleanup_setting_description": "Netegeu les dades antigues i caducades de la base de dades", + "nightly_tasks_missing_thumbnails_setting": "Generar les miniatures restants", "no_paths_added": "No s'ha afegit cap ruta", "no_pattern_added": "Cap patró aplicat", "note_apply_storage_label_previous_assets": "Nota: Per aplicar l'etiquetatge d'emmagatzematge a elements pujats prèviament, executeu la", @@ -196,6 +200,8 @@ "oauth_mobile_redirect_uri": "URI de redirecció mòbil", "oauth_mobile_redirect_uri_override": "Sobreescriu l'URI de redirecció mòbil", "oauth_mobile_redirect_uri_override_description": "Habilita quan el proveïdor d'OAuth no permet una URI mòbil, com ara ''{callback}''", + "oauth_role_claim": "Concessió de rol", + "oauth_role_claim_description": "Atorgar accés d'administrador automàticament segons la presència d'aquesta concessió. La concessió pot ser 'usuari' o 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Gestiona la configuració de l'inici de sessió OAuth", "oauth_settings_more_details": "Per a més detalls sobre aquesta funcionalitat, consulteu la documentació.", @@ -244,6 +250,7 @@ "storage_template_migration_info": "Les extensions es convertiran a minúscules. Els canvis de plantilla només s'aplicaran a nous elements. Per aplicar la plantilla rectroactivament a elements pujats prèviament, executeu la {job}.", "storage_template_migration_job": "Tasca de migració de la plantilla d'emmagatzematge", "storage_template_more_details": "Per obtenir més detalls sobre aquesta funció, consulteu la Storage Template i les seves implications", + "storage_template_onboarding_description_v2": "Un cop habilitada, aquesta funció organitzarà automàticament els fitxers a partir d'una plantilla definida per l'usuari. Per a més informació, podeu consultar la documentació.", "storage_template_path_length": "Límit aproximat de longitud de la ruta: {length, number}/{limit, number}", "storage_template_settings": "Plantilla d'emmagatzematge", "storage_template_settings_description": "Gestiona l'estructura de les carpetes i el nom del fitxers dels elements pujats", @@ -359,7 +366,7 @@ "advanced_settings_enable_alternate_media_filter_subtitle": "Feu servir aquesta opció per filtrar els continguts multimèdia durant la sincronització segons criteris alternatius. Només proveu-ho si teniu problemes amb l'aplicació per detectar tots els àlbums.", "advanced_settings_enable_alternate_media_filter_title": "Utilitza el filtre de sincronització d'àlbums de dispositius alternatius", "advanced_settings_log_level_title": "Nivell de registre: {level}", - "advanced_settings_prefer_remote_subtitle": "Alguns dispositius són molt lents en carregar miniatures dels elements del dispositiu. Activeu aquest paràmetre per carregar imatges remotes en el seu lloc.", + "advanced_settings_prefer_remote_subtitle": "Alguns dispositius són molt lents en carregar miniatures dels elements locals. Activeu aquest paràmetre per carregar imatges remotes en el seu lloc.", "advanced_settings_prefer_remote_title": "Prefereix imatges remotes", "advanced_settings_proxy_headers_subtitle": "Definiu les capçaleres de proxy que Immich per enviar amb cada sol·licitud de xarxa", "advanced_settings_proxy_headers_title": "Capçaleres de proxy", @@ -426,6 +433,7 @@ "app_settings": "Configuració de l'app", "appears_in": "Apareix a", "archive": "Arxiu", + "archive_action_prompt": "{count} afegit a Arxiu", "archive_or_unarchive_photo": "Arxivar o desarxivar fotografia", "archive_page_no_archived_assets": "No s'ha trobat res arxivat", "archive_page_title": "Arxiu({count})", @@ -463,7 +471,6 @@ "assets": "Elements", "assets_added_count": "{count, plural, one {Afegit un element} other {Afegits # elements}}", "assets_added_to_album_count": "{count, plural, one {Afegit un element} other {Afegits # elements}} a l'àlbum", - "assets_added_to_name_count": "{count, plural, one {S'ha afegit # recurs} other {S'han afegit # recursos}} a {hasName, select, true {{name}} other {new album}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} no es pot afegir a l'àlbum", "assets_count": "{count, plural, one {# recurs} other {# recursos}}", "assets_deleted_permanently": "{count} element(s) esborrats permanentment", @@ -489,6 +496,7 @@ "back_close_deselect": "Tornar, tancar o anul·lar la selecció", "background_location_permission": "Permís d'ubicació en segon pla", "background_location_permission_content": "Per canviar de xarxa quan s'executa en segon pla, Immich ha de *sempre* tenir accés a la ubicació precisa perquè l'aplicació pugui llegir el nom de la xarxa Wi-Fi", + "backup": "Còpia", "backup_album_selection_page_albums_device": "Àlbums al dispositiu ({count})", "backup_album_selection_page_albums_tap": "Un toc per incloure, doble toc per excloure", "backup_album_selection_page_assets_scatter": "Els elements poden dispersar-se en diversos àlbums. Per tant, els àlbums es poden incloure o excloure durant el procés de còpia de seguretat.", @@ -702,7 +710,7 @@ "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Fosc", - "darkTheme": "Activa/desactiva el tema fosc", + "dark_theme": "Canviar a tema fosc", "date_after": "Data posterior a", "date_and_time": "Data i hora", "date_before": "Data anterior a", @@ -718,6 +726,7 @@ "default_locale": "Localització predeterminada", "default_locale_description": "Format de dates i números segons la configuració del navegador", "delete": "Esborra", + "delete_action_prompt": "{count} eliminats permanentment", "delete_album": "Esborra l'àlbum", "delete_api_key_prompt": "Esteu segurs que voleu eliminar aquesta clau API?", "delete_dialog_alert": "Aquests elements seran eliminats de manera permanent d'Immich i del vostre dispositiu", @@ -798,6 +807,7 @@ "edit_key": "Edita clau", "edit_link": "Edita enllaç", "edit_location": "Edita ubicació", + "edit_location_action_prompt": "{count} ubicacions editades", "edit_location_dialog_title": "Ubicació", "edit_name": "Edita el nom", "edit_people": "Edita la gent", @@ -983,6 +993,7 @@ "failed_to_load_assets": "Error carregant recursos", "failed_to_load_folder": "No s'ha pogut carregar la carpeta", "favorite": "Preferit", + "favorite_action_prompt": "{count} afegit a Favorits", "favorite_or_unfavorite_photo": "Foto preferida o no preferida", "favorites": "Preferits", "favorites_page_no_favorites": "No s'han trobat preferits", @@ -1129,7 +1140,6 @@ "light": "Llum", "like_deleted": "M'agrada suprimit", "link_motion_video": "Enllaçar vídeo en moviment", - "link_options": "Opcions d'enllaç", "link_to_oauth": "Enllaç a OAuth", "linked_oauth_account": "Compte OAuth enllaçat", "list": "Llista", @@ -1149,6 +1159,7 @@ "locked_folder": "Carpeta bloquejada", "log_out": "Tanca la sessió", "log_out_all_devices": "Tanqueu la sessió de tots els dispositius", + "logged_in_as": "Sessió iniciada com a {user}", "logged_out_all_devices": "S'ha tancat la sessió de tots els dispositius", "logged_out_device": "Dispositiu tancat", "login": "Iniciar sessió", @@ -1191,7 +1202,6 @@ "manage_your_devices": "Gestioneu els vostres dispositius connectats", "manage_your_oauth_connection": "Gestioneu la vostra connexió OAuth", "map": "Mapa", - "map_assets_in_bound": "{count} foto", "map_assets_in_bounds": "{count} fotos", "map_cannot_get_user_location": "No es pot obtenir la ubicació de l'usuari", "map_location_dialog_yes": "Sí", @@ -1244,6 +1254,7 @@ "more": "Més", "move": "Moure", "move_off_locked_folder": "Moure fora de la carpeta bloquejada", + "move_to_lock_folder_action_prompt": "{count} afegides a la carpeta protegida", "move_to_locked_folder": "Moure a la carpeta bloquejada", "move_to_locked_folder_confirmation": "Aquestes fotos i vídeos seran eliminades de tots els àlbums, i només podran ser vistes des de la carpeta bloquejada", "moved_to_archive": "S'han mogut {count, plural, one {# asset} other {# assets}} a l'arxiu", @@ -1494,6 +1505,7 @@ "remove_deleted_assets": "Suprimeix fitxers fora de línia", "remove_from_album": "Treu de l'àlbum", "remove_from_favorites": "Eliminar dels preferits", + "remove_from_lock_folder_action_prompt": "{count} eliminades de la carpeta protegida", "remove_from_locked_folder": "Elimina de la carpeta bloquejada", "remove_from_locked_folder_confirmation": "Segur que vols moure aquestes fotos i vídeos fora de la carpeta bloquejada? Seran visibles a la teva biblioteca.", "remove_from_shared_link": "Eliminar de l'enllaç compartit", @@ -1606,6 +1618,7 @@ "select_album_cover": "Seleccionar la portada de l'àlbum", "select_all": "Selecciona-ho tot", "select_all_duplicates": "Seleccioneu tots els duplicats", + "select_all_in": "Selecciona tot en {group}", "select_avatar_color": "Tria color de l'avatar", "select_face": "Selecciona cara", "select_featured_photo": "Selecciona foto principal", @@ -1835,6 +1848,7 @@ "total": "Total", "total_usage": "Ús total", "trash": "Paperera", + "trash_action_prompt": "{count} mogudes a la brossa", "trash_all": "Envia-ho tot a la paperera", "trash_count": "Paperera {count, number}", "trash_delete_asset": "Esborra/Elimina element", @@ -1852,9 +1866,11 @@ "unable_to_change_pin_code": "No es pot canviar el codi PIN", "unable_to_setup_pin_code": "No s'ha pogut configurar el codi PIN", "unarchive": "Desarxivar", + "unarchive_action_prompt": "{count} eliminades de l'arxiu", "unarchived_count": "{count, plural, other {# elements desarxivats}}", "undo": "Desfer", "unfavorite": "Reverteix preferit", + "unfavorite_action_prompt": "{count} eliminades de preferits", "unhide_person": "Mostra persona", "unknown": "Desconegut", "unknown_country": "País Desconegut", @@ -1870,6 +1886,7 @@ "unsaved_change": "Canvi no desat", "unselect_all": "Deselecciona-ho tot", "unselect_all_duplicates": "Desmarqueu tots els duplicats", + "unselect_all_in": "Desseleccionar tots els elements de {group}", "unstack": "Desapila", "unstacked_assets_count": "No apilat {count, plural, one {# recurs} other {# recursos}}", "up_next": "Pròxim", diff --git a/i18n/cs.json b/i18n/cs.json index 82e72cab46..c5e5ac9553 100644 --- a/i18n/cs.json +++ b/i18n/cs.json @@ -14,6 +14,7 @@ "add_a_location": "Přidat polohu", "add_a_name": "Přidat jméno", "add_a_title": "Přidat název", + "add_birthday": "Přidat datum narození", "add_endpoint": "Přidat koncový bod", "add_exclusion_pattern": "Přidat vzor vyloučení", "add_import_path": "Přidat cestu importu", @@ -44,7 +45,14 @@ "backup_database": "Vytvořit výpis databáze", "backup_database_enable_description": "Povolit výpisy z databáze", "backup_keep_last_amount": "Počet předchozích výpisů, které se mají ponechat", - "backup_settings": "Nastavení výpisu databáze", + "backup_onboarding_1_description": "kopie v cloudu nebo na jiném fyzickém místě.", + "backup_onboarding_2_description": "místní kopie na různých zařízeních. To zahrnuje hlavní soubory a jejich místní zálohu.", + "backup_onboarding_3_description": "kompletní kopie vašich dat, včetně původních souborů. To zahrnuje 1 kopii na jiném místě a 2 místní kopie.", + "backup_onboarding_description": "K ochraně vašich dat doporučujeme strategii zálohování 3-2-1. Pro komplexní zálohování byste měli uchovávat kopie nahraných fotografií/videí i databáze Immich.", + "backup_onboarding_footer": "Další informace o zálohování Immiche naleznete v dokumentaci.", + "backup_onboarding_parts_title": "Záloha 3-2-1 zahrnuje:", + "backup_onboarding_title": "Zálohy", + "backup_settings": "Zálohování databáze", "backup_settings_description": "Správa nastavení výpisu databáze.", "cleared_jobs": "Hotové úlohy pro: {job}", "config_set_by_file": "Konfigurace je aktuálně prováděna konfiguračním souborem", @@ -166,6 +174,20 @@ "metadata_settings_description": "Správa nastavení metadat", "migration_job": "Migrace", "migration_job_description": "Migrace miniatur snímků a obličejů do nejnovější struktury složek", + "nightly_tasks_cluster_faces_setting_description": "Spustit rozpoznávání obličeje na nově nalezených obličejích", + "nightly_tasks_cluster_new_faces_setting": "Seskupit nové tváře", + "nightly_tasks_database_cleanup_setting": "Úlohy čištění databáze", + "nightly_tasks_database_cleanup_setting_description": "Vyčistit databázi od starých dat, jejichž platnost vypršela", + "nightly_tasks_generate_memories_setting": "Vytváření vzpomínek", + "nightly_tasks_generate_memories_setting_description": "Vytváření nových vzpomínek z položek", + "nightly_tasks_missing_thumbnails_setting": "Generovat chybějící miniatury", + "nightly_tasks_missing_thumbnails_setting_description": "Řadit položky bez miniatur do fronty pro generování miniatur", + "nightly_tasks_settings": "Noční úlohy", + "nightly_tasks_settings_description": "Správa nočních úkolů", + "nightly_tasks_start_time_setting": "Čas zahájení", + "nightly_tasks_start_time_setting_description": "Čas, kdy server spustí noční úlohy", + "nightly_tasks_sync_quota_usage_setting": "Synchronizace využití kvóty", + "nightly_tasks_sync_quota_usage_setting_description": "Aktualizovat kvótu úložiště uživatele na základě aktuálního využití", "no_paths_added": "Nebyly přidány žádné cesty", "no_pattern_added": "Nebyl přidán žádný vzor", "note_apply_storage_label_previous_assets": "Upozornění: Pro uplatnění Štítku úložiště na dříve nahrané položky spusťte", @@ -196,6 +218,8 @@ "oauth_mobile_redirect_uri": "Mobilní přesměrování URI", "oauth_mobile_redirect_uri_override": "Přepsat mobilní přesměrování URI", "oauth_mobile_redirect_uri_override_description": "Povolit, pokud poskytovatel OAuth nepovoluje mobilní URI, například ''{callback}''", + "oauth_role_claim": "Deklarace Role", + "oauth_role_claim_description": "Automaticky udělit přístup správce na základě přítomnosti této deklarace. Deklarace může mít hodnotu 'user' nebo 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Správa nastavení OAuth přihlášení", "oauth_settings_more_details": "Další podrobnosti o této funkci naleznete v dokumentaci.", @@ -357,10 +381,12 @@ "admin_password": "Heslo správce", "administration": "Administrace", "advanced": "Pokročilé", + "advanced_settings_beta_timeline_subtitle": "Vyzkoušejte nové prostředí aplikace", + "advanced_settings_beta_timeline_title": "Beta verze časové osy", "advanced_settings_enable_alternate_media_filter_subtitle": "Tuto možnost použijte k filtrování médií během synchronizace na základě alternativních kritérií. Tuto možnost vyzkoušejte pouze v případě, že máte problémy s detekcí všech alb v aplikaci.", "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTÁLNÍ] Použít alternativní filtr pro synchronizaci alb zařízení", "advanced_settings_log_level_title": "Úroveň protokolování: {level}", - "advanced_settings_prefer_remote_subtitle": "U některých zařízení je načítání miniatur z prostředků v zařízení velmi pomalé. Aktivujte toto nastavení, aby se místo toho načítaly vzdálené obrázky.", + "advanced_settings_prefer_remote_subtitle": "U některých zařízení je načítání miniatur z lokálních prostředků velmi pomalé. Aktivujte toto nastavení, aby se místo toho načítaly vzdálené obrázky.", "advanced_settings_prefer_remote_title": "Preferovat vzdálené obrázky", "advanced_settings_proxy_headers_subtitle": "Definice hlaviček proxy serveru, které by měl Immich odesílat s každým síťovým požadavkem", "advanced_settings_proxy_headers_title": "Proxy hlavičky", @@ -379,6 +405,7 @@ "album_cover_updated": "Obal alba aktualizován", "album_delete_confirmation": "Opravdu chcete album {album} odstranit?", "album_delete_confirmation_description": "Pokud je toto album sdíleno, ostatní uživatelé k němu již nebudou mít přístup.", + "album_deleted": "Album smazáno", "album_info_card_backup_album_excluded": "VYLOUČENO", "album_info_card_backup_album_included": "ZAHRNUTO", "album_info_updated": "Informace o albu aktualizovány", @@ -388,6 +415,7 @@ "album_options": "Možnosti alba", "album_remove_user": "Odebrat uživatele?", "album_remove_user_confirmation": "Opravdu chcete odebrat uživatele {user}?", + "album_search_not_found": "Nebyla nalezena žádná alba odpovídající vašemu hledání", "album_share_no_users": "Zřejmě jste toto album sdíleli se všemi uživateli, nebo nemáte žádného uživatele, se kterým byste ho mohli sdílet.", "album_updated": "Album aktualizováno", "album_updated_setting_description": "Dostávat e-mailová oznámení o nových položkách sdíleného alba", @@ -407,6 +435,7 @@ "albums_default_sort_order": "Výchozí řazení alb", "albums_default_sort_order_description": "Výchozí řazení položek při vytváření nových alb.", "albums_feature_description": "Sbírky položek, které lze sdílet s ostatními uživateli.", + "albums_on_device_count": "Alba v zařízení ({count})", "all": "Vše", "all_albums": "Všechna alba", "all_people": "Všichni lidé", @@ -427,7 +456,8 @@ "app_settings": "Aplikace", "appears_in": "Vyskytuje se v", "archive": "Archiv", - "archive_or_unarchive_photo": "Archivovat nebo odarchivovat fotku", + "archive_action_prompt": "{count} přidaných do archivu", + "archive_or_unarchive_photo": "Přidat nebo odebrat fotku z archivu", "archive_page_no_archived_assets": "Nebyla nalezena žádná archivovaná média", "archive_page_title": "Archiv ({count})", "archive_size": "Velikost archivu", @@ -464,14 +494,13 @@ "assets": "Položky", "assets_added_count": "{count, plural, one {Přidána # položka} few {Přidány # položky} other {Přidáno # položek}}", "assets_added_to_album_count": "Do alba {count, plural, one {byla přidána # položka} few {byly přidány # položky} other {bylo přidáno # položek}}", - "assets_added_to_name_count": "{count, plural, one {Přidána # položka} few {Přidány # položky} other {Přidáno # položek}} do {hasName, select, true {alba {name}} other {nového alba}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Položku} other {Položky}} nelze přidat do alba", "assets_count": "{count, plural, one {# položka} few {# položky} other {# položek}}", "assets_deleted_permanently": "{count} položek trvale odstraněno", "assets_deleted_permanently_from_server": "{count} položek trvale odstraněno z Immich serveru", "assets_downloaded_failed": "{count, plural, one {Stažen # soubor - {error} souborů selhalo} few {Staženy # soubory - {error} souborů selhalo} other {Staženo # souborů - {error} souborů selhalo}}", "assets_downloaded_successfully": "{count, plural, one {Úspěšně stažen # soubor} few {Úspěšně staženy # soubory} other {Úspěšně staženo # souborů}}", - "assets_moved_to_trash_count": "Do koše {count, plural, one {přesunuta # položka} few {přesunuty # položky} other {přesunuto # položek}}", + "assets_moved_to_trash_count": "{count, plural, one {# položka přesunuta} few {# položky přesunuty} other {# položek přesunuto}} do koše", "assets_permanently_deleted_count": "Trvale {count, plural, one {smazána # položka} few {smazány # položky} other {smazáno # položek}}", "assets_removed_count": "{count, plural, one {Odstraněna # položka} few {Odstraněny # položky} other {Odstraněno # položek}}", "assets_removed_permanently_from_device": "{count} položek trvale odstraněno z vašeho zařízení", @@ -490,6 +519,7 @@ "back_close_deselect": "Zpět, zavřít nebo zrušit výběr", "background_location_permission": "Povolení polohy na pozadí", "background_location_permission_content": "Aby bylo možné přepínat sítě při běhu na pozadí, musí mít Immich *vždy* přístup k přesné poloze, aby mohl zjistit název Wi-Fi sítě", + "backup": "Záloha", "backup_album_selection_page_albums_device": "Alba v zařízení ({count})", "backup_album_selection_page_albums_tap": "Klepnutím na položku ji zahrnete, opětovným klepnutím ji vyloučíte", "backup_album_selection_page_assets_scatter": "Položky mohou být roztroušeny ve více albech. To umožňuje zahrnout nebo vyloučit alba během procesu zálohování.", @@ -553,6 +583,8 @@ "backup_options_page_title": "Nastavení záloh", "backup_setting_subtitle": "Správa nastavení zálohování na pozadí a na popředí", "backward": "Pozpátku", + "beta_sync": "Stav synchronizace (beta)", + "beta_sync_subtitle": "Správa nového systému synchronizace", "biometric_auth_enabled": "Biometrické ověřování je povoleno", "biometric_locked_out": "Jste vyloučeni z biometrického ověřování", "biometric_no_options": "Biometrické možnosti nejsou k dispozici", @@ -570,7 +602,7 @@ "cache_settings_clear_cache_button": "Vymazat vyrovnávací paměť", "cache_settings_clear_cache_button_title": "Vymaže vyrovnávací paměť aplikace. To výrazně ovlivní výkon aplikace, dokud se vyrovnávací paměť neobnoví.", "cache_settings_duplicated_assets_clear_button": "VYMAZAT", - "cache_settings_duplicated_assets_subtitle": "Fotografie a videa, které aplikace zařadila na černou listinu", + "cache_settings_duplicated_assets_subtitle": "Fotografie a videa, které aplikace ignoruje", "cache_settings_duplicated_assets_title": "Duplicitní položky ({count})", "cache_settings_statistics_album": "Knihovna náhledů", "cache_settings_statistics_full": "Kompletní fotografie", @@ -587,6 +619,7 @@ "cancel": "Zrušit", "cancel_search": "Zrušit vyhledávání", "canceled": "Zrušeno", + "canceling": "Rušení", "cannot_merge_people": "Nelze sloučit osoby", "cannot_undo_this_action": "Tuto akci nelze vrátit zpět!", "cannot_update_the_description": "Nelze aktualizovat popis", @@ -700,10 +733,11 @@ "current_server_address": "Aktuální adresa serveru", "custom_locale": "Vlastní lokalizace", "custom_locale_description": "Formátovat datumy a čísla podle jazyka a oblasti", + "custom_url": "Vlastní URL", "daily_title_text_date": "EEEE, d. MMMM", "daily_title_text_date_year": "EEEE, d. MMMM y", "dark": "Tmavý", - "darkTheme": "Přepnout tmavý motiv", + "dark_theme": "Přepnout tmavý motiv", "date_after": "Datum po", "date_and_time": "Datum a čas", "date_before": "Datum před", @@ -719,6 +753,8 @@ "default_locale": "Výchozí jazyk", "default_locale_description": "Formátovat datumy a čísla podle místního prostředí prohlížeče", "delete": "Smazat", + "delete_action_confirmation_message": "Opravdu chcete odstranit tuto položku? Tato akce přesune položku do serverového koše a zeptá se vás, zda ji chcete odstranit lokálně", + "delete_action_prompt": "{count} smazáno", "delete_album": "Smazat album", "delete_api_key_prompt": "Opravdu chcete tento API klíč odstranit?", "delete_dialog_alert": "Tyto položky budou trvale smazány z aplikace Immich i z vašeho zařízení", @@ -732,9 +768,12 @@ "delete_key": "Smazat klíč", "delete_library": "Smazat knihovnu", "delete_link": "Smazat odkaz", + "delete_local_action_prompt": "{count} smazáno lokálně", "delete_local_dialog_ok_backed_up_only": "Smazat pouze zálohované", "delete_local_dialog_ok_force": "Přesto smazat", - "delete_others": "Odstranit ostatní", + "delete_others": "Smazat ostatní", + "delete_permanently": "Trvale smazat", + "delete_permanently_action_prompt": "{count} trvale smazáno", "delete_shared_link": "Smazat sdílený odkaz", "delete_shared_link_dialog_title": "Odstranit sdílený odkaz", "delete_tag": "Smazat značku", @@ -745,6 +784,7 @@ "description": "Popis", "description_input_hint_text": "Přidat popis...", "description_input_submit_error": "Chyba aktualizace popisu, další podrobnosti najdete v logu", + "deselect_all": "Zrušit výběr všech", "details": "Podrobnosti", "direction": "Směr", "disabled": "Zakázáno", @@ -762,6 +802,7 @@ "documentation": "Dokumentace", "done": "Hotovo", "download": "Stáhnout", + "download_action_prompt": "Stahování {count} položek", "download_canceled": "Stahování zrušeno", "download_complete": "Stahování kompletní", "download_enqueue": "Stahování ve frontě", @@ -788,6 +829,7 @@ "edit": "Upravit", "edit_album": "Upravit album", "edit_avatar": "Upravit avatar", + "edit_birthday": "Upravit datum narození", "edit_date": "Upravit datum", "edit_date_and_time": "Upravit datum a čas", "edit_description": "Upravit popis", @@ -799,6 +841,7 @@ "edit_key": "Upravit klíč", "edit_link": "Upravit odkaz", "edit_location": "Upravit polohu", + "edit_location_action_prompt": "{count} upravených poloh", "edit_location_dialog_title": "Poloha", "edit_name": "Upravit jméno", "edit_people": "Upravit lidi", @@ -817,6 +860,7 @@ "empty_trash": "Vyprázdnit koš", "empty_trash_confirmation": "Opravdu chcete vysypat koš? Tím se z Immiche trvale odstraní všechny položky v koši.\nTuto akci nelze vrátit zpět!", "enable": "Povolit", + "enable_backup": "Povolit zálohování", "enable_biometric_auth_description": "Zadejte váš PIN kód pro povolení biometrického ověřování", "enabled": "Povoleno", "end_date": "Konečné datum", @@ -861,7 +905,7 @@ "failed_to_load_people": "Chyba načítání osob", "failed_to_remove_product_key": "Nepodařilo se odebrat klíč produktu", "failed_to_stack_assets": "Nepodařilo se seskupit položky", - "failed_to_unstack_assets": "Nepodařilo se rozložit položky", + "failed_to_unstack_assets": "Nepodařilo se zrušit seskupení položek", "failed_to_update_notification_status": "Nepodařilo se aktualizovat stav oznámení", "import_path_already_exists": "Tato cesta importu již existuje.", "incorrect_email_or_password": "Nesprávný e-mail nebo heslo", @@ -876,7 +920,7 @@ "unable_to_add_partners": "Nelze přidat partnery", "unable_to_add_remove_archive": "Nelze {archived, select, true {odstranit položku z} other {přidat položku do}} archivu", "unable_to_add_remove_favorites": "Nelze {favorite, select, true {oblíbit položku} other {zrušit oblíbení položky}}", - "unable_to_archive_unarchive": "Nelze {archived, select, true {archivovat} other {odarchivovat}}", + "unable_to_archive_unarchive": "Nelze {archived, select, true {archivovat} other {odebrat z archivu}}", "unable_to_change_album_user_role": "Nelze změnit roli uživatele alba", "unable_to_change_date": "Nelze změnit datum", "unable_to_change_description": "Nelze změnit popis", @@ -953,6 +997,7 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "Přidat popis...", + "exif_bottom_sheet_description_error": "Chyba při aktualizaci popisu", "exif_bottom_sheet_details": "PODROBNOSTI", "exif_bottom_sheet_location": "POLOHA", "exif_bottom_sheet_people": "LIDÉ", @@ -973,6 +1018,8 @@ "explorer": "Průzkumník", "export": "Export", "export_as_json": "Exportovat jako JSON", + "export_database": "Exportovat databázi", + "export_database_description": "Exportovat databázi SQLite", "extension": "Přípona", "external": "Externí", "external_libraries": "Externí knihovny", @@ -984,6 +1031,7 @@ "failed_to_load_assets": "Nepodařilo se načíst položky", "failed_to_load_folder": "Nepodařilo se načíst složku", "favorite": "Oblíbit", + "favorite_action_prompt": "{count} přidáno do Oblíbených", "favorite_or_unfavorite_photo": "Oblíbit nebo zrušit oblíbení fotky", "favorites": "Oblíbené", "favorites_page_no_favorites": "Nebyla nalezena žádná oblíbená média", @@ -1023,6 +1071,9 @@ "haptic_feedback_switch": "Povolit dotykovou zpětnou vazbu", "haptic_feedback_title": "Dotyková zpětná vazba", "has_quota": "Má kvótu", + "hash_asset": "Hash položky", + "hashed_assets": "Hashované položky", + "hashing": "Hashování", "header_settings_add_header_tip": "Přidat hlavičku", "header_settings_field_validator_msg": "Hodnota nemůže být prázdná", "header_settings_header_name_input": "Název hlavičky", @@ -1055,6 +1106,7 @@ "host": "Hostitel", "hour": "Hodina", "id": "ID", + "idle": "Nečinnost", "ignore_icloud_photos": "Ignorovat fotografie na iCloudu", "ignore_icloud_photos_description": "Fotografie uložené na iCloudu se nebudou nahrávat na Immich server", "image": "Obrázek", @@ -1112,6 +1164,7 @@ "language_no_results_title": "Nebyly nalezeny žádné jazyky", "language_search_hint": "Vyhledat jazyk...", "language_setting_description": "Vyberte upřednostňovaný jazyk", + "large_files": "Velké soubory", "last_seen": "Naposledy viděno", "latest_version": "Nejnovější verze", "latitude": "Zeměpisná šířka", @@ -1127,16 +1180,18 @@ "library_page_sort_created": "Naposledy vytvořené", "library_page_sort_last_modified": "Naposledy upraveno", "library_page_sort_title": "Podle názvu alba", + "licenses": "Licence", "light": "Světlý", "like_deleted": "Lajk smazán", "link_motion_video": "Připojit pohyblivé video", - "link_options": "Možnosti odkazu", "link_to_oauth": "Propojit s OAuth", "linked_oauth_account": "Propojený OAuth účet", "list": "Seznam", "loading": "Načítání", "loading_search_results_failed": "Načítání výsledků vyhledávání se nezdařilo", + "local": "Místní", "local_asset_cast_failed": "Nelze odeslat položku, která není nahraná na serveru", + "local_assets": "Místní položky", "local_network": "Místní síť", "local_network_sheet_info": "Aplikace se při použití zadané sítě Wi-Fi připojí k serveru prostřednictvím tohoto URL", "location_permission": "Oprávnění polohy", @@ -1193,8 +1248,7 @@ "manage_your_devices": "Správa přihlášených zařízení", "manage_your_oauth_connection": "Správa OAuth propojení", "map": "Mapa", - "map_assets_in_bound": "{count} fotka", - "map_assets_in_bounds": "{count} fotek", + "map_assets_in_bounds": "{count, plural, one {# fotka} few{# fotky} other {# fotek}}", "map_cannot_get_user_location": "Nelze zjistit polohu uživatele", "map_location_dialog_yes": "Ano", "map_location_picker_page_use_location": "Použít tuto polohu", @@ -1246,10 +1300,11 @@ "more": "Více", "move": "Přesunout", "move_off_locked_folder": "Přesunout z uzamčené složky", + "move_to_lock_folder_action_prompt": "{count} přidaných do uzamčené složky", "move_to_locked_folder": "Přesunout do uzamčené složky", "move_to_locked_folder_confirmation": "Tyto fotky a videa budou odstraněny ze všech alb a bude je možné zobrazit pouze v uzamčené složce", - "moved_to_archive": "{count, plural, one {Přesunuta # položka} few {Přesunuty # položky} other {Přesunuto # položek}} do archivu", - "moved_to_library": "{count, plural, one {Přesunuta # položka} few {Přesunuty # položky} other {Přesunuto # položek}} do knihovny", + "moved_to_archive": "{count, plural, one {# položka přesunuta} few {# položky přesunuty} other {# položek přesunuto}} do archivu", + "moved_to_library": "{count, plural, one {# položka přesunuta} few {# položky přesunuty} other {# položek přesunuto}} do knihovny", "moved_to_trash": "Přesunuto do koše", "multiselect_grid_edit_date_time_err_read_only": "Nelze upravit datum položek pouze pro čtení, přeskakuji", "multiselect_grid_edit_gps_err_read_only": "Nelze upravit polohu položek pouze pro čtení, přeskakuji", @@ -1292,6 +1347,7 @@ "no_results": "Žádné výsledky", "no_results_description": "Zkuste použít synonymum nebo obecnější klíčové slovo", "no_shared_albums_message": "Vytvořte si album a sdílejte fotografie a videa s lidmi ve své síti", + "no_uploads_in_progress": "Neprobíhá žádné nahrávání", "not_in_any_album": "Bez alba", "not_selected": "Není vybráno", "note_apply_storage_label_to_previously_uploaded assets": "Upozornění: Chcete-li použít štítek úložiště na dříve nahrané položky, spusťte příkaz", @@ -1329,6 +1385,7 @@ "original": "originál", "other": "Ostatní", "other_devices": "Ostatní zařízení", + "other_entities": "Ostatní entity", "other_variables": "Další proměnné", "owned": "Vlastní", "owner": "Vlastník", @@ -1460,6 +1517,7 @@ "purchase_server_description_2": "Stav podporovatele", "purchase_server_title": "Server", "purchase_settings_server_activated": "Produktový klíč serveru spravuje správce", + "queue_status": "Ve frontě {count}/{total}", "rating": "Hodnocení hvězdičkami", "rating_clear": "Vyčistit hodnocení", "rating_count": "{count, plural, one {# hvězdička} few {# hvězdičky} other {# hvězdček}}", @@ -1488,6 +1546,8 @@ "refreshing_faces": "Obnovování obličejů", "refreshing_metadata": "Obnovování metadat", "regenerating_thumbnails": "Regenerace miniatur", + "remote": "Vzdálený", + "remote_assets": "Vzdálené položky", "remove": "Odstranit", "remove_assets_album_confirmation": "Opravdu chcete z alba odstranit {count, plural, one {# položku} few {# položky} other {# položek}}?", "remove_assets_shared_link_confirmation": "Opravdu chcete ze sdíleného odkazu odstranit {count, plural, one {# položku} few {# položky} other {# položek}}?", @@ -1495,7 +1555,9 @@ "remove_custom_date_range": "Odstranit vlastní rozsah datumů", "remove_deleted_assets": "Odstranit offline soubory", "remove_from_album": "Odstranit z alba", + "remove_from_album_action_prompt": "{count} odstraněných z alba", "remove_from_favorites": "Odstranit z oblíbených", + "remove_from_lock_folder_action_prompt": "{count} odebraných z uzamčené složky", "remove_from_locked_folder": "Odstranit z uzamčené složky", "remove_from_locked_folder_confirmation": "Opravdu chcete tyto fotky a videa přesunout z uzamčené složky? Budou viditelné ve vaší knihovně.", "remove_from_shared_link": "Odstranit ze sdíleného odkazu", @@ -1523,19 +1585,25 @@ "reset_password": "Obnovit heslo", "reset_people_visibility": "Obnovit viditelnost lidí", "reset_pin_code": "Resetovat PIN kód", + "reset_sqlite": "Obnovit SQLite databázi", + "reset_sqlite_confirmation": "Jste si jisti, že chcete obnovit SQLite databázi? Pro opětovnou synchronizaci dat se budete muset odhlásit a znovu přihlásit", + "reset_sqlite_success": "Obnovení SQLite databáze proběhlo úspěšně", "reset_to_default": "Obnovit výchozí nastavení", "resolve_duplicates": "Vyřešit duplicity", "resolved_all_duplicates": "Vyřešeny všechny duplicity", "restore": "Obnovit", "restore_all": "Obnovit vše", + "restore_trash_action_prompt": "{count} obnoveno z koše", "restore_user": "Obnovit uživatele", "restored_asset": "Položka obnovena", "resume": "Pokračovat", "retry_upload": "Opakování nahrávání", "review_duplicates": "Kontrola duplicit", + "review_large_files": "Kontrola velkých souborů", "role": "Role", "role_editor": "Editor", "role_viewer": "Divák", + "running": "Probíhá", "save": "Uložit", "save_to_gallery": "Uložit do galerie", "saved_api_key": "API klíč uložen", @@ -1667,6 +1735,7 @@ "settings_saved": "Nastavení uloženo", "setup_pin_code": "Nastavení PIN kódu", "share": "Sdílet", + "share_action_prompt": "Sdíleno {count} položek", "share_add_photos": "Přidat fotografie", "share_assets_selected": "{count} vybráno", "share_dialog_preparing": "Připravuji...", @@ -1688,6 +1757,7 @@ "shared_link_clipboard_copied_massage": "Zkopírováno do schránky", "shared_link_clipboard_text": "Odkaz: {link}\nHeslo: {password}", "shared_link_create_error": "Chyba při vytváření sdíleného odkazu", + "shared_link_custom_url_description": "Přístup k tomuto sdílenému odkazu pomocí vlastního URL", "shared_link_edit_description_hint": "Zadejte popis sdílení", "shared_link_edit_expire_after_option_day": "1 den", "shared_link_edit_expire_after_option_days": "{count} dní", @@ -1713,6 +1783,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Spravovat sdílené odkazy", "shared_link_options": "Možnosti sdíleného odkazu", + "shared_link_password_description": "Vyžadovat heslo pro přístup k tomuto sdílenému odkazu", "shared_links": "Sdílené odkazy", "shared_links_description": "Sdílet fotky a videa pomocí odkazu", "shared_photos_and_videos_count": "{assetCount, plural, one {# sdílená fotografie a video.} few {# sdílené fotografie a videa.} other {# sdílených fotografií a videí.}}", @@ -1768,6 +1839,7 @@ "sort_title": "Název alba", "source": "Zdroj", "stack": "Seskupit", + "stack_action_prompt": "{count} seskupeno", "stack_duplicates": "Seskupit duplicity", "stack_select_one_photo": "Vyberte jednu hlavní fotografii pro seskupení", "stack_selected_photos": "Seskupení vybraných fotografií", @@ -1787,6 +1859,7 @@ "storage_quota": "Kvóta úložiště", "storage_usage": "Využito {used} z {available}", "submit": "Odeslat", + "success": "Úspěch", "suggestions": "Návrhy", "sunrise_on_the_beach": "Východ slunce na pláži", "support": "Podpora", @@ -1796,6 +1869,8 @@ "sync": "Synchronizovat", "sync_albums": "Synchronizovat alba", "sync_albums_manual_subtitle": "Synchronizovat všechna nahraná videa a fotografie do vybraných záložních alb", + "sync_local": "Synchronizovat místní", + "sync_remote": "Synchronizovat vzdálené", "sync_upload_album_setting_subtitle": "Vytvořit a nahrát fotografie a videa do vybraných alb na Immich", "tag": "Značka", "tag_assets": "Přiřadit značku", @@ -1806,6 +1881,7 @@ "tag_updated": "Aktualizována značka: {tag}", "tagged_assets": "Přiřazena značka {count, plural, one {# položce} other {# položkám}}", "tags": "Značky", + "tap_to_run_job": "Klepnutím na spustíte úlohu", "template": "Šablona", "theme": "Motiv", "theme_selection": "Výběr motivu", @@ -1838,6 +1914,7 @@ "total": "Celkem", "total_usage": "Celkové využití", "trash": "Koš", + "trash_action_prompt": "{count} přesunutých do koše", "trash_all": "Vyhodit vše", "trash_count": "Vyhodit {count, number}", "trash_delete_asset": "Vyhodit/Smazat položku", @@ -1854,10 +1931,12 @@ "type": "Typ", "unable_to_change_pin_code": "Nelze změnit PIN kód", "unable_to_setup_pin_code": "Nelze nastavit PIN kód", - "unarchive": "Odarchivovat", + "unarchive": "Odebrat z archivu", + "unarchive_action_prompt": "{count} odstraněných z archivu", "unarchived_count": "{count, plural, one {Odarchivována #} few {Odarchivovány #} other {Odarchivováno #}}", "undo": "Vrátit zpět", "unfavorite": "Zrušit oblíbení", + "unfavorite_action_prompt": "{count} odstraněných z oblíbených", "unhide_person": "Zrušit skrytí osoby", "unknown": "Neznámý", "unknown_country": "Neznámá země", @@ -1875,15 +1954,20 @@ "unselect_all_duplicates": "Zrušit výběr všech duplicit", "unselect_all_in": "Zrušit výběr ve skupině {group}", "unstack": "Zrušit seskupení", - "unstacked_assets_count": "{count, plural, one {Rozložená # položka} few {Rozložené # položky} other {Rozložených # položiek}}", + "unstack_action_prompt": "{count} seskupených zrušeno", + "unstacked_assets_count": "{count, plural, one {Rozložená # položka} few {Rozložené # položky} other {Rozložených # položek}}", + "untagged": "Neoznačeno", "up_next": "To je prozatím vše", "updated_at": "Aktualizováno", "updated_password": "Heslo aktualizováno", "upload": "Nahrát", + "upload_action_prompt": "{count} ve frontě pro nahrání", "upload_concurrency": "Souběžnost nahrávání", + "upload_details": "Detaily nahrávání", "upload_dialog_info": "Chcete zálohovat vybrané položky na server?", "upload_dialog_title": "Nahrát položku", "upload_errors": "Nahrávání bylo dokončeno s {count, plural, one {# chybou} other {# chybami}}, obnovte stránku pro zobrazení nových položek.", + "upload_finished": "Nahrávání dokončeno", "upload_progress": "Zbývá {remaining, number} - Zpracováno {processed, number}/{total, number}", "upload_skipped_duplicates": "{count, plural, one {Přeskočena # duplicitní položka} few {Přeskočeny # duplicitní položky} other {Přeskočeno # duplicitních položek}}", "upload_status_duplicates": "Duplicity", @@ -1892,6 +1976,7 @@ "upload_success": "Nahrání proběhlo úspěšně, obnovením stránky se zobrazí nově nahrané položky.", "upload_to_immich": "Nahrát do Immich ({count})", "uploading": "Nahrávání", + "uploading_media": "Nahrávání médií", "url": "URL", "usage": "Využití", "use_biometric": "Použít biometrické údaje", @@ -1912,6 +1997,7 @@ "user_usage_stats_description": "Zobrazit statistiky používání účtu", "username": "Uživateleské jméno", "users": "Uživatelé", + "users_added_to_album_count": "{count, plural, one {Přidán # uživatel} few {Přidány # uživatelé} other {Přidáno # uživatelů}} do alba", "utilities": "Nástroje", "validate": "Ověřit", "validate_endpoint_error": "Zadejte platné URL", @@ -1930,6 +2016,7 @@ "view_album": "Zobrazit album", "view_all": "Zobrazit vše", "view_all_users": "Zobrazit všechny uživatele", + "view_details": "Zobrazit podrobnosti", "view_in_timeline": "Zobrazit na časové ose", "view_link": "Zobrazit odkaz", "view_links": "Zobrazit odkazy", @@ -1941,7 +2028,7 @@ "view_user": "Zobrazit uživatele", "viewer_remove_from_stack": "Odstranit ze zásobníku", "viewer_stack_use_as_main_asset": "Použít jako hlavní položku", - "viewer_unstack": "Rozbalit zásobník", + "viewer_unstack": "Zrušit zásobník", "visibility_changed": "Viditelnost změněna u {count, plural, one {# osoby} few {# osob} other {# lidí}}", "waiting": "Čekající", "warning": "Upozornění", diff --git a/i18n/da.json b/i18n/da.json index 3273a2d553..4e9846791b 100644 --- a/i18n/da.json +++ b/i18n/da.json @@ -34,6 +34,7 @@ "added_to_favorites_count": "Tilføjet {count, number} til favoritter", "admin": { "add_exclusion_pattern_description": "Tilføj udelukkelsesmønstre. Globbing ved hjælp af *, ** og ? understøttes. For at ignorere alle filer i enhver mappe med navnet \"Raw\", brug \"**/Raw/**\". For at ignorere alle filer, der slutter på \".tif\", brug \"**/*.tif\". For at ignorere en absolut sti, brug \"/sti/til/ignoreret/**\".", + "admin_user": "Administrator bruger", "asset_offline_description": "Denne eksterne biblioteksressource findes ikke længere på disken og er blevet flyttet til papirkurven. Hvis filen blev flyttet inde i biblioteket, skal du tjekke din tidslinje for den nye tilsvarende ressource. For at gendanne denne ressource skal du sikre, at filstien nedenfor kan tilgås af Immich og scanne biblioteket.", "authentication_settings": "Godkendelsesindstillinger", "authentication_settings_description": "Administrer adgangskode, OAuth og andre godkendelsesindstillinger", @@ -165,6 +166,20 @@ "metadata_settings_description": "Håndtér metadataindstillinger", "migration_job": "Migrering", "migration_job_description": "Migrér miniaturebilleder for aktiver og ansigter til den seneste mappestruktur", + "nightly_tasks_cluster_faces_setting_description": "Kør ansigtsgenkendelse på nye ansigter", + "nightly_tasks_cluster_new_faces_setting": "Gruppér nye ansigter", + "nightly_tasks_database_cleanup_setting": "Databaseoprydning opgaver", + "nightly_tasks_database_cleanup_setting_description": "Ryd op i gamle, udløbne data fra databasen", + "nightly_tasks_generate_memories_setting": "Generer minder", + "nightly_tasks_generate_memories_setting_description": "Skab nye minder ud fra dine medier", + "nightly_tasks_missing_thumbnails_setting": "Generer manglende miniaturebilleder", + "nightly_tasks_missing_thumbnails_setting_description": "Sæt medier uden thumbnails i kø til generering af thumbnails", + "nightly_tasks_settings": "Indstillinger for opgaver om natten", + "nightly_tasks_settings_description": "Administrér opgaver om natten", + "nightly_tasks_start_time_setting": "Starttidspunkt", + "nightly_tasks_start_time_setting_description": "Tidspunktet hvor serveren begynder at køre opgaver om natten", + "nightly_tasks_sync_quota_usage_setting": "Synkronisér kvoteforbrug", + "nightly_tasks_sync_quota_usage_setting_description": "Opdater brugerens lagerkvote baseret på aktuelt forbrug", "no_paths_added": "Ingen stier tilføjet", "no_pattern_added": "Intet mønster tilføjet", "note_apply_storage_label_previous_assets": "Bemærk: For at anvende Lagringsmærkatet på tidligere uploadede mediefiler, kør", @@ -203,7 +218,7 @@ "oauth_storage_quota_claim": "Lagringskvotefordring", "oauth_storage_quota_claim_description": "Sæt automatisk brugeres lagringskvote til denne nye fordrings værdi.", "oauth_storage_quota_default": "Standard lagringskvote (GiB)", - "oauth_storage_quota_default_description": "Kvote i GiB som bruges, når der ikke bliver oplyst en fordring (Indtast 0 for uendelig kvote).", + "oauth_storage_quota_default_description": "Kvote i GiB som bruges, når der ikke bliver oplyst en fordring.", "oauth_timeout": "Forespørgslen udløb", "oauth_timeout_description": "Udløbstid for forespørgsel i milisekunder", "password_enable_description": "Log ind med email og adgangskode", @@ -462,7 +477,6 @@ "assets": "elementer", "assets_added_count": "Tilføjet {count, plural, one {# mediefil} other {# mediefiler}}", "assets_added_to_album_count": "{count, plural, one {# mediefil} other {# mediefiler}} tilføjet til albummet", - "assets_added_to_name_count": "Tilføjet {count, plural, one {# mediefil} other {# mediefiler}} til {hasName, select, true {{name}} other {nyt album}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Billed} other {Billeder}} kan ikke blive tilføjet til album", "assets_count": "{count, plural, one {# mediefil} other {# mediefiler}}", "assets_deleted_permanently": "{count} element(er) blev fjernet permanent", @@ -488,6 +502,7 @@ "back_close_deselect": "Tilbage, luk eller fravælg", "background_location_permission": "Tilladelse til baggrundsplacering", "background_location_permission_content": "For at skifte netværk, når appen kører i baggrunden, skal Immich *altid* have præcis placeringsadgang, så appen kan læse WiFi-netværkets navn", + "backup": "Sikkerhedskopier", "backup_album_selection_page_albums_device": "Albummer på enheden ({count})", "backup_album_selection_page_albums_tap": "Tryk en gang for at inkludere, tryk to gange for at ekskludere", "backup_album_selection_page_assets_scatter": "Elementer kan være spredt på tværs af flere albummer. Albummer kan således inkluderes eller udelukkes under sikkerhedskopieringsprocessen.", @@ -701,7 +716,6 @@ "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Mørk", - "darkTheme": "Skift til mørkt tema", "date_after": "Dato efter", "date_and_time": "Dato og klokkeslæt", "date_before": "Dato før", @@ -1128,7 +1142,6 @@ "light": "Lys", "like_deleted": "Ligesom slettet", "link_motion_video": "Link bevægelsesvideo", - "link_options": "Link-indstillinger", "link_to_oauth": "Link til OAuth", "linked_oauth_account": "Tilsluttet OAuth-konto", "list": "Liste", @@ -1190,7 +1203,6 @@ "manage_your_devices": "Administrér dine enheder der er logget ind", "manage_your_oauth_connection": "Administrér din OAuth-tilslutning", "map": "Kort", - "map_assets_in_bound": "{count} billede", "map_assets_in_bounds": "{count} billeder", "map_cannot_get_user_location": "Kan ikke finde brugerens placering", "map_location_dialog_yes": "Ja", diff --git a/i18n/de.json b/i18n/de.json index bbcd9c569c..651f68792e 100644 --- a/i18n/de.json +++ b/i18n/de.json @@ -14,6 +14,7 @@ "add_a_location": "Standort hinzufügen", "add_a_name": "Name hinzufügen", "add_a_title": "Titel hinzufügen", + "add_birthday": "Geburtsdatum hinzufügen", "add_endpoint": "Endpunkt hinzufügen", "add_exclusion_pattern": "Ausschlussmuster hinzufügen", "add_import_path": "Importpfad hinzufügen", @@ -41,9 +42,16 @@ "authentication_settings_disable_all": "Bist du sicher, dass du alle Anmeldemethoden deaktivieren willst? Die Anmeldung wird vollständig deaktiviert.", "authentication_settings_reenable": "Nutze einen Server-Befehl zur Reaktivierung.", "background_task_job": "Hintergrundaufgaben", - "backup_database": "Datenbanksicherung regelmäßig erstellen", + "backup_database": "Datenbanksicherung erstellen", "backup_database_enable_description": "Datenbank regelmäßig sichern", "backup_keep_last_amount": "Anzahl der aufzubewahrenden früheren Backups", + "backup_onboarding_1_description": "Offsite-Kopie in der Cloud oder an einem anderen physischen Ort.", + "backup_onboarding_2_description": "Lokale Kopien auf verschiedenen Geräten. Dazu gehören die Hauptdateien und eine lokale Sicherung dieser Dateien.", + "backup_onboarding_3_description": "3 komplette Kopien deiner Daten, inkl. der Originaldateien. Dies umfasst 1 Kopie an einem anderen Ort und 2 lokale Kopie.", + "backup_onboarding_description": "Eine 3-2-1 Backup-Strategie wird empfohlen, um deine Daten zu schützen. Du solltest sowohl Kopien deiner hochgeladenen Fotos/Videos als auch der Immich-Datenbank aufbewahren, um eine umfassende Backup-Lösung zu haben.", + "backup_onboarding_footer": "Weitere Informationen zum Sichern von Immich findest du in der Dokumentation.", + "backup_onboarding_parts_title": "Eine 3-2-1-Sicherung umfasst:", + "backup_onboarding_title": "Backups", "backup_settings": "Einstellungen für Datenbanksicherung", "backup_settings_description": "Einstellungen zur regelmäßigen Sicherung der Datenbank. Hinweis: Diese Jobs werden nicht überwacht und du wirst nicht über Fehler informiert.", "cleared_jobs": "Folgende Aufgaben zurückgesetzt: {job}", @@ -56,9 +64,9 @@ "confirm_user_pin_code_reset": "Bist du sicher, dass du den PIN Code von {user} zurücksetzen möchtest?", "create_job": "Aufgabe erstellen", "cron_expression": "Cron Zeitangabe", - "cron_expression_description": "Setze ein Intervall für die Sicherung mittels cron. Hilfe mit dem Format bietet dir dabei z.B der Crontab Guru", + "cron_expression_description": "Setze ein Intervall für die Sicherung mittels cron. Hilfe mit dem Format bietet dir dabei z. B. der Crontab Guru", "cron_expression_presets": "Nützliche Zeitangaben für Cron", - "disable_login": "Login deaktvieren", + "disable_login": "Login deaktivieren", "duplicate_detection_job_description": "Diese Aufgabe führt das maschinelle Lernen für jede Datei aus, um Duplikate zu finden. Diese Aufgabe beruht auf der intelligenten Suche", "exclusion_pattern_description": "Mit Ausschlussmustern können Dateien und Ordner beim Scannen Ihrer Bibliothek ignoriert werden. Dies ist nützlich, wenn du Ordner hast, die Dateien enthalten, die du nicht importieren möchtest, wie z. B. RAW-Dateien.", "external_library_management": "Verwaltung externer Bibliotheken", @@ -120,7 +128,7 @@ "machine_learning_duplicate_detection_setting_description": "Verwendung von CLIP-Embeddings zum Erkennen möglicher Duplikate", "machine_learning_enabled": "Maschinelles Lernen aktivieren", "machine_learning_enabled_description": "Wenn diese Option deaktiviert ist, werden alle ML-Funktionen unabhängig von den unten aufgeführten Einstellungen deaktiviert.", - "machine_learning_facial_recognition": "Gesichtsidentifikation", + "machine_learning_facial_recognition": "Gesichtsidentifizierung", "machine_learning_facial_recognition_description": "Erkenne, identifiziere und gruppiere Gesichter in Bildern", "machine_learning_facial_recognition_model": "Gesichtserkennungs-Modell", "machine_learning_facial_recognition_model_description": "Die Modelle sind in absteigender Reihenfolge ihrer Größe aufgeführt. Größere Modelle sind langsamer und verbrauchen mehr Speicher, liefern aber bessere Ergebnisse. Bitte beachte dabei, dass du die Gesichtserkennungsaufgabe für alle Bilder neu starten musst, wenn du ein Modell änderst.", @@ -166,6 +174,20 @@ "metadata_settings_description": "Metadaten-Einstellungen verwalten", "migration_job": "Migration", "migration_job_description": "Diese Aufgabe migriert Miniaturansichten für Dateien und Gesichter in die neueste Ordnerstruktur", + "nightly_tasks_cluster_faces_setting_description": "Gesichtsidentifikation auf neu erkannten Gesichtern ausführen", + "nightly_tasks_cluster_new_faces_setting": "Neue Gesichter gruppieren", + "nightly_tasks_database_cleanup_setting": "Datenbankbereinigungs-Aufgaben", + "nightly_tasks_database_cleanup_setting_description": "Alte, abgelaufene Daten aus der Datenbank bereinigen", + "nightly_tasks_generate_memories_setting": "Erinnerungen generieren", + "nightly_tasks_generate_memories_setting_description": "Neue Erinnerungen aus Dateien erstellen", + "nightly_tasks_missing_thumbnails_setting": "Fehlende Miniaturansichten generieren", + "nightly_tasks_missing_thumbnails_setting_description": "Dateien ohne Miniaturansicht in die Warteschlange zur Miniaturansicht-Generierung hinzufügen", + "nightly_tasks_settings": "Einstellungen für nächtliche Aufgaben", + "nightly_tasks_settings_description": "Nächtliche Aufgaben verwalten", + "nightly_tasks_start_time_setting": "Startzeit", + "nightly_tasks_start_time_setting_description": "Die Zeit, zu der der Server mit der Ausführung der nächtlichen Aufgaben beginnt", + "nightly_tasks_sync_quota_usage_setting": "Kontingentnutzung synchronisieren", + "nightly_tasks_sync_quota_usage_setting_description": "Benutzerspeicherkontingent basierend auf der aktuellen Nutzung aktualisieren", "no_paths_added": "Keine Pfade hinzugefügt", "no_pattern_added": "Kein Ausschlussmuster hinzugefügt", "note_apply_storage_label_previous_assets": "Hinweis: Um den Speicherpfad auf die vorher hochgeladenen Dateien anzuwenden, starte den", @@ -196,6 +218,8 @@ "oauth_mobile_redirect_uri": "Mobile Umleitungs-URI", "oauth_mobile_redirect_uri_override": "Mobile Umleitungs-URI überschreiben", "oauth_mobile_redirect_uri_override_description": "Einschalten, wenn der OAuth-Anbieter keine mobile URI wie ''{callback}'' erlaubt", + "oauth_role_claim": "Rollen-Claim", + "oauth_role_claim_description": "Gewähre automatisch Admin-Zugriff basierend auf dem Vorhandensein dieses Claims. Der Claim kann entweder 'user' oder 'admin' sein.", "oauth_settings": "OAuth", "oauth_settings_description": "OAuth-Anmeldeeinstellungen verwalten", "oauth_settings_more_details": "Weitere Informationen zu dieser Funktion findest du in der Dokumentation.", @@ -244,6 +268,7 @@ "storage_template_migration_info": "Die Speichervorlage wird alle Dateierweiterungen in Kleinbuchstaben umwandeln. Vorlagenänderungen gelten nur für neue Dateien. Um die Vorlage rückwirkend auf bereits hochgeladene Assets anzuwenden, führe den {job} aus.", "storage_template_migration_job": "Speichervorlagenmigrations-Aufgabe", "storage_template_more_details": "Weitere Details zu dieser Funktion findest du unter Speichervorlage und dessen Implikationen", + "storage_template_onboarding_description_v2": "Wenn aktiviert, werden Dateien automatisch nach einer benutzerdefinierten Vorlage organisiert. Für mehr Informationen siehe die Dokumentation.", "storage_template_path_length": "Ungefähres Pfadlängen-Limit: {length, number}/{limit, number}", "storage_template_settings": "Speichervorlage", "storage_template_settings_description": "Die Ordnerstruktur und den Dateinamen der hochgeladenen Datei verwalten", @@ -356,17 +381,19 @@ "admin_password": "Administrator Passwort", "administration": "Verwaltung", "advanced": "Erweitert", + "advanced_settings_beta_timeline_subtitle": "Probier die neue App-Erfahrung aus", + "advanced_settings_beta_timeline_title": "Beta-Timeline", "advanced_settings_enable_alternate_media_filter_subtitle": "Verwende diese Option, um Medien während der Synchronisierung nach anderen Kriterien zu filtern. Versuchen dies nur, wenn Probleme mit der Erkennung aller Alben durch die App auftreten.", "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTELL] Benutze alternativen Filter für Synchronisierung der Gerätealben", "advanced_settings_log_level_title": "Log-Level: {level}", - "advanced_settings_prefer_remote_subtitle": "Einige Geräte sind sehr langsam beim Laden von Miniaturbildern direkt aus dem Gerät. Aktivieren Sie diese Einstellung, um stattdessen die Server-Bilder zu laden.", + "advanced_settings_prefer_remote_subtitle": "Einige Geräte sind sehr langsam beim Laden von lokalen Vorschaubildern. Aktivieren Sie diese Einstellung, um stattdessen die Server-Bilder zu laden.", "advanced_settings_prefer_remote_title": "Server-Bilder bevorzugen", "advanced_settings_proxy_headers_subtitle": "Definiere einen Proxy-Header, den Immich bei jeder Netzwerkanfrage mitschicken soll", "advanced_settings_proxy_headers_title": "Proxy-Headers", "advanced_settings_self_signed_ssl_subtitle": "Verifizierung von SSL-Zertifikaten vom Server überspringen. Notwendig bei selbstsignierten Zertifikaten.", "advanced_settings_self_signed_ssl_title": "Selbstsignierte SSL-Zertifikate erlauben", "advanced_settings_sync_remote_deletions_subtitle": "Automatisches Löschen oder Wiederherstellen einer Datei auf diesem Gerät, wenn diese Aktion im Web durchgeführt wird", - "advanced_settings_sync_remote_deletions_title": "Synchrone Remote-Löschungen [Experimentell]", + "advanced_settings_sync_remote_deletions_title": "Mit Server-Löschungen synchronisieren [Experimentell]", "advanced_settings_tile_subtitle": "Erweiterte Benutzereinstellungen", "advanced_settings_troubleshooting_subtitle": "Erweiterte Funktionen zur Fehlersuche aktivieren", "advanced_settings_troubleshooting_title": "Fehlersuche", @@ -378,6 +405,7 @@ "album_cover_updated": "Album-Cover aktualisiert", "album_delete_confirmation": "Bist du sicher, dass du das Album {album} löschen willst?", "album_delete_confirmation_description": "Falls dieses Album geteilt wurde, können andere Benutzer nicht mehr darauf zugreifen.", + "album_deleted": "Album gelöscht", "album_info_card_backup_album_excluded": "AUSGESCHLOSSEN", "album_info_card_backup_album_included": "EINGESCHLOSSEN", "album_info_updated": "Album-Infos aktualisiert", @@ -387,6 +415,7 @@ "album_options": "Albumoptionen", "album_remove_user": "Nutzer entfernen?", "album_remove_user_confirmation": "Bist du sicher, dass du {user} entfernen willst?", + "album_search_not_found": "Keine Alben gefunden, die zur Suche passen", "album_share_no_users": "Es sieht so aus, als hättest du dieses Album mit allen Benutzern geteilt oder du hast keine Benutzer, mit denen du teilen kannst.", "album_updated": "Album aktualisiert", "album_updated_setting_description": "Erhalte eine E-Mail-Benachrichtigung, wenn ein freigegebenes Album neue Dateien enthält", @@ -406,6 +435,7 @@ "albums_default_sort_order": "Standard Album Sortierung", "albums_default_sort_order_description": "Sortierreihenfolge der Dateien bei der Erstellung neuer Alben.", "albums_feature_description": "Sammlung an Alben die mit anderen Benutzern geteilt werden können.", + "albums_on_device_count": "Alben auf dem Gerät ({count})", "all": "Alle", "all_albums": "Alle Alben", "all_people": "Alle Personen", @@ -426,6 +456,7 @@ "app_settings": "App-Einstellungen", "appears_in": "Erscheint in", "archive": "Archiv", + "archive_action_prompt": "{count} zum Archiv hinzugefügt", "archive_or_unarchive_photo": "Foto archivieren bzw. Archivierung aufheben", "archive_page_no_archived_assets": "Keine archivierten Inhalte gefunden", "archive_page_title": "Archiv ({count})", @@ -463,7 +494,6 @@ "assets": "Dateien", "assets_added_count": "{count, plural, one {# Datei} other {# Dateien}} hinzugefügt", "assets_added_to_album_count": "{count, plural, one {# Datei} other {# Dateien}} zum Album hinzugefügt", - "assets_added_to_name_count": "{count, plural, one {# Element} other {# Elemente}} zu {hasName, select, true {{name}} other {neuem Album}} hinzugefügt", "assets_cannot_be_added_to_album_count": "{count, plural, one {Datei kann}other {Dateien können}} nicht zum Album hinzugefügt werden", "assets_count": "{count, plural, one {# Datei} other {# Dateien}}", "assets_deleted_permanently": "{count} Element(e) permanent gelöscht", @@ -489,6 +519,7 @@ "back_close_deselect": "Zurück, Schließen oder Abwählen", "background_location_permission": "Hintergrund Standortfreigabe", "background_location_permission_content": "Um im Hintergrund zwischen den Netzwerken wechseln zu können, muss Immich *immer* Zugriff auf den genauen Standort haben, damit die App den Namen des WLAN-Netzwerks ermitteln kann", + "backup": "Sicherung", "backup_album_selection_page_albums_device": "Alben auf dem Gerät ({count})", "backup_album_selection_page_albums_tap": "Einmalig das Album antippen um es zu sichern, doppelt antippen um es nicht mehr zu sichern", "backup_album_selection_page_assets_scatter": "Elemente (Fotos / Videos) können sich über mehrere Alben verteilen. Daher können diese vor der Sicherung eingeschlossen oder ausgeschlossen werden.", @@ -552,6 +583,8 @@ "backup_options_page_title": "Sicherungsoptionen", "backup_setting_subtitle": "Verwaltung der Upload-Einstellungen im Hintergrund und im Vordergrund", "backward": "Rückwärts", + "beta_sync": "Status der Beta-Synchronisierung", + "beta_sync_subtitle": "Verwalte das neue Synchronisierungssystem", "biometric_auth_enabled": "Biometrische Authentifizierung aktiviert", "biometric_locked_out": "Du bist von der biometrischen Authentifizierung ausgeschlossen", "biometric_no_options": "Keine biometrischen Optionen verfügbar", @@ -586,6 +619,7 @@ "cancel": "Abbrechen", "cancel_search": "Suche abbrechen", "canceled": "Abgebrochen", + "canceling": "Abbrechen", "cannot_merge_people": "Personen können nicht zusammengeführt werden", "cannot_undo_this_action": "Diese Aktion kann nicht rückgängig gemacht werden!", "cannot_update_the_description": "Beschreibung kann nicht aktualisiert werden", @@ -699,10 +733,11 @@ "current_server_address": "Aktuelle Serveradresse", "custom_locale": "Benutzerdefinierte Sprache", "custom_locale_description": "Datumsangaben und Zahlen je nach Sprache und Land formatieren", + "custom_url": "Benutzerdefinierte URL", "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Dunkel", - "darkTheme": "Dunkles Theme umschalten", + "dark_theme": "Dunkle Ansicht umschalten", "date_after": "Datum nach", "date_and_time": "Datum und Zeit", "date_before": "Datum vor", @@ -718,6 +753,8 @@ "default_locale": "Standard-Sprache", "default_locale_description": "Datumsangaben und Zahlen basierend auf dem Gebietsschema des Browsers formatieren", "delete": "Löschen", + "delete_action_confirmation_message": "Bist du sicher, dass du dieses Objekt löschen willst? Diese Aktion wird das Objekt in den Papierkorb des Servers verschieben und fragen, ob du es lokal löschen willst", + "delete_action_prompt": "{count} gelöscht", "delete_album": "Album löschen", "delete_api_key_prompt": "Bist du sicher, dass du diesen API-Schlüssel löschen willst?", "delete_dialog_alert": "Diese Elemente werden unwiderruflich von Immich und dem Gerät entfernt", @@ -731,9 +768,12 @@ "delete_key": "Schlüssel löschen", "delete_library": "Bibliothek löschen", "delete_link": "Link löschen", + "delete_local_action_prompt": "{count} lokal gelöscht", "delete_local_dialog_ok_backed_up_only": "Nur gesicherte Inhalte löschen", "delete_local_dialog_ok_force": "Trotzdem löschen", "delete_others": "Andere löschen", + "delete_permanently": "Endgültig löschen", + "delete_permanently_action_prompt": "{count} endgültig gelöscht", "delete_shared_link": "geteilten Link löschen", "delete_shared_link_dialog_title": "Geteilten Link löschen", "delete_tag": "Tag löschen", @@ -744,6 +784,7 @@ "description": "Beschreibung", "description_input_hint_text": "Beschreibung hinzufügen...", "description_input_submit_error": "Beschreibung konnte nicht geändert werden, bitte im Log für mehr Details nachsehen", + "deselect_all": "Alle abwählen", "details": "Details", "direction": "Richtung", "disabled": "Deaktiviert", @@ -761,6 +802,7 @@ "documentation": "Dokumentation", "done": "Fertig", "download": "Herunterladen", + "download_action_prompt": "Herunterladen von {count} Dateien", "download_canceled": "Download abgebrochen", "download_complete": "Download vollständig", "download_enqueue": "Download in die Warteschlange gesetzt", @@ -787,6 +829,7 @@ "edit": "Bearbeiten", "edit_album": "Album bearbeiten", "edit_avatar": "Avatar bearbeiten", + "edit_birthday": "Geburtsdatum bearbeiten", "edit_date": "Datum bearbeiten", "edit_date_and_time": "Datum und Uhrzeit bearbeiten", "edit_description": "Beschreibung bearbeiten", @@ -798,6 +841,7 @@ "edit_key": "Schlüssel bearbeiten", "edit_link": "Link bearbeiten", "edit_location": "Standort bearbeiten", + "edit_location_action_prompt": "{count} Geolokationen angepasst", "edit_location_dialog_title": "Ort bearbeiten", "edit_name": "Name bearbeiten", "edit_people": "Personen bearbeiten", @@ -816,6 +860,7 @@ "empty_trash": "Papierkorb leeren", "empty_trash_confirmation": "Bist du sicher, dass du den Papierkorb leeren willst?\nDies entfernt alle Dateien im Papierkorb endgültig aus Immich und kann nicht rückgängig gemacht werden!", "enable": "Aktivieren", + "enable_backup": "Sicherung aktivieren", "enable_biometric_auth_description": "Gib deinen PIN Code ein, um die biometrische Authentifizierung zu aktivieren", "enabled": "Aktiviert", "end_date": "Enddatum", @@ -952,6 +997,7 @@ }, "exif": "EXIF", "exif_bottom_sheet_description": "Beschreibung hinzufügen...", + "exif_bottom_sheet_description_error": "Fehler bei der Aktualisierung der Beschreibung", "exif_bottom_sheet_details": "DETAILS", "exif_bottom_sheet_location": "STANDORT", "exif_bottom_sheet_people": "PERSONEN", @@ -972,6 +1018,8 @@ "explorer": "Datei-Explorer", "export": "Exportieren", "export_as_json": "Als JSON exportieren", + "export_database": "Datenbank exportieren", + "export_database_description": "Exportiert die SQLite Datenbank", "extension": "Erweiterung", "external": "Extern", "external_libraries": "Externe Bibliotheken", @@ -983,6 +1031,7 @@ "failed_to_load_assets": "Laden der Assets fehlgeschlagen", "failed_to_load_folder": "Fehler beim Laden des Ordners", "favorite": "Favorit", + "favorite_action_prompt": "{count} zu den Favoriten hinzugefügt", "favorite_or_unfavorite_photo": "Favorisiertes oder nicht favorisiertes Foto", "favorites": "Favoriten", "favorites_page_no_favorites": "Keine favorisierten Inhalte gefunden", @@ -1022,6 +1071,9 @@ "haptic_feedback_switch": "Haptisches Feedback aktivieren", "haptic_feedback_title": "Haptisches Feedback", "has_quota": "Kontingent", + "hash_asset": "Dateihash", + "hashed_assets": "Gehashte Dateien", + "hashing": "Hashen", "header_settings_add_header_tip": "Header hinzufügen", "header_settings_field_validator_msg": "Der Wert darf nicht leer sein", "header_settings_header_name_input": "Header-Name", @@ -1043,7 +1095,7 @@ "home_page_archive_err_partner": "Inhalte von Partnern können nicht archiviert werden", "home_page_building_timeline": "Zeitachse wird erstellt", "home_page_delete_err_partner": "Inhalte von Partnern können nicht gelöscht werden, überspringe", - "home_page_delete_remote_err_local": "Lokale Inhalte in der Auswahl, überspringen", + "home_page_delete_remote_err_local": "Lokale Elemente in der Auswahl zum Entfernen von Remote-Elementen, Überspringe", "home_page_favorite_err_local": "Kann lokale Elemente noch nicht favorisieren, überspringen", "home_page_favorite_err_partner": "Inhalte von Partnern können nicht favorisiert werden, überspringe", "home_page_first_time_notice": "Wenn dies das erste Mal ist dass Du Immich nutzt, stelle bitte sicher, dass mindestens ein Album zur Sicherung ausgewählt ist, sodass die Zeitachse mit Fotos und Videos gefüllt werden kann", @@ -1054,6 +1106,7 @@ "host": "Host", "hour": "Stunde", "id": "ID", + "idle": "Untätig", "ignore_icloud_photos": "iCloud Fotos ignorieren", "ignore_icloud_photos_description": "Fotos, die in der iCloud gespeichert sind, werden nicht auf den immich Server hochgeladen", "image": "Bild", @@ -1111,6 +1164,7 @@ "language_no_results_title": "Keine Sprachen gefunden", "language_search_hint": "Sprachen durchsuchen...", "language_setting_description": "Wähle deine bevorzugte Sprache", + "large_files": "Große Dateien", "last_seen": "Zuletzt gesehen", "latest_version": "Aktuellste Version", "latitude": "Breitengrad", @@ -1126,16 +1180,18 @@ "library_page_sort_created": "Zuletzt erstellt", "library_page_sort_last_modified": "Zuletzt bearbeitet", "library_page_sort_title": "Titel des Albums", + "licenses": "Lizenzen", "light": "Hell", "like_deleted": "Like gelöscht", "link_motion_video": "Bewegungsvideo verknüpfen", - "link_options": "Link-Optionen", "link_to_oauth": "Mit OAuth verknüpfen", "linked_oauth_account": "Verknüpftes OAuth-Konto", "list": "Liste", "loading": "Laden", "loading_search_results_failed": "Laden von Suchergebnissen fehlgeschlagen", + "local": "Lokal", "local_asset_cast_failed": "Eine Datei, die nicht auf den Server hochgeladen wurde, kann nicht gecastet werden", + "local_assets": "Lokale Dateien", "local_network": "Lokales Netzwerk", "local_network_sheet_info": "Die App stellt über diese URL eine Verbindung zum Server her, wenn sie das angegebene WLAN-Netzwerk verwendet", "location_permission": "Standort Genehmigung", @@ -1192,8 +1248,7 @@ "manage_your_devices": "Deine eingeloggten Geräte verwalten", "manage_your_oauth_connection": "Deine OAuth-Verknüpfung verwalten", "map": "Karte", - "map_assets_in_bound": "{count} Foto", - "map_assets_in_bounds": "{count} Fotos", + "map_assets_in_bounds": "{count, plural, one {# Foto} other {# Fotos}}", "map_cannot_get_user_location": "Standort konnte nicht ermittelt werden", "map_location_dialog_yes": "Ja", "map_location_picker_page_use_location": "Aufnahmeort verwenden", @@ -1245,6 +1300,7 @@ "more": "Mehr", "move": "Verschieben", "move_off_locked_folder": "Aus dem gesperrten Ordner verschieben", + "move_to_lock_folder_action_prompt": "{count} zum gesperrten Ordner hinzugefügt", "move_to_locked_folder": "In den gesperrten Ordner verschieben", "move_to_locked_folder_confirmation": "Diese Fotos und Videos werden aus allen Alben entfernt und können nur noch im gesperrten Ordner angezeigt werden", "moved_to_archive": "{count, plural, one {# Datei} other {# Dateien}} archiviert", @@ -1291,6 +1347,7 @@ "no_results": "Keine Ergebnisse", "no_results_description": "Versuche es mit einem Synonym oder einem allgemeineren Stichwort", "no_shared_albums_message": "Erstelle ein Album, um Fotos und Videos mit Personen in deinem Netzwerk zu teilen", + "no_uploads_in_progress": "Kein Upload in Bearbeitung", "not_in_any_album": "In keinem Album", "not_selected": "Nicht ausgewählt", "note_apply_storage_label_to_previously_uploaded assets": "Hinweis: Um eine Speicherpfadbezeichnung anzuwenden, starte den", @@ -1328,6 +1385,7 @@ "original": "Original", "other": "Sonstiges", "other_devices": "Andere Geräte", + "other_entities": "Andere Entitäten", "other_variables": "Sonstige Variablen", "owned": "Eigenes", "owner": "Besitzer", @@ -1459,6 +1517,7 @@ "purchase_server_description_2": "Unterstützerstatus", "purchase_server_title": "Server", "purchase_settings_server_activated": "Der Server-Produktschlüssel wird durch den Administrator verwaltet", + "queue_status": "Warteschlange {count}/{total}", "rating": "Bewertung", "rating_clear": "Bewertung löschen", "rating_count": "{count, plural, one {# Stern} other {# Sterne}}", @@ -1487,6 +1546,8 @@ "refreshing_faces": "Gesichter werden aktualisiert", "refreshing_metadata": "Metadaten werden aktualisiert", "regenerating_thumbnails": "Miniaturansichten werden neu erstellt", + "remote": "Server", + "remote_assets": "Server-Dateien", "remove": "Entfernen", "remove_assets_album_confirmation": "Bist du sicher, dass du {count, plural, one {# Datei} other {# Dateien}} aus dem Album entfernen willst?", "remove_assets_shared_link_confirmation": "Bist du sicher, dass du {count, plural, one {# Datei} other {# Dateien}} von diesem geteilten Link entfernen willst?", @@ -1494,7 +1555,9 @@ "remove_custom_date_range": "Benutzerdefinierten Datumsbereich entfernen", "remove_deleted_assets": "Offline-Dateien entfernen", "remove_from_album": "Aus Album entfernen", + "remove_from_album_action_prompt": "{count} vom Album entfernt", "remove_from_favorites": "Aus Favoriten entfernen", + "remove_from_lock_folder_action_prompt": "{count} aus dem gesperrten Ordner entfernt", "remove_from_locked_folder": "Aus gesperrtem Ordner entfernen", "remove_from_locked_folder_confirmation": "Bist du sicher, dass du diese Fotos und Videos aus dem gesperrten Ordner entfernen möchtest? Sie werden wieder in deiner Bibliothek sichtbar sein.", "remove_from_shared_link": "Aus geteiltem Link entfernen", @@ -1522,19 +1585,25 @@ "reset_password": "Passwort zurücksetzen", "reset_people_visibility": "Sichtbarkeit von Personen zurücksetzen", "reset_pin_code": "PIN Code zurücksetzen", + "reset_sqlite": "SQLite Datenbank zurücksetzen", + "reset_sqlite_confirmation": "Bist du sicher, dass du die SQLite-Datenbank zurücksetzen willst? Du musst dich ab- und wieder anmelden, um die Daten neu zu synchronisieren", + "reset_sqlite_success": "SQLite Datenbank erfolgreich zurückgesetzt", "reset_to_default": "Auf Standard zurücksetzen", "resolve_duplicates": "Duplikate entfernen", "resolved_all_duplicates": "Alle Duplikate aufgelöst", "restore": "Wiederherstellen", "restore_all": "Alle wiederherstellen", + "restore_trash_action_prompt": "{count} aus dem Papierkorb wiederhergestellt", "restore_user": "Nutzer wiederherstellen", "restored_asset": "Datei wiederhergestellt", "resume": "Fortsetzen", "retry_upload": "Upload wiederholen", "review_duplicates": "Duplikate überprüfen", + "review_large_files": "Große Dateien überprüfen", "role": "Rolle", "role_editor": "Bearbeiter", "role_viewer": "Betrachter", + "running": "Läuft", "save": "Speichern", "save_to_gallery": "In Galerie speichern", "saved_api_key": "API-Schlüssel wurde gespeichert", @@ -1666,6 +1735,7 @@ "settings_saved": "Einstellungen gespeichert", "setup_pin_code": "Einen PIN Code festlegen", "share": "Teilen", + "share_action_prompt": "{count} Dateien geteilt", "share_add_photos": "Fotos hinzufügen", "share_assets_selected": "{count} ausgewählt", "share_dialog_preparing": "Vorbereiten...", @@ -1687,6 +1757,7 @@ "shared_link_clipboard_copied_massage": "Link kopiert", "shared_link_clipboard_text": "Link: {link}\nPasswort: {password}", "shared_link_create_error": "Fehler beim Erstellen der Linkfreigabe", + "shared_link_custom_url_description": "Greife über eine benutzerdefinierte URL auf diesen Freigabelink zu", "shared_link_edit_description_hint": "Beschreibung eingeben", "shared_link_edit_expire_after_option_day": "1 Tag", "shared_link_edit_expire_after_option_days": "{count} Tagen", @@ -1712,6 +1783,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Geteilte Links verwalten", "shared_link_options": "Optionen für geteilten Link", + "shared_link_password_description": "Für den Zugriff auf diesen freigegebenen Link ist ein Passwort erforderlich", "shared_links": "Geteilte Links", "shared_links_description": "Teile Fotos und Videos mit einem Link", "shared_photos_and_videos_count": "{assetCount, plural, one {# geteiltes Foto oder Video.} other {# geteilte Fotos & Videos.}}", @@ -1767,6 +1839,7 @@ "sort_title": "Titel", "source": "Quellcode", "stack": "Stapel", + "stack_action_prompt": "{count} gestapelt", "stack_duplicates": "Duplikate stapeln", "stack_select_one_photo": "Hauptfoto für den Stapel auswählen", "stack_selected_photos": "Ausgewählte Fotos stapeln", @@ -1786,6 +1859,7 @@ "storage_quota": "Speicherplatz-Kontingent", "storage_usage": "{used} von {available} verwendet", "submit": "Bestätigen", + "success": "Erfolgreich", "suggestions": "Vorschläge", "sunrise_on_the_beach": "Sonnenaufgang am Strand", "support": "Unterstützung", @@ -1795,6 +1869,8 @@ "sync": "Synchronisieren", "sync_albums": "Alben synchronisieren", "sync_albums_manual_subtitle": "Synchronisiere alle hochgeladenen Videos und Fotos in die ausgewählten Backup-Alben", + "sync_local": "Lokal synchronisieren", + "sync_remote": "mit Server synchronisieren", "sync_upload_album_setting_subtitle": "Erstelle deine ausgewählten Alben in Immich und lade die Fotos und Videos dort hoch", "tag": "Tag", "tag_assets": "Dateien taggen", @@ -1805,11 +1881,12 @@ "tag_updated": "Tag aktualisiert: {tag}", "tagged_assets": "{count, plural, one {# Datei} other {# Dateien}} getagged", "tags": "Tags", + "tap_to_run_job": "Tippen um den Job zu starten", "template": "Vorlage", "theme": "Theme", "theme_selection": "Themenauswahl", "theme_selection_description": "Automatische Einstellung des Themes auf Hell oder Dunkel, je nach Systemeinstellung des Browsers", - "theme_setting_asset_list_storage_indicator_title": "Forschrittsbalken der Sicherung auf dem Vorschaubild", + "theme_setting_asset_list_storage_indicator_title": "Fortschrittsbalken der Sicherung auf dem Vorschaubild", "theme_setting_asset_list_tiles_per_row_title": "Anzahl der Elemente pro Reihe ({count})", "theme_setting_colorful_interface_subtitle": "Primärfarbe auf App-Hintergrund anwenden.", "theme_setting_colorful_interface_title": "Farbige UI-Oberfläche", @@ -1837,6 +1914,7 @@ "total": "Gesamt", "total_usage": "Gesamtnutzung", "trash": "Papierkorb", + "trash_action_prompt": "{count} in den Papierkorb verschoben", "trash_all": "Alle löschen", "trash_count": "Papierkorb {count, number}", "trash_delete_asset": "Datei löschen/in den Papierkorb verschieben", @@ -1854,9 +1932,11 @@ "unable_to_change_pin_code": "PIN Code konnte nicht geändert werden", "unable_to_setup_pin_code": "PIN Code konnte nicht festgelegt werden", "unarchive": "Entarchivieren", + "unarchive_action_prompt": "{count} aus dem Archiv entfernt", "unarchived_count": "{count, plural, other {# entarchiviert}}", "undo": "Rückgängig", "unfavorite": "Entfavorisieren", + "unfavorite_action_prompt": "{count} aus den Favoriten entfernt", "unhide_person": "Person einblenden", "unknown": "Unbekannt", "unknown_country": "Unbekanntes Land", @@ -1874,15 +1954,20 @@ "unselect_all_duplicates": "Alle Duplikate abwählen", "unselect_all_in": "Alle in {group} abwählen", "unstack": "Entstapeln", + "unstack_action_prompt": "{count} entstapelt", "unstacked_assets_count": "{count, plural, one {# Datei} other {# Dateien}} entstapelt", + "untagged": "Ohne Tag", "up_next": "Weiter", "updated_at": "Aktualisiert", "updated_password": "Passwort aktualisiert", "upload": "Hochladen", + "upload_action_prompt": "{count} in der Warteschlange für Upload", "upload_concurrency": "Parallelität beim Hochladen", + "upload_details": "Upload Details", "upload_dialog_info": "Willst du die ausgewählten Elemente auf dem Server sichern?", "upload_dialog_title": "Element hochladen", "upload_errors": "Hochladen mit {count, plural, one {# Fehler} other {# Fehlern}} abgeschlossen, aktualisiere die Seite, um neu hochgeladene Dateien zu sehen.", + "upload_finished": "Upload fertig", "upload_progress": "{remaining, number} verbleibend - {processed, number}/{total, number} verarbeitet", "upload_skipped_duplicates": "{count, plural, one {# doppelte Datei} other {# doppelte Dateien}} ausgelassen", "upload_status_duplicates": "Duplikate", @@ -1891,6 +1976,7 @@ "upload_success": "Hochladen erfolgreich. Aktualisiere die Seite, um neue hochgeladene Dateien zu sehen.", "upload_to_immich": "Auf Immich hochladen ({count})", "uploading": "Wird hochgeladen", + "uploading_media": "Medien werden hochgeladen", "url": "URL", "usage": "Verwendung", "use_biometric": "Biometrie verwenden", @@ -1911,6 +1997,7 @@ "user_usage_stats_description": "Statistiken zur Kontonutzung anzeigen", "username": "Nutzername", "users": "Benutzer", + "users_added_to_album_count": "{count, plural, one {# Benutzer} other {# Benutzer}} zum Album hinzugefügt", "utilities": "Hilfsmittel", "validate": "Validieren", "validate_endpoint_error": "Bitte gib eine gültige URL ein", @@ -1929,6 +2016,7 @@ "view_album": "Album anzeigen", "view_all": "Alles anzeigen", "view_all_users": "Alle Nutzer anzeigen", + "view_details": "Details ansehen", "view_in_timeline": "In Zeitleiste anzeigen", "view_link": "Link anzeigen", "view_links": "Links anzeigen", diff --git a/i18n/el.json b/i18n/el.json index 62d0481ec6..d8d12bddd0 100644 --- a/i18n/el.json +++ b/i18n/el.json @@ -166,6 +166,20 @@ "metadata_settings_description": "Διαχείρηση ρυθμίσεων μεταδεδομένων", "migration_job": "Μεταφορά δεδομένων (Migration)", "migration_job_description": "Μεταφορά των εικονιδίων για αρχεία και πρόσωπα στην πιο πρόσφατη δομή αρχείων", + "nightly_tasks_cluster_faces_setting_description": "Εκτέλεση αναγνώρισης προσώπου σε νέα ανιχνευμένα πρόσωπα", + "nightly_tasks_cluster_new_faces_setting": "Ομαδοποίηση νέων προσώπων", + "nightly_tasks_database_cleanup_setting": "Εργασίες καθαρισμού βάσης δεδομένων", + "nightly_tasks_database_cleanup_setting_description": "Εκκαθάριση παλιών και ληγμένων δεδομένων από τη βάση δεδομένων", + "nightly_tasks_generate_memories_setting": "Δημιουργία αναμνήσεων", + "nightly_tasks_generate_memories_setting_description": "Δημιουργία νέων αναμνήσεων από αντικείμενα", + "nightly_tasks_missing_thumbnails_setting": "Δημιουργία ελλειπόντων μικρογραφιών", + "nightly_tasks_missing_thumbnails_setting_description": "Τοποθέτηση στη ουρά των αρχείων χωρίς μικρογραφίες για δημιουργία μικρογραφιών", + "nightly_tasks_settings": "Ρυθμίσεις για τις νυχτερινές εργασίες", + "nightly_tasks_settings_description": "Διαχείριση νυχτερινών εργασιών", + "nightly_tasks_start_time_setting": "Ώρα έναρξης", + "nightly_tasks_start_time_setting_description": "Η ώρα κατά την οποία ο διακομιστής ξεκινάει να εκτελεί τις νυχτερινές εργασίες", + "nightly_tasks_sync_quota_usage_setting": "Συγχρονισμός χρήσης χώρου", + "nightly_tasks_sync_quota_usage_setting_description": "Ενημέρωση του διαθέσιμου χώρου χρήστη, με βάση την τρέχουσα χρήση", "no_paths_added": "Δεν προστέθηκαν διαδρομές", "no_pattern_added": "Δεν προστέθηκε μοτίβο", "note_apply_storage_label_previous_assets": "Σημείωση: Για να εφαρμοστεί η Ετικέτα Αποθήκευσης σε στοιχεία που είχαν αναρτηθεί παλαιότερα, εκτέλεσε το", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "URI Ανακατεύθυνσης για κινητά τηλέφωνα", "oauth_mobile_redirect_uri_override": "Προσπέλαση URI ανακατεύθυνσης για κινητά τηλέφωνα", "oauth_mobile_redirect_uri_override_description": "Ενεργοποιήστε το όταν ο πάροχος OAuth δεν επιτρέπει μια URI για κινητά, όπως το ''{callback}''", + "oauth_role_claim": "Ανάθεση ρόλου", + "oauth_role_claim_description": "Αυτόματη παραχώρηση πρόσβασης διαχειριστή με βάση την ύπαρξη αυτής της ανάθεσης. Η ανάθεση μπορεί να είναι είτε 'χρήστης' είτε 'διαχειριστής'.", "oauth_settings": "OAuth", "oauth_settings_description": "Διαχείριση ρυθμίσεων σύνδεσης OAuth", "oauth_settings_more_details": "Για περισσότερες λεπτομέρειες σχετικά με αυτήν τη δυνατότητα, ανατρέξτε στην τεκμηρίωση.", @@ -357,10 +373,12 @@ "admin_password": "Κωδικός πρόσβασης Διαχειριστή", "administration": "Διαχείριση", "advanced": "Για προχωρημένους", + "advanced_settings_beta_timeline_subtitle": "Δοκίμασε τη νέα εμπειρία της εφαρμογής", + "advanced_settings_beta_timeline_title": "Δοκιμαστικό χρονολόγιο", "advanced_settings_enable_alternate_media_filter_subtitle": "Χρησιμοποιήστε αυτήν την επιλογή για να φιλτράρετε τα μέσα ενημέρωσης κατά τον συγχρονισμό με βάση εναλλακτικά κριτήρια. Δοκιμάστε αυτή τη δυνατότητα μόνο αν έχετε προβλήματα με την εφαρμογή που εντοπίζει όλα τα άλμπουμ.", "advanced_settings_enable_alternate_media_filter_title": "[ΠΕΙΡΑΜΑΤΙΚΟ] Χρήση εναλλακτικού φίλτρου συγχρονισμού άλμπουμ συσκευής", "advanced_settings_log_level_title": "Επίπεδο σύνδεσης: {level}", - "advanced_settings_prefer_remote_subtitle": "Μερικές συσκευές αργούν πολύ να φορτώσουν μικρογραφίες από αρχεία στη συσκευή. Ενεργοποιήστε αυτήν τη ρύθμιση για να φορτώνονται αντί αυτού απομακρυσμένες εικόνες.", + "advanced_settings_prefer_remote_subtitle": "Μερικές συσκευές αργούν πολύ να φορτώσουν μικρογραφίες από τοπικά αρχεία. Ενεργοποιήστε αυτήν τη ρύθμιση για να φορτώνονται αντί αυτού απομακρυσμένες εικόνες.", "advanced_settings_prefer_remote_title": "Προτίμηση απομακρυσμένων εικόνων", "advanced_settings_proxy_headers_subtitle": "Καθορισμός κεφαλίδων διακομιστή μεσολάβησης που το Immich πρέπει να στέλνει με κάθε αίτημα δικτύου", "advanced_settings_proxy_headers_title": "Κεφαλίδες διακομιστή μεσολάβησης", @@ -379,6 +397,7 @@ "album_cover_updated": "Το εξώφυλλο του άλμπουμ, ενημερώθηκε", "album_delete_confirmation": "Είστε σίγουροι ότι θέλετε να διαγράψετε το άλμπουμ {album};", "album_delete_confirmation_description": "Εάν αυτό το άλμπουμ είναι κοινόχρηστο, οι άλλοι χρήστες δεν θα μπορούν να έχουν πρόσβαση.", + "album_deleted": "Το άλμπουμ διαγράφηκε", "album_info_card_backup_album_excluded": "ΕΞΑΙΡΟΥΜΕΝΟ", "album_info_card_backup_album_included": "ΣΥΜΠΕΡΙΛΑΜΒΑΝΟΜΕΝΟ", "album_info_updated": "Οι πληροφορίες του άλμπουμ, ενημερώθηκαν", @@ -388,6 +407,7 @@ "album_options": "Επιλογές άλμπουμ", "album_remove_user": "Διαγραφή χρήστη;", "album_remove_user_confirmation": "Είστε σίγουροι ότι θέλετε να αφαιρέσετε τον/την {user};", + "album_search_not_found": "Δε βρέθηκαν άλμπουμ που να ταιριάζουν με την αναζήτησή σας", "album_share_no_users": "Φαίνεται ότι έχετε κοινοποιήσει αυτό το άλμπουμ σε όλους τους χρήστες ή δεν έχετε χρήστες για να το κοινοποιήσετε.", "album_updated": "Το άλμπουμ, ενημερώθηκε", "album_updated_setting_description": "Λάβετε ειδοποίηση μέσω email όταν ένα κοινόχρηστο άλμπουμ έχει νέα αρχεία", @@ -407,6 +427,7 @@ "albums_default_sort_order": "Προεπιλεγμένη ταξινόμηση άλμπουμ", "albums_default_sort_order_description": "Αρχική ταξινόμηση κατά τη δημιουργία νέων άλμπουμ.", "albums_feature_description": "Συλλογές στοιχείων που μπορούν να κοινοποιηθούν σε άλλους χρήστες.", + "albums_on_device_count": "Άλμπουμ στη συσκευή ({count})", "all": "Όλα", "all_albums": "Όλα τα άλμπουμ", "all_people": "Όλα τα άτομα", @@ -427,6 +448,7 @@ "app_settings": "Ρυθμίσεις εφαρμογής", "appears_in": "Εμφανίζεται σε", "archive": "Αρχείο", + "archive_action_prompt": "Προστέθηκαν {count} στο Αρχείο", "archive_or_unarchive_photo": "Αρχειοθέτηση ή αποαρχειοθέτηση φωτογραφίας", "archive_page_no_archived_assets": "Δε βρέθηκαν αρχειοθετημένα στοιχεία", "archive_page_title": "Αρχείο ({count})", @@ -464,7 +486,6 @@ "assets": "Αντικείμενα", "assets_added_count": "Προστέθηκε {count, plural, one {# αρχείο} other {# αρχεία}}", "assets_added_to_album_count": "Προστέθηκε {count, plural, one {# αρχείο} other {# αρχεία}} στο άλμπουμ", - "assets_added_to_name_count": "Προστέθηκε {count, plural, one {# αρχείο} other {# αρχεία}} στο {hasName, select, true {{name}} other {νέο άλμπουμ}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Στοιχείο} other {Στοιχεία}} δεν μπορούν να προστεθούν στο άλμπουμ", "assets_count": "{count, plural, one {# αρχείο} other {# αρχεία}}", "assets_deleted_permanently": "{count} τα στοιχεία διαγράφηκαν οριστικά", @@ -490,6 +511,7 @@ "back_close_deselect": "Πίσω, κλείσιμο ή αποεπιλογή", "background_location_permission": "Άδεια τοποθεσίας στο παρασκήνιο", "background_location_permission_content": "Το Immich για να μπορεί να αλλάζει δίκτυα όταν τρέχει στο παρασκήνιο, πρέπει *πάντα* να έχει πρόσβαση στην ακριβή τοποθεσία ώστε η εφαρμογή να μπορεί να διαβάζει το όνομα του δικτύου Wi-Fi", + "backup": "Αντίγραφα ασφαλείας", "backup_album_selection_page_albums_device": "Άλμπουμ στη συσκευή ({count})", "backup_album_selection_page_albums_tap": "Πάτημα για συμπερίληψη, διπλό πάτημα για εξαίρεση", "backup_album_selection_page_assets_scatter": "Τα στοιχεία μπορεί να διασκορπιστούν σε πολλά άλμπουμ. Έτσι, τα άλμπουμ μπορούν να περιληφθούν ή να εξαιρεθούν κατά τη διαδικασία δημιουργίας αντιγράφων ασφαλείας.", @@ -553,6 +575,8 @@ "backup_options_page_title": "Επιλογές αντιγράφων ασφαλείας", "backup_setting_subtitle": "Διαχείριση ρυθμίσεων μεταφόρτωσης στο παρασκήνιο και στο προσκήνιο", "backward": "Προς τα πίσω", + "beta_sync": "Κατάσταση Συγχρονισμού Beta (δοκιμαστική)", + "beta_sync_subtitle": "Διαχείριση του νέου συστήματος συγχρονισμού", "biometric_auth_enabled": "Βιομετρική ταυτοποίηση ενεργοποιήθηκε", "biometric_locked_out": "Είστε κλειδωμένοι εκτός της βιομετρικής ταυτοποίησης", "biometric_no_options": "Δεν υπάρχουν διαθέσιμοι τρόποι βιομετρικής ταυτοποίησης", @@ -587,6 +611,7 @@ "cancel": "Ακύρωση", "cancel_search": "Ακύρωση αναζήτησης", "canceled": "Ακυρωμένο", + "canceling": "Ακυρώνεται", "cannot_merge_people": "Αδύνατη η συγχώνευση ατόμων", "cannot_undo_this_action": "Δεν μπορείτε να αναιρέσετε αυτήν την ενέργεια!", "cannot_update_the_description": "Αδύνατη η ενημέρωση της περιγραφής", @@ -700,10 +725,11 @@ "current_server_address": "Τρέχουσα διεύθυνση διακομιστή", "custom_locale": "Προσαρμοσμένη Τοπική Ρύθμιση", "custom_locale_description": "Μορφοποιήστε τις ημερομηνίες και τους αριθμούς, σύμφωνα με τη γλώσσα και την περιοχή", + "custom_url": "Προσαρμοσμένη διεύθυνση URL", "daily_title_text_date": "Ε, MMM dd", "daily_title_text_date_year": "Ε, MMM dd, yyyy", "dark": "Σκούρο", - "darkTheme": "Εναλλαγή σκούρου θέματος", + "dark_theme": "Εναλλαγή σκοτεινής εμφάνισης", "date_after": "Ημερομηνία μετά", "date_and_time": "Ημερομηνία και ώρα", "date_before": "Ημερομηνία πριν", @@ -719,6 +745,8 @@ "default_locale": "Προεπιλεγμένη Τοπική Ρύθμιση", "default_locale_description": "Μορφοποιήστε τις ημερομηνίες και τους αριθμούς με βάση την τοπική ρύθμιση του προγράμματος περιήγησής σας", "delete": "Διαγραφή", + "delete_action_confirmation_message": "Είστε σίγουροι ότι θέλετε να διαγράψετε αυτό το αρχείο; Αυτή η ενέργεια θα το μετακινήσει στον κάδο απορριμμάτων του διακομιστή και θα εμφανιστεί μήνυμα για το αν θέλετε να το διαγράψετε και τοπικά", + "delete_action_prompt": "{count} διαγράφηκαν", "delete_album": "Διαγραφή άλμπουμ", "delete_api_key_prompt": "Είστε σίγουροι ότι θέλετε να διαγράψετε αυτό κλειδί API;", "delete_dialog_alert": "Αυτά τα αντικείμενα θα διαγραφούν οριστικά από το Immich και από τη συσκευή σας", @@ -732,9 +760,12 @@ "delete_key": "Διαγραφή κλειδιού", "delete_library": "Διαγραφή Βιβλιοθήκης", "delete_link": "Διαγραφή συνδέσμου", + "delete_local_action_prompt": "{count} διαγράφηκαν τοπικά", "delete_local_dialog_ok_backed_up_only": "Διαγραφή μόνο των αντιγράφων ασφαλείας", "delete_local_dialog_ok_force": "Διαγραφή όπως και να έχει", "delete_others": "Διαγραφή υπολοίπων", + "delete_permanently": "Διαγραφή οριστικά", + "delete_permanently_action_prompt": "{count} διαγράφηκε οριστικά", "delete_shared_link": "Διαγραφή κοινόχρηστου συνδέσμου", "delete_shared_link_dialog_title": "Διαγραφή Κοινοποιημένου Συνδέσμου", "delete_tag": "Διαγραφή ετικέτας", @@ -745,6 +776,7 @@ "description": "Περιγραφή", "description_input_hint_text": "Προσθήκη περιγραφής...", "description_input_submit_error": "Σφάλμα κατά την ενημέρωση της περιγραφής, ελέγξτε το αρχείο καταγραφής για περισσότερες λεπτομέρειες", + "deselect_all": "Ακύρωση όλων των επιλογών", "details": "Λεπτομέρειες", "direction": "Κατεύθυνση", "disabled": "Απενεργοποιημένο", @@ -762,6 +794,7 @@ "documentation": "Τεκμηρίωση", "done": "Έγινε", "download": "Λήψη", + "download_action_prompt": "Κατέβασμα {count} στοιχείων", "download_canceled": "Η λήψη ακυρώθηκε", "download_complete": "Η λήψη ολοκληρώθηκε", "download_enqueue": "Η λήψη τέθηκε σε ουρά", @@ -799,6 +832,7 @@ "edit_key": "Επεξεργασία κλειδιού", "edit_link": "Επεξεργασία συνδέσμου", "edit_location": "Επεξεργασία τοποθεσίας", + "edit_location_action_prompt": "Επεξεργάστηκαν {count} τοποθεσίες", "edit_location_dialog_title": "Τοποθεσία", "edit_name": "Επεξεργασία ονόματος", "edit_people": "Επεξεργασία ατόμων", @@ -817,6 +851,7 @@ "empty_trash": "Άδειασμα κάδου απορριμμάτων", "empty_trash_confirmation": "Είστε σίγουροι οτι θέλετε να αδειάσετε τον κάδο απορριμμάτων; Αυτό θα αφαιρέσει μόνιμα όλα τα στοιχεία του κάδου απορριμμάτων του Immich. \nΑυτή η ενέργεια δεν μπορεί να αναιρεθεί!", "enable": "Ενεργοποίηση", + "enable_backup": "Ενεργοποίηση αντιγράφου ασφαλείας", "enable_biometric_auth_description": "Εισάγετε τον κωδικό PIN σας για να ενεργοποιήσετε την βιομετρική ταυτοποίηση", "enabled": "Ενεργοποιημένο", "end_date": "Τελική ημερομηνία", @@ -973,6 +1008,8 @@ "explorer": "Περιηγητής", "export": "Εξαγωγή", "export_as_json": "Εξαγωγή ως JSON", + "export_database": "Εξαγωγή βάσης δεδομένων", + "export_database_description": "Εξαγωγή της SQLite βάσης δεδομένων", "extension": "Επέκταση", "external": "Εξωτερικός", "external_libraries": "Εξωτερικές βιβλιοθήκες", @@ -984,6 +1021,7 @@ "failed_to_load_assets": "Αποτυχία φόρτωσης στοιχείων", "failed_to_load_folder": "Αποτυχία φόρτωσης φακέλου", "favorite": "Αγαπημένο", + "favorite_action_prompt": "Προστέθηκαν {count} στα Αγαπημένα", "favorite_or_unfavorite_photo": "Ορίστε μία φωτογραφία ως αγαπημένη ή αφαιρέστε την από τα αγαπημένα", "favorites": "Αγαπημένα", "favorites_page_no_favorites": "Δεν βρέθηκαν αγαπημένα στοιχεία", @@ -1023,6 +1061,9 @@ "haptic_feedback_switch": "Ενεργοποίηση απτικής ανάδρασης", "haptic_feedback_title": "Απτική Ανάδραση", "has_quota": "Έχει ποσόστωση", + "hash_asset": "Κατακερματισμός στοιχείου", + "hashed_assets": "Κατακερματισμένα στοιχεία", + "hashing": "Κατακερματισμός", "header_settings_add_header_tip": "Προσθήκη Κεφαλίδας", "header_settings_field_validator_msg": "Η τιμή δεν μπορεί να είναι κενή", "header_settings_header_name_input": "Όνομα κεφαλίδας", @@ -1055,6 +1096,7 @@ "host": "Φιλοξενία", "hour": "Ώρα", "id": "ID", + "idle": "Αδράνεια", "ignore_icloud_photos": "Αγνοήστε τις φωτογραφίες iCloud", "ignore_icloud_photos_description": "Οι φωτογραφίες που είναι αποθηκευμένες στο iCloud δεν θα μεταφορτωθούν στον διακομιστή Immich", "image": "Εικόνα", @@ -1112,6 +1154,7 @@ "language_no_results_title": "Δε βρέθηκαν γλώσσες", "language_search_hint": "Αναζήτηση γλωσσών...", "language_setting_description": "Επιλέξτε τη γλώσσα που προτιμάτε", + "large_files": "Μεγάλα Αρχεία", "last_seen": "Τελευταία προβολή", "latest_version": "Τελευταία Έκδοση", "latitude": "Γεωγραφικό πλάτος", @@ -1127,16 +1170,18 @@ "library_page_sort_created": "Ημερομηνία δημιουργίας", "library_page_sort_last_modified": "Τελευταία τροποποίηση", "library_page_sort_title": "Τίτλος άλμπουμ", + "licenses": "Άδειες", "light": "Φωτεινό", "like_deleted": "Το \"μου αρέσει\" διαγράφηκε", "link_motion_video": "Σύνδεσε βίντεο κίνησης", - "link_options": "Επιλογές συνδέσμου", "link_to_oauth": "Σύνδεση στον OAuth", "linked_oauth_account": "Ο OAuth λογαριασμός συνδέθηκε", "list": "Λίστα", "loading": "Φόρτωση", "loading_search_results_failed": "Η φόρτωση αποτελεσμάτων αναζήτησης απέτυχε", + "local": "Τοπικά", "local_asset_cast_failed": "Αδυναμία μετάδοσης στοιχείου που δεν έχει ανέβει στον διακομιστή", + "local_assets": "Τοπικά στοιχεία", "local_network": "Τοπικό δίκτυο", "local_network_sheet_info": "Η εφαρμογή θα συνδεθεί με τον διακομιστή μέσω αυτού του URL όταν χρησιμοποιείται το καθορισμένο δίκτυο Wi-Fi", "location_permission": "Άδεια τοποθεσίας", @@ -1193,8 +1238,7 @@ "manage_your_devices": "Διαχειριστείτε τις συνδεδεμένες συσκευές σας", "manage_your_oauth_connection": "Διαχειριστείτε τη σύνδεσή σας OAuth", "map": "Χάρτης", - "map_assets_in_bound": "{count} φωτογραφία", - "map_assets_in_bounds": "{count} φωτογραφίες", + "map_assets_in_bounds": "{count, plural, one {# φωτογραφία} other {# φωτογραφίες}}", "map_cannot_get_user_location": "Δεν είναι δυνατή η λήψη της τοποθεσίας του χρήστη", "map_location_dialog_yes": "Ναι", "map_location_picker_page_use_location": "Χρησιμοποιήστε αυτήν την τοποθεσία", @@ -1246,6 +1290,7 @@ "more": "Περισσότερα", "move": "Μετακίνηση", "move_off_locked_folder": "Μετακίνηση έξω από τον κλειδωμένο φάκελο", + "move_to_lock_folder_action_prompt": "Προστέθηκαν {count} στον κλειδωμένο φάκελο", "move_to_locked_folder": "Μετακίνηση σε κλειδωμένο φάκελο", "move_to_locked_folder_confirmation": "Αυτές οι φωτογραφίες και τα βίντεο θα αφαιρεθούν από όλα τα άλμπουμ και θα μπορούν να προβληθούν μόνο από τον κλειδωμένο φάκελο", "moved_to_archive": "Μετακινήθηκαν {count, plural, one {# στοιχείο} other {# στοιχεία}} στο αρχείο", @@ -1292,6 +1337,7 @@ "no_results": "Κανένα αποτέλεσμα", "no_results_description": "Δοκιμάστε ένα συνώνυμο ή πιο γενική λέξη-κλειδί", "no_shared_albums_message": "Δημιουργήστε ένα άλμπουμ για να μοιράζεστε φωτογραφίες και βίντεο με άτομα στο δίκτυό σας", + "no_uploads_in_progress": "Καμία μεταφόρτωση σε εξέλιξη", "not_in_any_album": "Σε κανένα άλμπουμ", "not_selected": "Δεν επιλέχθηκε", "note_apply_storage_label_to_previously_uploaded assets": "Σημείωση: Για να εφαρμόσετε την Ετικέτα Αποθήκευσης σε στοιχεία που έχουν μεταφορτωθεί προηγουμένως, εκτελέστε το", @@ -1329,6 +1375,7 @@ "original": "πρωτότυπο", "other": "Άλλες", "other_devices": "Άλλες συσκευές", + "other_entities": "Άλλες οντότητες", "other_variables": "Άλλες μεταβλητές", "owned": "Δικά μου", "owner": "Κάτοχος", @@ -1460,6 +1507,7 @@ "purchase_server_description_2": "Κατάσταση υποστηρικτή", "purchase_server_title": "Διακομιστής", "purchase_settings_server_activated": "Η διαχείριση του κλειδιού προϊόντος του διακομιστή γίνεται από τον διαχειριστή", + "queue_status": "Τοποθέτηση στη ουρά {count} από {total}", "rating": "Αξιολόγηση με αστέρια", "rating_clear": "Εκκαθάριση αξιολόγησης", "rating_count": "{count, plural, one {# αστέρι} other {# αστέρια}}", @@ -1488,6 +1536,8 @@ "refreshing_faces": "Ανανεώνονται πρόσωπα", "refreshing_metadata": "Τα μεταδεδομένα ανανεώνονται", "regenerating_thumbnails": "Οι μικρογραφίες αναγεννώνται", + "remote": "Απομακρυσμένος", + "remote_assets": "Απομακρυσμένα στοιχεία", "remove": "Αφαίρεση", "remove_assets_album_confirmation": "Είστε σίγουροι ότι θέλετε να αφαιρέσετε {count, plural, one {# στοιχείο} other {# στοιχεία}} από το άλμπουμ;", "remove_assets_shared_link_confirmation": "Είστε σίγουροι ότι θέλετε να αφαιρέσετε {count, plural, one {# στοιχείο} other {# στοιχεία}} από αυτόν τον κοινόχρηστο σύνδεσμο;", @@ -1495,7 +1545,9 @@ "remove_custom_date_range": "Αφαίρεση προσαρμοσμένης χρονικής περιόδου", "remove_deleted_assets": "Αφαίρεση Διεγραμμένων Στοιχείων", "remove_from_album": "Αφαίρεση από το άλμπουμ", + "remove_from_album_action_prompt": "{count} αφαιρέθηκαν από το άλμπουμ", "remove_from_favorites": "Αφαίρεση από τα αγαπημένα", + "remove_from_lock_folder_action_prompt": "{count} αφαιρέθηκαν από τον κλειδωμένο φάκελο", "remove_from_locked_folder": "Αφαίρεση από κλειδωμένο φάκελο", "remove_from_locked_folder_confirmation": "Είστε σίγουροι ότι θέλετε να μετακινήσετε αυτές τις φωτογραφίες και τα βίντεο από τον κλειδωμένο φάκελο; Θα είναι πλέον ορατές στη βιβλιοθήκη σας.", "remove_from_shared_link": "Αφαίρεση από τον κοινόχρηστο σύνδεσμο", @@ -1523,19 +1575,25 @@ "reset_password": "Επαναφορά κωδικού πρόσβασης", "reset_people_visibility": "Επαναφορά προβολής ατόμων", "reset_pin_code": "Επαναφορά κωδικού PIN", + "reset_sqlite": "Επαναφορά SQLite βάσης δεδομένων", + "reset_sqlite_confirmation": "Είσαι σίγουρος ότι θέλεις να επαναφέρεις τη βάση δεδομένων SQLite; Θα χρειαστεί να κάνεις αποσύνδεση και επανασύνδεση για να επανασυγχρονίσεις τα δεδομένα", + "reset_sqlite_success": "Η επαναφορά της SQLite βάσης δεδομένων ολοκληρώθηκε με επιτυχία", "reset_to_default": "Επαναφορά στις προεπιλογές", "resolve_duplicates": "Επίλυση διπλοτύπων", "resolved_all_duplicates": "Επιλύθηκαν όλα τα διπλότυπα", "restore": "Ανάκτηση", "restore_all": "Ανάκτηση όλων", + "restore_trash_action_prompt": "{count} ανακτήθηκαν από τα απορρίμματα", "restore_user": "Επαναφορά χρήστη", "restored_asset": "Ανακτήθηκε το αρχείο", "resume": "Συνέχιση", "retry_upload": "Επανάληψη ανεβάσματος", "review_duplicates": "Προβολή διπλότυπων", + "review_large_files": "Επισκόπηση μεγάλων αρχείων", "role": "Ρόλος", "role_editor": "Επεξεργαστής", "role_viewer": "Θεατής", + "running": "Σε λειτουργία", "save": "Αποθήκευση", "save_to_gallery": "Αποθήκευση στη συλλογή", "saved_api_key": "Αποθηκευμένο API key", @@ -1667,6 +1725,7 @@ "settings_saved": "Οι ρυθμίσεις αποθηκεύτηκαν", "setup_pin_code": "Ρύθμιση κωδικού PIN", "share": "Κοινοποίηση", + "share_action_prompt": "Κοινή χρήση {count} στοιχείων", "share_add_photos": "Προσθήκη φωτογραφιών", "share_assets_selected": "{count} επιλεγμένα", "share_dialog_preparing": "Προετοιμασία...", @@ -1688,6 +1747,7 @@ "shared_link_clipboard_copied_massage": "Αντιγράφηκε στο πρόχειρο", "shared_link_clipboard_text": "Σύνδεσμος: {link}\nΚωδικός πρόσβασης: {password}", "shared_link_create_error": "Σφάλμα κατά τη δημιουργία κοινόχρηστου συνδέσμου", + "shared_link_custom_url_description": "Αποκτήστε πρόσβαση σε αυτόν τον κοινόχρηστο σύνδεσμο με μια προσαρμοσμένη διεύθυνση URL", "shared_link_edit_description_hint": "Εισαγάγετε την περιγραφή της κοινής χρήσης", "shared_link_edit_expire_after_option_day": "1 ημέρα", "shared_link_edit_expire_after_option_days": "{count} ημέρες", @@ -1713,6 +1773,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Διαχείριση Κοινόχρηστων Συνδέσμων", "shared_link_options": "Επιλογές κοινόχρηστου συνδέσμου", + "shared_link_password_description": "Απαίτηση κωδικού για πρόσβαση στον κοινοποιημένο σύνδεσμο", "shared_links": "Κοινόχρηστοι σύνδεσμοι", "shared_links_description": "Μοιραστείτε φωτογραφίες και βίντεο με σύνδεσμο", "shared_photos_and_videos_count": "{assetCount, plural, other {# κοινόχρηστες φωτογραφίες & βίντεο.}}", @@ -1768,6 +1829,7 @@ "sort_title": "Τίτλος", "source": "Πηγή", "stack": "Στοίβα", + "stack_action_prompt": "{count} συσσωρεύτηκαν", "stack_duplicates": "Στοίβαξη διπλότυπων", "stack_select_one_photo": "Επιλέξτε μια κύρια φωτογραφία για τη στοίβαξη", "stack_selected_photos": "Στοίβαγμα επιλεγμένων φωτογραφιών", @@ -1787,6 +1849,7 @@ "storage_quota": "Ποσοστό αποθηκευτικού χώρου", "storage_usage": "{used} από {available} σε χρήση", "submit": "Υποβολή", + "success": "Επιτυχία", "suggestions": "Προτάσεις", "sunrise_on_the_beach": "Ηλιοβασίλεμα στην παραλία", "support": "Υποστήριξη", @@ -1796,6 +1859,8 @@ "sync": "Συγχρονισμός", "sync_albums": "Συγχρονισμός άλμπουμ", "sync_albums_manual_subtitle": "Συγχρονίστε όλα τα μεταφορτωμένα βίντεο και φωτογραφίες με τα επιλεγμένα εφεδρικά άλμπουμ", + "sync_local": "Τοπικός Συγχρονισμός", + "sync_remote": "Απομακρυσμένος Συγχρονισμός", "sync_upload_album_setting_subtitle": "Δημιουργήστε και ανεβάστε τις φωτογραφίες και τα βίντεό σας στα επιλεγμένα άλμπουμ στο Immich", "tag": "Ετικέτα", "tag_assets": "Ετικετοποίηση στοιχείων", @@ -1806,6 +1871,7 @@ "tag_updated": "Ενημερώθηκε η ετικέτα: {tag}", "tagged_assets": "Ετικετοποιημένο/α {count, plural, one {# στοιχείο} other {# στοιχεία}}", "tags": "Ετικέτες", + "tap_to_run_job": "Πατήστε για να ξεκινήσει η εργασία", "template": "Πρότυπο", "theme": "Θέμα", "theme_selection": "Επιλογή θέματος", @@ -1838,6 +1904,7 @@ "total": "Σύνολο", "total_usage": "Συνολικη χρηση", "trash": "Κάδος απορριμμάτων", + "trash_action_prompt": "{count} μετακινήθηκαν στα απορρίμματα", "trash_all": "Διαγραφή Όλων", "trash_count": "Διαγραφή {count, number}", "trash_delete_asset": "Διαγραφή/Οριστ. Διαγραφή Αντικειμένου", @@ -1855,9 +1922,11 @@ "unable_to_change_pin_code": "Αδυναμία αλλαγής κωδικού PIN", "unable_to_setup_pin_code": "Αδυναμία ρύθμισης κωδικού PIN", "unarchive": "Αναίρεση αρχειοθέτησης", + "unarchive_action_prompt": "{count} αφαιρέθηκαν από το Αρχείο", "unarchived_count": "{count, plural, other {Αρχειοθετήσεις αναιρέθηκαν #}}", "undo": "Αναίρεση", "unfavorite": "Αποεπιλογή από τα αγαπημένα", + "unfavorite_action_prompt": "{count} αφαιρέθηκαν από τα Αγαπημένα", "unhide_person": "Αναίρεση απόκρυψης ατόμου", "unknown": "Άγνωστο", "unknown_country": "Άγνωστη Χώρα", @@ -1875,15 +1944,20 @@ "unselect_all_duplicates": "Αποεπιλογή όλων των διπλότυπων", "unselect_all_in": "Αποεπιλογή όλων στο {group}", "unstack": "Αποστοίβαξη", + "unstack_action_prompt": "{count} αποσυσσωρεύτηκαν", "unstacked_assets_count": "Αποστοιβάξατε {count, plural, one {# στοιχείο} other {# στοιχεία}}", + "untagged": "Χωρίς ετικέτα", "up_next": "Ακολουθεί", "updated_at": "Ενημερωμένο", "updated_password": "Ο κωδικός πρόσβασης ενημερώθηκε", "upload": "Μεταφόρτωση", + "upload_action_prompt": "{count} τοποθετήθηκαν στην ουρά για μεταφόρτωση", "upload_concurrency": "Ταυτόχρονη μεταφόρτωση", + "upload_details": "Λεπτομέρειες μεταφόρτωσης", "upload_dialog_info": "Θέλετε να αντιγράψετε (κάνετε backup) τα επιλεγμένo(α) στοιχείο(α) στο διακομιστή;", "upload_dialog_title": "Ανέβασμα στοιχείου", "upload_errors": "Η μεταφόρτωση ολοκληρώθηκε με {count, plural, one {# σφάλμα} other {# σφάλματα}}, ανανεώστε τη σελίδα για να δείτε νέα στοιχεία μεταφόρτωσης.", + "upload_finished": "Ολοκλήρωση μεταφόρτωσης", "upload_progress": "Απομένουν {remaining, number} - Ολοκληρώθηκαν {processed, number}/{total, number}", "upload_skipped_duplicates": "Παραλείφθηκαν {count, plural, one {# διπλότυπο στοιχείο} other {# διπλότυπα στοιχεία}}", "upload_status_duplicates": "Διπλότυπα", @@ -1892,6 +1966,7 @@ "upload_success": "Η μεταφόρτωση ολοκληρώθηκε, ανανεώστε τη σελίδα για να δείτε τα νέα αντικείμενα.", "upload_to_immich": "Μεταφόρτωση στο Immich ({count})", "uploading": "Μεταφορτώνεται", + "uploading_media": "Μεταφόρτωση πολυμέσων", "url": "URL", "usage": "Χρήση", "use_biometric": "Χρήση βιομετρικών στοιχείων", @@ -1912,6 +1987,7 @@ "user_usage_stats_description": "Προβολή στατιστικών χρήσης λογαριασμού", "username": "Όνομα Χρήστη", "users": "Χρήστες", + "users_added_to_album_count": "Προστέθηκε {count, plural, one {# χρήστης} other {# χρήστες}} στο άλμπουμ", "utilities": "Βοηθητικά προγράμματα", "validate": "Επικύρωση", "validate_endpoint_error": "Παρακαλώ εισάγετε ένα έγκυρο URL", @@ -1930,6 +2006,7 @@ "view_album": "Προβολή Άλμπουμ", "view_all": "Προβολή Όλων", "view_all_users": "Προβολή όλων των χρηστών", + "view_details": "Προβολή Λεπτομερειών", "view_in_timeline": "Προβολή στο χρονοδιάγραμμα", "view_link": "Προβολή σύνδεσμου", "view_links": "Προβολή συνδέσμων", diff --git a/i18n/en.json b/i18n/en.json index dfe2954c9f..700ff60c53 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -14,6 +14,7 @@ "add_a_location": "Add a location", "add_a_name": "Add a name", "add_a_title": "Add a title", + "add_birthday": "Add a birthday", "add_endpoint": "Add endpoint", "add_exclusion_pattern": "Add exclusion pattern", "add_import_path": "Add import path", @@ -44,6 +45,13 @@ "backup_database": "Create Database Dump", "backup_database_enable_description": "Enable database dumps", "backup_keep_last_amount": "Amount of previous dumps to keep", + "backup_onboarding_1_description": "offsite copy in the cloud or at another physical location.", + "backup_onboarding_2_description": "local copies on different devices. This includes the main files and a backup of those files locally.", + "backup_onboarding_3_description": "total copies of your data, including the original files. This includes 1 offsite copy and 2 local copies.", + "backup_onboarding_description": "A 3-2-1 backup strategy is recommended to protect your data. You should keep copies of your uploaded photos/videos as well as the Immich database for a comprehensive backup solution.", + "backup_onboarding_footer": "For more information about backing up Immich, please refer to the documentation.", + "backup_onboarding_parts_title": "A 3-2-1 backup includes:", + "backup_onboarding_title": "Backups", "backup_settings": "Database Dump Settings", "backup_settings_description": "Manage database dump settings.", "cleared_jobs": "Cleared jobs for: {job}", @@ -397,6 +405,7 @@ "album_cover_updated": "Album cover updated", "album_delete_confirmation": "Are you sure you want to delete the album {album}?", "album_delete_confirmation_description": "If this album is shared, other users will not be able to access it anymore.", + "album_deleted": "Album deleted", "album_info_card_backup_album_excluded": "EXCLUDED", "album_info_card_backup_album_included": "INCLUDED", "album_info_updated": "Album info updated", @@ -406,6 +415,7 @@ "album_options": "Album options", "album_remove_user": "Remove user?", "album_remove_user_confirmation": "Are you sure you want to remove {user}?", + "album_search_not_found": "No albums found matching your search", "album_share_no_users": "Looks like you have shared this album with all users or you don't have any user to share with.", "album_updated": "Album updated", "album_updated_setting_description": "Receive an email notification when a shared album has new assets", @@ -425,6 +435,7 @@ "albums_default_sort_order": "Default album sort order", "albums_default_sort_order_description": "Initial asset sort order when creating new albums.", "albums_feature_description": "Collections of assets that can be shared with other users.", + "albums_on_device_count": "Albums on device ({count})", "all": "All", "all_albums": "All albums", "all_people": "All people", @@ -508,6 +519,7 @@ "back_close_deselect": "Back, close, or deselect", "background_location_permission": "Background location permission", "background_location_permission_content": "In order to switch networks when running in the background, Immich must *always* have precise location access so the app can read the Wi-Fi network's name", + "backup": "Backup", "backup_album_selection_page_albums_device": "Albums on device ({count})", "backup_album_selection_page_albums_tap": "Tap to include, double tap to exclude", "backup_album_selection_page_assets_scatter": "Assets can scatter across multiple albums. Thus, albums can be included or excluded during the backup process.", @@ -568,9 +580,13 @@ "backup_manual_in_progress": "Upload already in progress. Try after sometime", "backup_manual_success": "Success", "backup_manual_title": "Upload status", + "backup_options": "Backup Options", "backup_options_page_title": "Backup options", "backup_setting_subtitle": "Manage background and foreground upload settings", + "backup_settings_subtitle": "Manage upload settings", "backward": "Backward", + "beta_sync": "Beta Sync Status", + "beta_sync_subtitle": "Manage the new sync system", "biometric_auth_enabled": "Biometric authentication enabled", "biometric_locked_out": "You are locked out of biometric authentication", "biometric_no_options": "No biometric options available", @@ -588,7 +604,7 @@ "cache_settings_clear_cache_button": "Clear cache", "cache_settings_clear_cache_button_title": "Clears the app's cache. This will significantly impact the app's performance until the cache has rebuilt.", "cache_settings_duplicated_assets_clear_button": "CLEAR", - "cache_settings_duplicated_assets_subtitle": "Photos and videos that are black listed by the app", + "cache_settings_duplicated_assets_subtitle": "Photos and videos that are ignore listed by the app", "cache_settings_duplicated_assets_title": "Duplicated Assets ({count})", "cache_settings_statistics_album": "Library thumbnails", "cache_settings_statistics_full": "Full images", @@ -605,6 +621,7 @@ "cancel": "Cancel", "cancel_search": "Cancel search", "canceled": "Canceled", + "canceling": "Canceling", "cannot_merge_people": "Cannot merge people", "cannot_undo_this_action": "You cannot undo this action!", "cannot_update_the_description": "Cannot update the description", @@ -636,6 +653,7 @@ "clear": "Clear", "clear_all": "Clear all", "clear_all_recent_searches": "Clear all recent searches", + "clear_file_cache": "Clear File Cache", "clear_message": "Clear message", "clear_value": "Clear value", "client_cert_dialog_msg_confirm": "OK", @@ -718,6 +736,7 @@ "current_server_address": "Current server address", "custom_locale": "Custom Locale", "custom_locale_description": "Format dates and numbers based on the language and the region", + "custom_url": "Custom URL", "daily_title_text_date": "E, MMM dd", "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "Dark", @@ -737,7 +756,8 @@ "default_locale": "Default Locale", "default_locale_description": "Format dates and numbers based on your browser locale", "delete": "Delete", - "delete_action_prompt": "{count} deleted permanently", + "delete_action_confirmation_message": "Are you sure you want to delete this asset? This action will move the asset to the server's trash and will prompt if you want to delete it locally", + "delete_action_prompt": "{count} deleted", "delete_album": "Delete album", "delete_api_key_prompt": "Are you sure you want to delete this API key?", "delete_dialog_alert": "These items will be permanently deleted from Immich and from your device", @@ -755,6 +775,8 @@ "delete_local_dialog_ok_backed_up_only": "Delete Backed Up Only", "delete_local_dialog_ok_force": "Delete Anyway", "delete_others": "Delete others", + "delete_permanently": "Delete permanently", + "delete_permanently_action_prompt": "{count} deleted permanently", "delete_shared_link": "Delete shared link", "delete_shared_link_dialog_title": "Delete Shared Link", "delete_tag": "Delete tag", @@ -765,6 +787,7 @@ "description": "Description", "description_input_hint_text": "Add description...", "description_input_submit_error": "Error updating description, check the log for more details", + "deselect_all": "Deselect All", "details": "Details", "direction": "Direction", "disabled": "Disabled", @@ -782,6 +805,7 @@ "documentation": "Documentation", "done": "Done", "download": "Download", + "download_action_prompt": "Downloading {count} assets", "download_canceled": "Download canceled", "download_complete": "Download complete", "download_enqueue": "Download enqueued", @@ -808,8 +832,10 @@ "edit": "Edit", "edit_album": "Edit album", "edit_avatar": "Edit avatar", + "edit_birthday": "Edit Birthday", "edit_date": "Edit date", "edit_date_and_time": "Edit date and time", + "edit_date_and_time_action_prompt": "{count} date and time edited", "edit_description": "Edit description", "edit_description_prompt": "Please select a new description:", "edit_exclusion_pattern": "Edit exclusion pattern", @@ -838,6 +864,7 @@ "empty_trash": "Empty trash", "empty_trash_confirmation": "Are you sure you want to empty the trash? This will remove all the assets in trash permanently from Immich.\nYou cannot undo this action!", "enable": "Enable", + "enable_backup": "Enable Backup", "enable_biometric_auth_description": "Enter your PIN code to enable biometric authentication", "enabled": "Enabled", "end_date": "End date", @@ -974,6 +1001,7 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "Add Description...", + "exif_bottom_sheet_description_error": "Error updating description", "exif_bottom_sheet_details": "DETAILS", "exif_bottom_sheet_location": "LOCATION", "exif_bottom_sheet_people": "PEOPLE", @@ -994,6 +1022,8 @@ "explorer": "Explorer", "export": "Export", "export_as_json": "Export as JSON", + "export_database": "Export Database", + "export_database_description": "Export the SQLite database", "extension": "Extension", "external": "External", "external_libraries": "External Libraries", @@ -1045,6 +1075,9 @@ "haptic_feedback_switch": "Enable haptic feedback", "haptic_feedback_title": "Haptic Feedback", "has_quota": "Has quota", + "hash_asset": "Hash asset", + "hashed_assets": "Hashed assets", + "hashing": "Hashing", "header_settings_add_header_tip": "Add Header", "header_settings_field_validator_msg": "Value cannot be empty", "header_settings_header_name_input": "Header name", @@ -1077,6 +1110,7 @@ "host": "Host", "hour": "Hour", "id": "ID", + "idle": "Idle", "ignore_icloud_photos": "Ignore iCloud photos", "ignore_icloud_photos_description": "Photos that are stored on iCloud will not be uploaded to the Immich server", "image": "Image", @@ -1134,6 +1168,7 @@ "language_no_results_title": "No languages found", "language_search_hint": "Search languages...", "language_setting_description": "Select your preferred language", + "large_files": "Large Files", "last_seen": "Last seen", "latest_version": "Latest Version", "latitude": "Latitude", @@ -1153,13 +1188,14 @@ "light": "Light", "like_deleted": "Like deleted", "link_motion_video": "Link motion video", - "link_options": "Link options", "link_to_oauth": "Link to OAuth", "linked_oauth_account": "Linked OAuth account", "list": "List", "loading": "Loading", "loading_search_results_failed": "Loading search results failed", + "local": "Local", "local_asset_cast_failed": "Unable to cast an asset that is not uploaded to the server", + "local_assets": "Local Assets", "local_network": "Local network", "local_network_sheet_info": "The app will connect to the server through this URL when using the specified Wi-Fi network", "location_permission": "Location permission", @@ -1216,8 +1252,7 @@ "manage_your_devices": "Manage your logged-in devices", "manage_your_oauth_connection": "Manage your OAuth connection", "map": "Map", - "map_assets_in_bound": "{count} photo", - "map_assets_in_bounds": "{count} photos", + "map_assets_in_bounds": "{count, plural, one {# photo} other {# photos}}", "map_cannot_get_user_location": "Cannot get user's location", "map_location_dialog_yes": "Yes", "map_location_picker_page_use_location": "Use this location", @@ -1281,6 +1316,9 @@ "my_albums": "My albums", "name": "Name", "name_or_nickname": "Name or nickname", + "network_requirement_photos_upload": "Use cellular data to backup photos", + "network_requirement_videos_upload": "Use cellular data to backup videos", + "network_requirements_updated": "Network requirements changed, resetting backup queue", "networking_settings": "Networking", "networking_subtitle": "Manage the server endpoint settings", "never": "Never", @@ -1316,6 +1354,7 @@ "no_results": "No results", "no_results_description": "Try a synonym or more general keyword", "no_shared_albums_message": "Create an album to share photos and videos with people in your network", + "no_uploads_in_progress": "No uploads in progress", "not_in_any_album": "Not in any album", "not_selected": "Not selected", "note_apply_storage_label_to_previously_uploaded assets": "Note: To apply the Storage Label to previously uploaded assets, run the", @@ -1353,6 +1392,7 @@ "original": "original", "other": "Other", "other_devices": "Other devices", + "other_entities": "Other entities", "other_variables": "Other variables", "owned": "Owned", "owner": "Owner", @@ -1484,6 +1524,7 @@ "purchase_server_description_2": "Supporter status", "purchase_server_title": "Server", "purchase_settings_server_activated": "The server product key is managed by the admin", + "queue_status": "Queuing {count}/{total}", "rating": "Star rating", "rating_clear": "Clear rating", "rating_count": "{count, plural, one {# star} other {# stars}}", @@ -1512,6 +1553,8 @@ "refreshing_faces": "Refreshing faces", "refreshing_metadata": "Refreshing metadata", "regenerating_thumbnails": "Regenerating thumbnails", + "remote": "Remote", + "remote_assets": "Remote Assets", "remove": "Remove", "remove_assets_album_confirmation": "Are you sure you want to remove {count, plural, one {# asset} other {# assets}} from the album?", "remove_assets_shared_link_confirmation": "Are you sure you want to remove {count, plural, one {# asset} other {# assets}} from this shared link?", @@ -1549,19 +1592,25 @@ "reset_password": "Reset password", "reset_people_visibility": "Reset people visibility", "reset_pin_code": "Reset PIN code", + "reset_sqlite": "Reset SQLite Database", + "reset_sqlite_confirmation": "Are you sure you want to reset the SQLite database? You will need to log out and log in again to resync the data", + "reset_sqlite_success": "Successfully reset the SQLite database", "reset_to_default": "Reset to default", "resolve_duplicates": "Resolve duplicates", "resolved_all_duplicates": "Resolved all duplicates", "restore": "Restore", "restore_all": "Restore all", + "restore_trash_action_prompt": "{count} restored from trash", "restore_user": "Restore user", "restored_asset": "Restored asset", "resume": "Resume", "retry_upload": "Retry upload", "review_duplicates": "Review duplicates", + "review_large_files": "Review large files", "role": "Role", "role_editor": "Editor", "role_viewer": "Viewer", + "running": "Running", "save": "Save", "save_to_gallery": "Save to gallery", "saved_api_key": "Saved API Key", @@ -1715,6 +1764,7 @@ "shared_link_clipboard_copied_massage": "Copied to clipboard", "shared_link_clipboard_text": "Link: {link}\nPassword: {password}", "shared_link_create_error": "Error while creating shared link", + "shared_link_custom_url_description": "Access this shared link with a custom URL", "shared_link_edit_description_hint": "Enter the share description", "shared_link_edit_expire_after_option_day": "1 day", "shared_link_edit_expire_after_option_days": "{count} days", @@ -1740,6 +1790,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Manage Shared links", "shared_link_options": "Shared link options", + "shared_link_password_description": "Require a password to access this shared link", "shared_links": "Shared links", "shared_links_description": "Share photos and videos with a link", "shared_photos_and_videos_count": "{assetCount, plural, other {# shared photos & videos.}}", @@ -1815,6 +1866,7 @@ "storage_quota": "Storage Quota", "storage_usage": "{used} of {available} used", "submit": "Submit", + "success": "Success", "suggestions": "Suggestions", "sunrise_on_the_beach": "Sunrise on the beach", "support": "Support", @@ -1824,6 +1876,8 @@ "sync": "Sync", "sync_albums": "Sync albums", "sync_albums_manual_subtitle": "Sync all uploaded videos and photos to the selected backup albums", + "sync_local": "Sync Local", + "sync_remote": "Sync Remote", "sync_upload_album_setting_subtitle": "Create and upload your photos and videos to the selected albums on Immich", "tag": "Tag", "tag_assets": "Tag assets", @@ -1834,6 +1888,7 @@ "tag_updated": "Updated tag: {tag}", "tagged_assets": "Tagged {count, plural, one {# asset} other {# assets}}", "tags": "Tags", + "tap_to_run_job": "Tap to run job", "template": "Template", "theme": "Theme", "theme_selection": "Theme selection", @@ -1913,10 +1968,13 @@ "updated_at": "Updated", "updated_password": "Updated password", "upload": "Upload", + "upload_action_prompt": "{count} queued for upload", "upload_concurrency": "Upload concurrency", + "upload_details": "Upload Details", "upload_dialog_info": "Do you want to backup the selected Asset(s) to the server?", "upload_dialog_title": "Upload Asset", "upload_errors": "Upload completed with {count, plural, one {# error} other {# errors}}, refresh the page to see new upload assets.", + "upload_finished": "Upload finished", "upload_progress": "Remaining {remaining, number} - Processed {processed, number}/{total, number}", "upload_skipped_duplicates": "Skipped {count, plural, one {# duplicate asset} other {# duplicate assets}}", "upload_status_duplicates": "Duplicates", @@ -1925,6 +1983,7 @@ "upload_success": "Upload success, refresh the page to see new upload assets.", "upload_to_immich": "Upload to Immich ({count})", "uploading": "Uploading", + "uploading_media": "Uploading media", "url": "URL", "usage": "Usage", "use_biometric": "Use biometric", @@ -1964,6 +2023,7 @@ "view_album": "View Album", "view_all": "View All", "view_all_users": "View all users", + "view_details": "View Details", "view_in_timeline": "View in timeline", "view_link": "View link", "view_links": "View links", diff --git a/i18n/es.json b/i18n/es.json index 7bfdc678df..e78c6936c7 100644 --- a/i18n/es.json +++ b/i18n/es.json @@ -2,7 +2,7 @@ "about": "Acerca de", "account": "Cuenta", "account_settings": "Ajustes de la cuenta", - "acknowledge": "De acuerdo", + "acknowledge": "Aceptar", "action": "Acción", "action_common_update": "Actualizar", "actions": "Acciones", @@ -14,13 +14,13 @@ "add_a_location": "Agregar ubicación", "add_a_name": "Agregar nombre", "add_a_title": "Agregar título", - "add_endpoint": "Añadir endpoint", + "add_endpoint": "Agregar endpoint", "add_exclusion_pattern": "Agregar patrón de exclusión", "add_import_path": "Agregar ruta de importación", "add_location": "Agregar ubicación", "add_more_users": "Agregar más usuarios", "add_partner": "Agregar compañero", - "add_path": "Agregar carpeta", + "add_path": "Agregar ruta", "add_photos": "Agregar fotos", "add_tag": "Agregar etiqueta", "add_to": "Agregar a…", @@ -63,8 +63,8 @@ "exclusion_pattern_description": "Los patrones de exclusión te permiten ignorar archivos y carpetas al escanear tu biblioteca. Es útil si tienes carpetas que contienen archivos que no deseas importar, por ejemplo archivos RAW.", "external_library_management": "Gestión de bibliotecas externas", "face_detection": "Detección de caras", - "face_detection_description": "Detecta las caras en los activos mediante aprendizaje automático. En el caso de los vídeos, solo se tiene en cuenta la miniatura. \"Actualizar\" (re)procesará todos los elementos. \"Restablecer\" borra además todos los datos de caras actuales. \"Falta\" pone en cola los elementos que aún no se han procesado. Las caras detectadas se pondrán en cola para el reconocimiento facial una vez finalizada la detección, agrupándolos en personas existentes o nuevas.", - "facial_recognition_job_description": "Agrupa las caras detectadas en personas. Este paso se ejecuta una vez finalizada la detección de caras. \"Restablecer\" (re)agrupa todas las caras. \"Falta\" pone en cola las caras que no tienen asignada una persona.", + "face_detection_description": "Detecta las caras en los elementos mediante aprendizaje automático. En el caso de los vídeos, solo se tiene en cuenta la miniatura. \"Actualizar\" (re)procesará todos los elementos. \"Restablecer\" borra además todos los datos de caras actuales. \"Faltante\" pone en cola los elementos que aún no se han procesado. Las caras detectadas se pondrán en cola para el reconocimiento facial una vez finalizada la detección, agrupándolos en personas existentes o nuevas.", + "facial_recognition_job_description": "Agrupa las caras detectadas en personas. Este paso se realiza después de completar la detección de caras. \"Restablecer\" (re)agrupa todas las caras. \"Faltante\" pone en cola las caras que no tienen una persona asignada.", "failed_job_command": "El comando {command} ha fallado para la tarea: {job}", "force_delete_user_warning": "CUIDADO: Esta acción eliminará inmediatamente el usuario y todos los elementos. Esta accion no se puede deshacer y los archivos no pueden ser recuperados.", "image_format": "Formato", @@ -166,9 +166,23 @@ "metadata_settings_description": "Administrar la configuración de metadatos", "migration_job": "Migración", "migration_job_description": "Migrar miniaturas de archivos y caras a la estructura de carpetas más reciente", + "nightly_tasks_cluster_faces_setting_description": "Ejecutar reconocimiento facial en caras detectadas recientemente", + "nightly_tasks_cluster_new_faces_setting": "Agrupar caras nuevas", + "nightly_tasks_database_cleanup_setting": "Tareas de limpieza de base de datos", + "nightly_tasks_database_cleanup_setting_description": "Limpiar datos antiguos y caducados de la base de datos", + "nightly_tasks_generate_memories_setting": "Generar recuerdos", + "nightly_tasks_generate_memories_setting_description": "Crear nuevos recuerdos a partir de activos", + "nightly_tasks_missing_thumbnails_setting": "Generar miniaturas faltantes", + "nightly_tasks_missing_thumbnails_setting_description": "Poner en cola a activos sin miniaturas para la generación de miniaturas", + "nightly_tasks_settings": "Configuración de Tareas Nocturnas", + "nightly_tasks_settings_description": "Gestionar Tareas Nocturnas", + "nightly_tasks_start_time_setting": "Tiempo de inicio", + "nightly_tasks_start_time_setting_description": "El tiempo cuando el servidor comienza a ejecutar las tareas nocturnas", + "nightly_tasks_sync_quota_usage_setting": "Uso de la cuota de sincronización", + "nightly_tasks_sync_quota_usage_setting_description": "Actualizar la cuota de almacenamiento del usuario, según el uso actual", "no_paths_added": "No se han añadido carpetas", "no_pattern_added": "No se han añadido patrones", - "note_apply_storage_label_previous_assets": "Nota: para aplicar una Etiqueta de Almacenamiento a un elemento anteriormente cargado, lanza el", + "note_apply_storage_label_previous_assets": "Nota: para aplicar una Etiqueta de Almacenamiento a un elemento anteriormente subido, lanza el", "note_cannot_be_changed_later": "NOTA: ¡No se puede cambiar posteriormente!", "notification_email_from_address": "Desde", "notification_email_from_address_description": "Dirección de correo electrónico del remitente, por ejemplo: \"Immich Photo Server \". Asegúrate de utilizar una dirección desde la que puedas enviar correos electrónicos.", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "URI de redireccionamiento móvil", "oauth_mobile_redirect_uri_override": "Sobreescribir URI de redirección móvil", "oauth_mobile_redirect_uri_override_description": "Habilitar cuando el proveedor de OAuth no permite una URI móvil, como ''{callback}''", + "oauth_role_claim": "Concesión de rol", + "oauth_role_claim_description": "Otorgar acceso de administrador automáticamente según la presencia de esta concesión. La concesión puede tener \"usuario\" o \"admin\".", "oauth_settings": "OAuth", "oauth_settings_description": "Administrar la configuración de inicio de sesión de OAuth", "oauth_settings_more_details": "Para más detalles acerca de esta característica, consulte la documentación.", @@ -205,7 +221,7 @@ "oauth_storage_quota_claim_description": "Establezca automáticamente la cuota de almacenamiento del usuario al valor de esta solicitud.", "oauth_storage_quota_default": "Cuota de almacenamiento predeterminada (GiB)", "oauth_storage_quota_default_description": "Cuota en GiB que se utilizará cuando no se proporcione ninguna por defecto.", - "oauth_timeout": "Expiración de solicitud", + "oauth_timeout": "Límite de tiempo para la solicitud", "oauth_timeout_description": "Tiempo de espera de solicitudes en milisegundos", "password_enable_description": "Iniciar sesión con correo electrónico y contraseña", "password_settings": "Contraseña de Acceso", @@ -247,7 +263,7 @@ "storage_template_onboarding_description_v2": "Al habilitar esta función, los archivos se organizarán automáticamente según la plantilla definida por el usuario. Para más información, consulte la documentación.", "storage_template_path_length": "Límite aproximado de la longitud de la ruta: {length, number}/{limit, number}", "storage_template_settings": "Plantilla de almacenamiento", - "storage_template_settings_description": "Administrar la estructura de carpetas y el nombre de archivo del recurso cargado", + "storage_template_settings_description": "Administrar la estructura de carpetas y el nombre de archivo del recurso subido", "storage_template_user_label": "{label} es la etiqueta de almacenamiento del usuario", "system_settings": "Ajustes del Sistema", "tag_cleanup_job": "Limpieza de etiquetas", @@ -357,10 +373,12 @@ "admin_password": "Contraseña del Administrador", "administration": "Administración", "advanced": "Avanzada", + "advanced_settings_beta_timeline_subtitle": "Prueba la nueva experiencia de la aplicación", + "advanced_settings_beta_timeline_title": "Cronología beta", "advanced_settings_enable_alternate_media_filter_subtitle": "Usa esta opción para filtrar medios durante la sincronización según criterios alternativos. Intenta esto solo si tienes problemas con que la aplicación detecte todos los álbumes.", "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTAL] Usar filtro alternativo de sincronización de álbumes del dispositivo", "advanced_settings_log_level_title": "Nivel de registro: {level}", - "advanced_settings_prefer_remote_subtitle": "Algunos dispositivos tardan mucho en cargar las miniaturas de los elementos encontrados en el dispositivo. Activa esta opción para cargar imágenes remotas en su lugar.", + "advanced_settings_prefer_remote_subtitle": "Algunos dispositivos tardan mucho en cargar las miniaturas desde los archivos locales. Activa esta opción para cargar imágenes remotas en su lugar.", "advanced_settings_prefer_remote_title": "Preferir imágenes remotas", "advanced_settings_proxy_headers_subtitle": "Configura headers HTTP que Immich incluirá en cada petición de red", "advanced_settings_proxy_headers_title": "Cabeceras Proxy", @@ -379,6 +397,7 @@ "album_cover_updated": "Portada del álbum actualizada", "album_delete_confirmation": "¿Estás seguro de que deseas eliminar el álbum {album}?", "album_delete_confirmation_description": "Si este álbum se comparte, otros usuarios ya no podrán acceder a él.", + "album_deleted": "Álbum eliminado", "album_info_card_backup_album_excluded": "EXCLUIDOS", "album_info_card_backup_album_included": "INCLUIDOS", "album_info_updated": "Información del álbum actualizada", @@ -388,6 +407,7 @@ "album_options": "Opciones del Album", "album_remove_user": "¿Eliminar usuario?", "album_remove_user_confirmation": "¿Estás seguro de que quieres eliminar a {user}?", + "album_search_not_found": "No se encontraron álbumes que coincidan con tu búsqueda", "album_share_no_users": "Parece que has compartido este álbum con todos los usuarios o no tienes ningún usuario con quien compartirlo.", "album_updated": "Album actualizado", "album_updated_setting_description": "Reciba una notificación por correo electrónico cuando un álbum compartido tenga nuevos archivos", @@ -405,8 +425,9 @@ "albums": "Álbumes", "albums_count": "{count, plural, one {{count, number} Álbum} other {{count, number} Álbumes}}", "albums_default_sort_order": "Ordenación por defecto de los álbumes", - "albums_default_sort_order_description": "Orden de clasificación inicial de los activos al crear nuevos álbumes.", - "albums_feature_description": "Colecciones de activos que pueden compartirse con otros usuarios.", + "albums_default_sort_order_description": "Orden de clasificación inicial de los recursos al crear nuevos álbumes.", + "albums_feature_description": "Colecciones de recursos que pueden ser compartidos con otros usuarios.", + "albums_on_device_count": "Álbumes en el dispositivo ({count})", "all": "Todos", "all_albums": "Todos los albums", "all_people": "Todas las personas", @@ -427,6 +448,7 @@ "app_settings": "Ajustes de Aplicacion", "appears_in": "Aparece en", "archive": "Archivo", + "archive_action_prompt": "{count} añadidos al Archivo", "archive_or_unarchive_photo": "Archivar o restaurar foto", "archive_page_no_archived_assets": "No se encontraron elementos archivados", "archive_page_title": "Archivo ({count})", @@ -464,13 +486,12 @@ "assets": "elementos", "assets_added_count": "Añadido {count, plural, one {# asset} other {# assets}}", "assets_added_to_album_count": "Añadido {count, plural, one {# asset} other {# assets}} al álbum", - "assets_added_to_name_count": "Añadido {count, plural, one {# asset} other {# assets}} a {hasName, select, true {{name}} other {new album}}", - "assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} no pueden ser añadidos al album", + "assets_cannot_be_added_to_album_count": "{count, plural, one {El recurso no puede ser añadido al álbum} other {Los recursos no pueden ser añadidos al álbum}}", "assets_count": "{count, plural, one {# activo} other {# activos}}", "assets_deleted_permanently": "{count} elemento(s) eliminado(s) permanentemente", "assets_deleted_permanently_from_server": "{count} recurso(s) eliminado(s) de forma permanente del servidor de Immich", - "assets_downloaded_failed": "{count, plural, one {Descargado archivo # - {error} archivo fallido} other {Descargados # archivos - {error} archivos fallidos}}", - "assets_downloaded_successfully": "{count, plural, one {Archivo # descargado correctamente} other {Archivos # descargados correctamente}}", + "assets_downloaded_failed": "{count, plural, one {# archivo descargado - {error} archivo fallido} other {# archivos descargados - {error} archivos fallidos}}", + "assets_downloaded_successfully": "{count, plural, one {# archivo descargado exitosamente} other {# archivos descargados exitosamente}}", "assets_moved_to_trash_count": "{count, plural, one {# elemento movido} other {# elementos movidos}} a la papelera", "assets_permanently_deleted_count": "Eliminado permanentemente {count, plural, one {# elemento} other {# elementos}}", "assets_removed_count": "Eliminado {count, plural, one {# elemento} other {# elementos}}", @@ -490,6 +511,7 @@ "back_close_deselect": "Atrás, cerrar o anular la selección", "background_location_permission": "Permiso de ubicación en segundo plano", "background_location_permission_content": "Para poder cambiar de red mientras se ejecuta en segundo plano, Immich debe tener *siempre* acceso a la ubicación precisa para que la aplicación pueda leer el nombre de la red Wi-Fi", + "backup": "Copia de Seguridad", "backup_album_selection_page_albums_device": "Álbumes en el dispositivo ({count})", "backup_album_selection_page_albums_tap": "Toque para incluir, doble toque para excluir", "backup_album_selection_page_assets_scatter": "Los elementos pueden dispersarse en varios álbumes. De este modo, los álbumes pueden ser incluidos o excluidos durante el proceso de copia de seguridad.", @@ -553,6 +575,8 @@ "backup_options_page_title": "Opciones de Copia de Seguridad", "backup_setting_subtitle": "Administra las configuraciones de respaldo en segundo y primer plano", "backward": "Retroceder", + "beta_sync": "Estado de Sincronización Beta", + "beta_sync_subtitle": "Administrar el nuevo sistema de sincronización", "biometric_auth_enabled": "Autentificación biométrica habilitada", "biometric_locked_out": "Estás bloqueado de la autentificación biométrica", "biometric_no_options": "Sin opciones biométricas disponibles", @@ -570,7 +594,7 @@ "cache_settings_clear_cache_button": "Borrar caché", "cache_settings_clear_cache_button_title": "Borra la caché de la aplicación. Esto afectará significativamente el rendimiento de la aplicación hasta que se reconstruya la caché.", "cache_settings_duplicated_assets_clear_button": "LIMPIAR", - "cache_settings_duplicated_assets_subtitle": "Fotos y vídeos en la lista negra de la app", + "cache_settings_duplicated_assets_subtitle": "Fotos y vídeos ignorados por la aplicación", "cache_settings_duplicated_assets_title": "Elementos duplicados ({count})", "cache_settings_statistics_album": "Miniaturas de la biblioteca", "cache_settings_statistics_full": "Imágenes completas", @@ -587,6 +611,7 @@ "cancel": "Cancelar", "cancel_search": "Cancelar búsqueda", "canceled": "Cancelado", + "canceling": "Cancelando", "cannot_merge_people": "No se pueden fusionar personas", "cannot_undo_this_action": "¡No puedes deshacer esta acción!", "cannot_update_the_description": "No se puede actualizar la descripción", @@ -700,10 +725,11 @@ "current_server_address": "Dirección actual del servidor", "custom_locale": "Configuración regional personalizada", "custom_locale_description": "Formatear fechas y números según el idioma y la región", + "custom_url": "URL personalizada", "daily_title_text_date": "E dd, MMM", "daily_title_text_date_year": "E dd de MMM, yyyy", "dark": "Oscuro", - "darkTheme": "Activar tema oscuro", + "dark_theme": "Alternar tema oscuro", "date_after": "Fecha posterior", "date_and_time": "Fecha y Hora", "date_before": "Fecha anterior", @@ -719,12 +745,14 @@ "default_locale": "Configuración regional predeterminada", "default_locale_description": "Formatee fechas y números según la configuración regional de su navegador", "delete": "Eliminar", + "delete_action_confirmation_message": "¿Está seguro que desea eliminar este archivo? Esta acción lo moverá a la papelera del servidor y le preguntará si desea eliminarlo localmente", + "delete_action_prompt": "{count} eliminados", "delete_album": "Eliminar álbum", "delete_api_key_prompt": "¿Está seguro de que desea eliminar esta clave API?", "delete_dialog_alert": "Estos elementos serán eliminados permanentemente de Immich y de tu dispositivo", - "delete_dialog_alert_local": "Estas imágenes van a ser borradas de tu dispositivo, pero seguirán disponibles en el servidor Immich", - "delete_dialog_alert_local_non_backed_up": "Algunas de las imágenes no tienen copia de seguridad y serán borradas de forma permanente de tu dispositivo", - "delete_dialog_alert_remote": "Estas imágenes van a ser borradas de forma permanente del servidor Immich", + "delete_dialog_alert_local": "Estos elementos se eliminarán permanente de tu dispositivo pero seguirán disponibles en el servidor de Immich", + "delete_dialog_alert_local_non_backed_up": "Algunos de los elementos no tienen copia de seguridad en Immich y serán borrados permanentemente de tu dispositivo", + "delete_dialog_alert_remote": "Estas imágenes van a ser borradas permanentemente del servidor de Immich", "delete_dialog_ok_force": "Borrar de todos modos", "delete_dialog_title": "Eliminar Permanentemente", "delete_duplicates_confirmation": "¿Está seguro de que desea eliminar permanentemente estos duplicados?", @@ -732,9 +760,12 @@ "delete_key": "Eliminar clave", "delete_library": "Eliminar biblioteca", "delete_link": "Eliminar enlace", + "delete_local_action_prompt": "{count} eliminados localmente", "delete_local_dialog_ok_backed_up_only": "Borrar solo las que tengan copia de seguridad", "delete_local_dialog_ok_force": "Borrar de todos modos", "delete_others": "Eliminar otros", + "delete_permanently": "Eliminar permanentemente", + "delete_permanently_action_prompt": "{count} eliminados permanentemente", "delete_shared_link": "Eliminar enlace compartido", "delete_shared_link_dialog_title": "Eliminar enlace compartido", "delete_tag": "Eliminar etiqueta", @@ -745,10 +776,12 @@ "description": "Descripción", "description_input_hint_text": "Agregar descripción...", "description_input_submit_error": "Error al actualizar la descripción, verifica el registro para obtener más detalles", + "deselect_all": "Deseleccionar Todo", "details": "Detalles", "direction": "Dirección", "disabled": "Deshabilitado", "disallow_edits": "Bloquear edición", + "discord": "Discord", "discover": "Descubrir", "discovered_devices": "Dispositivos descubiertos", "dismiss_all_errors": "Descartar todos los errores", @@ -761,6 +794,7 @@ "documentation": "Documentación", "done": "Hecho", "download": "Descargar", + "download_action_prompt": "Descargando {count} archivos", "download_canceled": "Descarga cancelada", "download_complete": "Descarga completada", "download_enqueue": "Descarga en cola", @@ -798,6 +832,7 @@ "edit_key": "Editar clave", "edit_link": "Editar enlace", "edit_location": "Editar ubicación", + "edit_location_action_prompt": "{count} ubicaciones actualizadas", "edit_location_dialog_title": "Ubicación", "edit_name": "Cambiar nombre", "edit_people": "Editar persona", @@ -816,6 +851,7 @@ "empty_trash": "Vaciar papelera", "empty_trash_confirmation": "¿Estás seguro de que quieres vaciar la papelera? Esto eliminará permanentemente todos los archivos de la basura de Immich.\n¡No puedes deshacer esta acción!", "enable": "Habilitar", + "enable_backup": "Habilitar Copia de Seguridad", "enable_biometric_auth_description": "Introduce tu código PIN para habilitar la autentificación biométrica", "enabled": "Habilitado", "end_date": "Fecha final", @@ -972,6 +1008,8 @@ "explorer": "Explorador", "export": "Exportar", "export_as_json": "Exportar a JSON", + "export_database": "Exportar Base de Datos", + "export_database_description": "Exportar la Base de Datos SQLite", "extension": "Extensión", "external": "Externo", "external_libraries": "Bibliotecas Externas", @@ -983,6 +1021,7 @@ "failed_to_load_assets": "Error al cargar los activos", "failed_to_load_folder": "No se pudo cargar la carpeta", "favorite": "Favorito", + "favorite_action_prompt": "{count} añadidos a Favoritos", "favorite_or_unfavorite_photo": "Foto favorita o no favorita", "favorites": "Favoritos", "favorites_page_no_favorites": "No se encontraron elementos marcados como favoritos", @@ -1022,6 +1061,9 @@ "haptic_feedback_switch": "Activar respuesta háptica", "haptic_feedback_title": "Respuesta Háptica", "has_quota": "Su cuota", + "hash_asset": "Generar hash del archivo", + "hashed_assets": "Archivos con hash generado", + "hashing": "Generando hash", "header_settings_add_header_tip": "Añadir cabecera", "header_settings_field_validator_msg": "El valor no puede estar vacío", "header_settings_header_name_input": "Nombre de la cabecera", @@ -1054,6 +1096,7 @@ "host": "Host", "hour": "Hora", "id": "ID", + "idle": "Inactivo", "ignore_icloud_photos": "Ignorar fotos de iCloud", "ignore_icloud_photos_description": "Las fotos almacenadas en iCloud no se subirán a Immich", "image": "Imagen", @@ -1111,6 +1154,7 @@ "language_no_results_title": "No se han encontrado idiomas", "language_search_hint": "Buscar idiomas...", "language_setting_description": "Selecciona tu idioma preferido", + "large_files": "Archivos Grandes", "last_seen": "Ultima vez visto", "latest_version": "Última versión", "latitude": "Latitud", @@ -1126,16 +1170,18 @@ "library_page_sort_created": "Creado más recientemente", "library_page_sort_last_modified": "Última modificación", "library_page_sort_title": "Título del álbum", + "licenses": "Licencias", "light": "Claro", "like_deleted": "Me gusta eliminado", "link_motion_video": "Enlazar vídeo en movimiento", - "link_options": "Opciones de enlace", "link_to_oauth": "Enlace a OAuth", "linked_oauth_account": "Cuenta OAuth vinculada", "list": "Listar", "loading": "Cargando", "loading_search_results_failed": "Error al cargar los resultados de la búsqueda", - "local_asset_cast_failed": "No se puede emitir un activo que no está cargado en el servidor", + "local": "Local", + "local_asset_cast_failed": "No es posible transmitir un recurso que no está subido al servidor", + "local_assets": "Archivos Locales", "local_network": "Red local", "local_network_sheet_info": "La aplicación se conectará al servidor a través de esta URL cuando utilice la red Wi-Fi especificada", "location_permission": "Permiso de ubicación", @@ -1192,8 +1238,7 @@ "manage_your_devices": "Administre sus dispositivos conectados", "manage_your_oauth_connection": "Administra tu conexión OAuth", "map": "Mapa", - "map_assets_in_bound": "{count} foto", - "map_assets_in_bounds": "{count} fotos", + "map_assets_in_bounds": "{count, plural, one {# foto} other {# fotos}}", "map_cannot_get_user_location": "No se pudo obtener la posición del usuario", "map_location_dialog_yes": "Sí", "map_location_picker_page_use_location": "Usar esta ubicación", @@ -1238,13 +1283,14 @@ "merged_people_count": "Fusionada {count, plural, one {# persona} other {# personas}}", "minimize": "Minimizar", "minute": "Minuto", - "missing": "Perdido", + "missing": "Faltante", "model": "Modelo", "month": "Mes", - "monthly_title_text_date_format": "MMMM y", + "monthly_title_text_date_format": "MMMM a", "more": "Mas", "move": "Mover", "move_off_locked_folder": "Mover fuera de la carpeta protegida", + "move_to_lock_folder_action_prompt": "{count} añadidos a la carpeta protegida", "move_to_locked_folder": "Mover a la carpeta protegida", "move_to_locked_folder_confirmation": "Estas fotos y vídeos serán eliminados de todos los álbumes y sólo podrán ser vistos desde la carpeta protegida", "moved_to_archive": "Movido(s) {count, plural, one {# recurso} other {# recursos}} a archivo", @@ -1277,7 +1323,7 @@ "no_archived_assets_message": "Archive fotos y videos para ocultarlos de su vista de Fotos", "no_assets_message": "HAZ CLIC PARA SUBIR TU PRIMERA FOTO", "no_assets_to_show": "No hay elementos a mostrar", - "no_cast_devices_found": "Dispositivos de difusión no encontrados", + "no_cast_devices_found": "No se encontraron dispositivos de transmisión", "no_duplicates_found": "No se encontraron duplicados.", "no_exif_info_available": "No hay información exif disponible", "no_explore_results_message": "Sube más fotos para explorar tu colección.", @@ -1291,6 +1337,7 @@ "no_results": "Sin resultados", "no_results_description": "Pruebe con un sinónimo o una palabra clave más general", "no_shared_albums_message": "Crea un álbum para compartir fotos y vídeos con personas de tu red", + "no_uploads_in_progress": "No hay cargas en progreso", "not_in_any_album": "Sin álbum", "not_selected": "No seleccionado", "note_apply_storage_label_to_previously_uploaded assets": "Nota: Para aplicar la etiqueta de almacenamiento a los archivos subidos previamente, ejecute el", @@ -1328,6 +1375,7 @@ "original": "original", "other": "Otro", "other_devices": "Otro dispositivo", + "other_entities": "Otras entidades", "other_variables": "Otras variables", "owned": "Propio", "owner": "Propietario", @@ -1459,6 +1507,7 @@ "purchase_server_description_2": "Estado del soporte", "purchase_server_title": "Servidor", "purchase_settings_server_activated": "La clave del producto del servidor la administra el administrador", + "queue_status": "Poniendo en cola {count}/{total}", "rating": "Valoración", "rating_clear": "Borrar calificación", "rating_count": "{count, plural, one {# estrella} other {# estrellas}}", @@ -1487,6 +1536,8 @@ "refreshing_faces": "Recargando caras", "refreshing_metadata": "Recargando metadatos", "regenerating_thumbnails": "Recargando miniaturas", + "remote": "Remoto", + "remote_assets": "Elementos remotos", "remove": "Eliminar", "remove_assets_album_confirmation": "¿Estás seguro que quieres eliminar {count, plural, one {# elemento} other {# elementos}} del álbum?", "remove_assets_shared_link_confirmation": "¿Estás seguro que quieres eliminar {count, plural, one {# elemento} other {# elementos}} del enlace compartido?", @@ -1494,7 +1545,9 @@ "remove_custom_date_range": "Eliminar intervalo de fechas personalizado", "remove_deleted_assets": "Eliminar archivos sin conexión", "remove_from_album": "Eliminar del álbum", + "remove_from_album_action_prompt": "{count} eliminado del álbum", "remove_from_favorites": "Quitar de favoritos", + "remove_from_lock_folder_action_prompt": "{count} eliminado de la carpeta protegida", "remove_from_locked_folder": "Eliminar de la carpeta protegida", "remove_from_locked_folder_confirmation": "¿Estás seguro de que deseas mover estas fotos y vídeos fuera de la carpeta protegida? Serán visibles en tu biblioteca.", "remove_from_shared_link": "Eliminar desde enlace compartido", @@ -1522,19 +1575,25 @@ "reset_password": "Restablecer la contraseña", "reset_people_visibility": "Restablecer la visibilidad de las personas", "reset_pin_code": "Restablecer PIN", + "reset_sqlite": "Restablecer la Base de Datos SQLite", + "reset_sqlite_confirmation": "¿Estás seguro que deseas restablecer la base de datos SQLite? Deberás cerrar sesión y volver a iniciarla para resincronizar los datos", + "reset_sqlite_success": "Restablecer exitosamente la base de datos SQLite", "reset_to_default": "Restablecer los valores predeterminados", "resolve_duplicates": "Resolver duplicados", "resolved_all_duplicates": "Todos los duplicados resueltos", "restore": "Restaurar", "restore_all": "Restaurar todo", + "restore_trash_action_prompt": "{count} restaurado de la papelera", "restore_user": "Restaurar usuario", "restored_asset": "Archivo restaurado", "resume": "Continuar", "retry_upload": "Reintentar subida", "review_duplicates": "Revisar duplicados", + "review_large_files": "Revisar archivos grandes", "role": "Rol", "role_editor": "Editor", "role_viewer": "Visor", + "running": "En ejecución", "save": "Guardar", "save_to_gallery": "Guardado en la galería", "saved_api_key": "Clave API guardada", @@ -1558,17 +1617,17 @@ "search_city": "Buscar ciudad...", "search_country": "Buscar país...", "search_filter_apply": "Aplicar filtros", - "search_filter_camera_title": "Elige tipo de cámara", + "search_filter_camera_title": "Elegir tipo de cámara", "search_filter_date": "Fecha", "search_filter_date_interval": "{start} al {end}", - "search_filter_date_title": "Selecciona un intervalo de fechas", + "search_filter_date_title": "Seleccionar un intervalo de fechas", "search_filter_display_option_not_in_album": "No en álbum", "search_filter_display_options": "Opciones de visualización", "search_filter_filename": "Buscar por nombre de archivo", "search_filter_location": "Ubicación", "search_filter_location_title": "Seleccionar una ubicación", "search_filter_media_type": "Tipo de archivo", - "search_filter_media_type_title": "Selecciona el tipo de archivo", + "search_filter_media_type_title": "Seleccionar el tipo de archivo", "search_filter_people_title": "Seleccionar personas", "search_for": "Buscar", "search_for_existing_person": "Buscar persona existente", @@ -1591,23 +1650,23 @@ "search_people": "Buscar personas", "search_places": "Buscar lugar", "search_rating": "Buscar por calificación...", - "search_result_page_new_search_hint": "Nueva Busqueda", + "search_result_page_new_search_hint": "Nueva Búsqueda", "search_settings": "Ajustes de la búsqueda", "search_state": "Buscar región/estado...", "search_suggestion_list_smart_search_hint_1": "La búsqueda inteligente está habilitada por defecto, para buscar metadatos utiliza esta sintaxis ", "search_suggestion_list_smart_search_hint_2": "m:tu-término-de-búsqueda", - "search_tags": "Buscando etiquetas...", + "search_tags": "Buscar etiquetas...", "search_timezone": "Buscar zona horaria...", "search_type": "Tipo de búsqueda", "search_your_photos": "Busca tus fotos", "searching_locales": "Buscando sitios...", "second": "Segundo", "see_all_people": "Ver todas las personas", - "select": "Selecciona", + "select": "Seleccionar", "select_album_cover": "Seleccionar portada del álbum", "select_all": "Seleccionar todo", "select_all_duplicates": "Seleccionar todos los duplicados", - "select_all_in": "Selecciona todos en {group}", + "select_all_in": "Seleccionar todos en {group}", "select_avatar_color": "Seleccionar color del avatar", "select_face": "Seleccionar cara", "select_featured_photo": "Seleccionar foto principal", @@ -1638,7 +1697,7 @@ "set_date_of_birth": "Establecer fecha de nacimiento", "set_profile_picture": "Establecer foto de perfil", "set_slideshow_to_fullscreen": "Mostrar diapositivas en pantalla completa", - "set_stack_primary_asset": "Establecer como activo principal", + "set_stack_primary_asset": "Establecer como recurso principal", "setting_image_viewer_help": "El visor de detalles carga primero la miniatura pequeña, luego carga la vista previa de tamaño mediano (si está habilitada), finalmente carga la original (si está habilitada).", "setting_image_viewer_original_subtitle": "Activar para cargar la imagen en resolución original (¡muy grande!). Deshabilitar para reducir el consumo de datos (de red y caché).", "setting_image_viewer_original_title": "Cargar imagen original", @@ -1666,6 +1725,7 @@ "settings_saved": "Ajustes guardados", "setup_pin_code": "Establecer un PIN", "share": "Compartir", + "share_action_prompt": "{count} recursos compartidos", "share_add_photos": "Agregar fotos", "share_assets_selected": "{count} seleccionado(s)", "share_dialog_preparing": "Preparando...", @@ -1687,6 +1747,7 @@ "shared_link_clipboard_copied_massage": "Copiado al portapapeles", "shared_link_clipboard_text": "Enlace: {link}\nContraseña: {password}", "shared_link_create_error": "Error creando el enlace compartido", + "shared_link_custom_url_description": "Accede a este enlace compartido con una URL personalizada", "shared_link_edit_description_hint": "Introduce la descripción del enlace", "shared_link_edit_expire_after_option_day": "1 día", "shared_link_edit_expire_after_option_days": "{count} días", @@ -1712,6 +1773,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Administrar enlaces compartidos", "shared_link_options": "Opciones de enlaces compartidos", + "shared_link_password_description": "Requerir una contraseña para acceder a este enlace compartido", "shared_links": "Enlaces compartidos", "shared_links_description": "Comparte fotos y vídeos con un enlace", "shared_photos_and_videos_count": "{assetCount, plural, other {# Fotos y vídeos compartidos.}}", @@ -1767,6 +1829,7 @@ "sort_title": "Título", "source": "Origen", "stack": "Apilar", + "stack_action_prompt": "{count} apilados", "stack_duplicates": "Apilar duplicados", "stack_select_one_photo": "Selecciona una imagen principal para la pila", "stack_selected_photos": "Apilar fotos seleccionadas", @@ -1776,7 +1839,7 @@ "start_date": "Fecha de inicio", "state": "Estado", "status": "Estado", - "stop_casting": "Parar difusión", + "stop_casting": "Detener transmisión", "stop_motion_photo": "Parar foto en movimiento", "stop_photo_sharing": "¿Dejar de compartir tus fotos?", "stop_photo_sharing_description": "{partner} ya no podrá acceder a tus fotos.", @@ -1786,6 +1849,7 @@ "storage_quota": "Cuota de Almacenamiento", "storage_usage": "{used} de {available} en uso", "submit": "Enviar", + "success": "Éxito", "suggestions": "Sugerencias", "sunrise_on_the_beach": "Amanecer en la playa", "support": "Soporte", @@ -1795,6 +1859,8 @@ "sync": "Sincronizar", "sync_albums": "Sincronizar álbumes", "sync_albums_manual_subtitle": "Sincroniza todos los videos y fotos subidos con los álbumes seleccionados a respaldar", + "sync_local": "Sincronización Local", + "sync_remote": "Sincronización Remota", "sync_upload_album_setting_subtitle": "Crea y sube tus fotos y videos a los álbumes seleccionados en Immich", "tag": "Etiqueta", "tag_assets": "Etiquetar activos", @@ -1805,6 +1871,7 @@ "tag_updated": "Etiqueta actualizada: {tag}", "tagged_assets": "Etiquetado(s) {count, plural, one {# activo} other {# activos}}", "tags": "Etiquetas", + "tap_to_run_job": "Toca para ejecutar la tarea", "template": "Plantilla", "theme": "Tema", "theme_selection": "Selección de tema", @@ -1837,6 +1904,7 @@ "total": "Total", "total_usage": "Uso total", "trash": "Papelera", + "trash_action_prompt": "{count} movidos a la papelera", "trash_all": "Descartar todo", "trash_count": "Descartar {count, number}", "trash_delete_asset": "Borrar/Eliminar archivo", @@ -1854,9 +1922,11 @@ "unable_to_change_pin_code": "No se ha podido cambiar el PIN", "unable_to_setup_pin_code": "No se ha podido establecer el PIN", "unarchive": "Desarchivar", + "unarchive_action_prompt": "{count} eliminados del archivo", "unarchived_count": "{count, plural, one {# No archivado} other {# No archivados}}", "undo": "Deshacer", "unfavorite": "Retirar favorito", + "unfavorite_action_prompt": "{count} eliminados de favoritos", "unhide_person": "Mostrar persona", "unknown": "Desconocido", "unknown_country": "País desconocido", @@ -1874,15 +1944,20 @@ "unselect_all_duplicates": "Deseleccionar todos los duplicados", "unselect_all_in": "Deselecciona todos en {group}", "unstack": "Desapilar", + "unstack_action_prompt": "{count} desapilado(s)", "unstacked_assets_count": "Desapilado(s) {count, plural, one {# elemento} other {# elementos}}", + "untagged": "Sin etiqueta", "up_next": "A continuación", "updated_at": "Actualizado", "updated_password": "Contraseña actualizada", "upload": "Subir", + "upload_action_prompt": "{count} en cola para carga", "upload_concurrency": "Subidas simultáneas", + "upload_details": "Cargar Detalles", "upload_dialog_info": "¿Quieres hacer una copia de seguridad al servidor de los elementos seleccionados?", "upload_dialog_title": "Subir elementos", "upload_errors": "Subida completada con {count, plural, one {# error} other {# errores}}, actualice la página para ver los nuevos recursos de la subida.", + "upload_finished": "Carga finalizada", "upload_progress": "Restante {remaining, number} - Procesado {processed, number}/{total, number}", "upload_skipped_duplicates": "Saltado {count, plural, one {# duplicate asset} other {# duplicate assets}}", "upload_status_duplicates": "Duplicados", @@ -1891,6 +1966,7 @@ "upload_success": "Subida realizada correctamente, actualice la página para ver los nuevos recursos de subida.", "upload_to_immich": "Subir a Immich ({count})", "uploading": "Subiendo", + "uploading_media": "Subiendo medios", "url": "URL", "usage": "Uso", "use_biometric": "Uso biométrico", @@ -1911,6 +1987,7 @@ "user_usage_stats_description": "Ver estadísticas de uso de la cuenta", "username": "Nombre de usuario", "users": "Usuarios", + "users_added_to_album_count": "{count, plural, one {# usuario agregado} other {# usuarios agregados}} al álbum", "utilities": "Utilidades", "validate": "Validar", "validate_endpoint_error": "Por favor, introduce una URL válida", @@ -1929,6 +2006,7 @@ "view_album": "Ver Álbum", "view_all": "Ver todas", "view_all_users": "Mostrar todos los usuarios", + "view_details": "Ver Detalles", "view_in_timeline": "Mostrar en la línea de tiempo", "view_link": "Ver enlace", "view_links": "Mostrar enlaces", diff --git a/i18n/et.json b/i18n/et.json index a2b17c6138..29bacae5c1 100644 --- a/i18n/et.json +++ b/i18n/et.json @@ -14,6 +14,7 @@ "add_a_location": "Lisa asukoht", "add_a_name": "Lisa nimi", "add_a_title": "Lisa pealkiri", + "add_birthday": "Lisa sünnipäev", "add_endpoint": "Lisa lõpp-punkt", "add_exclusion_pattern": "Lisa välistamismuster", "add_import_path": "Lisa imporditee", @@ -33,40 +34,47 @@ "added_to_favorites": "Lisatud lemmikutesse", "added_to_favorites_count": "{count, number} pilti lisatud lemmikutesse", "admin": { - "add_exclusion_pattern_description": "Lisa välistamismustreid. Toetatud on metamärgid *, ** ja ?. Kõikide kataloogis nimega \"Raw\" olevate failide ignoreerimiseks kasuta \"**/Raw/**\". Kõikide .tif failide ignoreerimiseks kasuta \"**/*.tif\". Absouutse tee ignoreerimiseks kasuta \"/path/to/ignore/**\".", + "add_exclusion_pattern_description": "Lisa välistamismustreid. Toetatud on metamärgid *, ** ja ?. Kõikide kataloogis nimega \"Raw\" olevate failide ignoreerimiseks kasuta \"**/Raw/**\". Kõikide \".tif\" lõpuga failide ignoreerimiseks kasuta \"**/*.tif\". Absouutse tee ignoreerimiseks kasuta \"/tee/mida/ignoreerida/**\".", "admin_user": "Administraator", - "asset_offline_description": "Seda välise kogu üksust ei leitud kettalt ning see liigutati prügikasti. Kui faili asukoht kogu siseselt muutus, leiad vastava uue üksuse oma ajajoonelt. Üksuse taastamiseks veendu, et allpool toodud failitee on Immich'ile kättesaadav ning skaneeri kogu uuesti.", + "asset_offline_description": "Seda välise kogu üksust ei leitud kettalt ning see liigutati prügikasti. Kui faili asukoht muutus kogu siseselt, leiad vastava uue üksuse oma ajajoonelt. Üksuse taastamiseks veendu, et allpool toodud failitee on Immich'ile kättesaadav ning skaneeri kogu uuesti.", "authentication_settings": "Autentimise seaded", - "authentication_settings_description": "Halda parooli, OAuth ja muid autentimise seadeid", + "authentication_settings_description": "Halda parooli, OAuth'i ja muid autentimise seadeid", "authentication_settings_disable_all": "Kas oled kindel, et soovid kõik sisselogimismeetodid välja lülitada? Sisselogimine lülitatakse täielikult välja.", "authentication_settings_reenable": "Et taas lubada, kasuta serveri käsku.", "background_task_job": "Tausttegumid", "backup_database": "Loo andmebaasi tõmmis", "backup_database_enable_description": "Luba andmebaasi tõmmised", "backup_keep_last_amount": "Eelmiste tõmmiste arv, mida alles hoida", + "backup_onboarding_1_description": "asukohaväline koopia pilves või teises füüsilises asukohas.", + "backup_onboarding_2_description": "lokaalset koopiat erinevatel seadmetel. See hõlmab põhifaile ja nende failide lokaalsed varundust.", + "backup_onboarding_3_description": "koopiat su andmetest, kaasa arvatud originaalfailid. See hõlmab üht asukohavälist ja kaht lokaalset koopiat.", + "backup_onboarding_description": "Andmete kaitsmiseks on soovituslik 3-2-1 varundusstrateegia. Põhjaliku varunduse jaoks peaksid talletama koopiaid nii oma üleslaaditud fotodest ja videotest kui ka Immich'i andmebaasist.", + "backup_onboarding_footer": "Rohkem informatsiooni Immich'i varundamise kohta leiad dokumentatsioonist.", + "backup_onboarding_parts_title": "3-2-1 varundus hõlmab:", + "backup_onboarding_title": "Varukoopiad", "backup_settings": "Andmebaasi tõmmiste seaded", "backup_settings_description": "Halda andmebaasi tõmmiste seadeid.", "cleared_jobs": "Tööted eemaldatud: {job}", - "config_set_by_file": "Konfiguratsioon on määratud konfifaili abil", + "config_set_by_file": "Konfiguratsioon on määratud konfiguratsioonifaili abil", "confirm_delete_library": "Kas oled kindel, et soovid kustutada {library} kogu?", - "confirm_delete_library_assets": "Kas oled kindel, et soovid selle kogu kustutada? Sellega kustutatakse {count, plural, one {# sisalduv üksus} other {kõik # sisalduvat üksust}} Immich'ist ning seda ei saa tagasi võtta. Failid jäävad kettale alles.", + "confirm_delete_library_assets": "Kas oled kindel, et soovid selle kogu kustutada? Sellega kustutatakse {count, plural, one {# sisalduv üksus} other {kõik # sisalduvat üksust}} Immich'ist ning seda toimingut ei saa tagasi võtta. Failid jäävad kettale alles.", "confirm_email_below": "Kinnitamiseks sisesta allpool \"{email}\"", "confirm_reprocess_all_faces": "Kas oled kindel, et soovid kõik näod uuesti töödelda? See eemaldab kõik nimega isikud.", "confirm_user_password_reset": "Kas oled kindel, et soovid kasutaja {user} parooli lähtestada?", "confirm_user_pin_code_reset": "Kas oled kindel, et soovid kasutaja {user} PIN-koodi lähtestada?", "create_job": "Lisa tööde", "cron_expression": "Cron avaldis", - "cron_expression_description": "Sea skaneerimise intervall cron formaadis. Rohkema info jaoks vaata nt. Crontab Guru", + "cron_expression_description": "Määra skaneerimise intervall cron formaadis. Rohkema info jaoks vaata nt. Crontab Guru", "cron_expression_presets": "Eelseadistatud cron avaldised", "disable_login": "Keela sisselogimine", "duplicate_detection_job_description": "Rakenda üksustele masinõpet, et leida sarnaseid pilte. Kasutab nutiotsingut", - "exclusion_pattern_description": "Välistamismustrid võimaldavad ignoreerida faile ja kaustu kogu skaneerimisel. See on kasulik, kui sul on kaustu, mis sisaldavad faile, mida sa ei soovi importida, nagu RAW failid.", + "exclusion_pattern_description": "Välistamismustrid võimaldavad ignoreerida faile ja kaustu selle kogu skaneerimisel. See on kasulik, kui sul on kaustu, mis sisaldavad faile, mida sa ei soovi importida, nagu RAW failid.", "external_library_management": "Väliste kogude haldus", "face_detection": "Näoavastus", - "face_detection_description": "Avasta üksustest nägusid masinõppe abil. Videote puhul kasutatakse ainult pisipilti. \"Värskenda\" töötleb kõik üksused uuesti. \"Lähtesta\" kustutab lisaks kõik seni leitud näed. \"Puuduvad\" võtab ette üksused, mida pole veel töödeldud. Avastatud näod suunatakse näotuvastusse, et grupeerida nad olemasolevateks või uuteks isikuteks.", + "face_detection_description": "Avasta üksustest nägusid masinõppe abil. Videote puhul kasutatakse ainult pisipilti. \"Värskenda\" töötleb kõik üksused uuesti. \"Lähtesta\" kustutab lisaks kõik seni leitud näod. \"Puuduvad\" võtab ette üksused, mida pole veel töödeldud. Avastatud näod suunatakse näotuvastusse, et grupeerida nad olemasolevateks või uuteks isikuteks.", "facial_recognition_job_description": "Grupeeri avastatud näod inimesteks. See samm käivitub siis, kui näoavastus on lõppenud. \"Lähtesta\" grupeerib kõik näod uuesti. \"Puuduvad\" võtab ette näod, mida pole isikuga seostatud.", "failed_job_command": "Käsk {command} ebaõnnestus töötes: {job}", - "force_delete_user_warning": "HOIATUS: See kustutab koheselt kasutaja ja kõik üksused. Seda ei saa tagasi võtta ja faile ei saa taastada.", + "force_delete_user_warning": "HOIATUS: See kustutab koheselt kasutaja ja kõik tema üksused. Toimingut ei saa tagasi võtta ja faile ei saa taastada.", "image_format": "Formaat", "image_format_description": "WebP failid on väiksemad kui JPEG, aga kodeerimine on aeglasem.", "image_fullsize_description": "Täismõõdus pilt ilma metaandmeteta, kasutatakse sisse suumimisel", @@ -77,9 +85,9 @@ "image_prefer_embedded_preview": "Eelista manustatud eelvaadet", "image_prefer_embedded_preview_setting_description": "Kasuta pilditöötluse sisendina võimalusel RAW fotodesse manustatud eelvaateid. See võib mõnede piltide puhul anda tulemuseks täpsemad värvid, aga eelvaate kvaliteet sõltub konkreetsest kaamerast ning pildis võib olla rohkem tihendusmüra.", "image_prefer_wide_gamut": "Eelista laia värvigammat", - "image_prefer_wide_gamut_setting_description": "Kasuta pisipiltide jaoks Display P3. See säilitab paremini laia värviruumiga piltide erksuse, aga vanematel seadmetel ja vanemate brauseritega võivad pildid teistsugused välja näha. sRGB pildid säilitatakse värvinihete vältimiseks.", + "image_prefer_wide_gamut_setting_description": "Kasuta pisipiltide jaoks Display P3. See säilitab paremini laia värviruumiga piltide erksuse, kuid vanematel seadmetel ja vanemate brauseritega võivad pildid teistsugused välja näha. sRGB pildid säilitatakse värvinihete vältimiseks.", "image_preview_description": "Keskmise suurusega pilt ilma metaandmeteta, kasutusel üksiku üksuse vaatamise ja masinõppe jaoks", - "image_preview_quality_description": "Eelvaate kvaliteet vahemikus 1-100. Kõrgem väärtus on parem, aga tekitab suuremaid faile ning võib mõjutada rakenduse töökiirust. Madala väärtuse seadmine võib mõjutada masinõppe kvaliteeti.", + "image_preview_quality_description": "Eelvaate kvaliteet vahemikus 1-100. Kõrgem väärtus on parem, aga tekitab suuremaid faile ning võib mõjutada rakenduse töökiirust. Madal väärtus võib mõjutada masinõppe kvaliteeti.", "image_preview_title": "Eelvaate seaded", "image_quality": "Kvaliteet", "image_resolution": "Resolutsioon", @@ -92,7 +100,7 @@ "job_concurrency": "{job} samaaegsus", "job_created": "Tööde lisatud", "job_not_concurrency_safe": "Seda töödet pole ohutu samaaegselt käivitada.", - "job_settings": "Tööte seaded", + "job_settings": "Töödete seaded", "job_settings_description": "Halda töödete samaaegsust", "job_status": "Tööte seisund", "jobs_delayed": "{jobCount, plural, other {# edasi lükatud}}", @@ -166,6 +174,20 @@ "metadata_settings_description": "Halda metaandmete seadeid", "migration_job": "Migratsioon", "migration_job_description": "Migreeri üksuste ja nägude pisipildid uusimale kaustastruktuurile", + "nightly_tasks_cluster_faces_setting_description": "Käivita värskelt avastatud nägudel näotuvastus", + "nightly_tasks_cluster_new_faces_setting": "Grupeeri uued näod", + "nightly_tasks_database_cleanup_setting": "Andmebaasi puhastuse tegumid", + "nightly_tasks_database_cleanup_setting_description": "Eemalda andmebaasist vanad, aegunud andmed", + "nightly_tasks_generate_memories_setting": "Genereeri mälestused", + "nightly_tasks_generate_memories_setting_description": "Loo üksustest uued mälestused", + "nightly_tasks_missing_thumbnails_setting": "Genereeri puuduvad pisipildid", + "nightly_tasks_missing_thumbnails_setting_description": "Suuna ilma pisipiltideta üksused pisipiltide genereerimisele", + "nightly_tasks_settings": "Öiste tegumite seaded", + "nightly_tasks_settings_description": "Halda öiseid tegumeid", + "nightly_tasks_start_time_setting": "Algusaeg", + "nightly_tasks_start_time_setting_description": "Aeg, millal server alustab öiste tegumite käivitamist", + "nightly_tasks_sync_quota_usage_setting": "Sünkrooni kvoodikasutus", + "nightly_tasks_sync_quota_usage_setting_description": "Uuenda kasutaja talletuskvoot jooksva kasutuse alusel", "no_paths_added": "Ühtegi teed pole", "no_pattern_added": "Mustreid ei ole", "note_apply_storage_label_previous_assets": "Märkus: Et rakendada talletussilt varem üleslaaditud üksustele, käivita", @@ -196,6 +218,8 @@ "oauth_mobile_redirect_uri": "Mobiilne ümbersuunamise URI", "oauth_mobile_redirect_uri_override": "Mobiilse ümbersuunamise URI ülekirjutamine", "oauth_mobile_redirect_uri_override_description": "Lülita sisse, kui OAuth pakkuja ei luba mobiilset URI-d, näiteks ''{callback}''", + "oauth_role_claim": "Rolli väide", + "oauth_role_claim_description": "Anna selle väite olemasolul automaatselt administraatori ligipääs. Väite väärtus võib olla 'user' või 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Halda OAuth sisselogimise seadeid", "oauth_settings_more_details": "Selle funktsiooni kohta rohkem teada saamiseks loe dokumentatsiooni.", @@ -357,10 +381,12 @@ "admin_password": "Administraatori parool", "administration": "Administratsioon", "advanced": "Täpsemad valikud", + "advanced_settings_beta_timeline_subtitle": "Koge uut rakendust", + "advanced_settings_beta_timeline_title": "Beeta ajajoon", "advanced_settings_enable_alternate_media_filter_subtitle": "Kasuta seda valikut, et filtreerida sünkroonimise ajal üksuseid alternatiivsete kriteeriumite alusel. Proovi seda ainult siis, kui rakendusel on probleeme kõigi albumite tuvastamisega.", "advanced_settings_enable_alternate_media_filter_title": "[EKSPERIMENTAALNE] Kasuta alternatiivset seadme albumi sünkroonimise filtrit", "advanced_settings_log_level_title": "Logimistase: {level}", - "advanced_settings_prefer_remote_subtitle": "Mõned seadmed laadivad seadmes olevate üksuste pisipilte piinavalt aeglaselt. Aktiveeri see seadistus, et laadida selle asemel kaugpilte.", + "advanced_settings_prefer_remote_subtitle": "Mõned seadmed laadivad lokaalsete üksuste pisipilte piinavalt aeglaselt. Aktiveeri see seadistus, et laadida selle asemel kaugpilte.", "advanced_settings_prefer_remote_title": "Eelista kaugpilte", "advanced_settings_proxy_headers_subtitle": "Määra vaheserveri päised, mida Immich peaks iga päringuga saatma", "advanced_settings_proxy_headers_title": "Vaheserveri päised", @@ -379,6 +405,7 @@ "album_cover_updated": "Albumi kaanepilt muudetud", "album_delete_confirmation": "Kas oled kindel, et soovid albumi {album} kustutada?", "album_delete_confirmation_description": "Kui see album on jagatud, ei pääse teised kasutajad sellele enam ligi.", + "album_deleted": "Album kustutatud", "album_info_card_backup_album_excluded": "VÄLJA JÄETUD", "album_info_card_backup_album_included": "LISATUD", "album_info_updated": "Albumi info muudetud", @@ -388,6 +415,7 @@ "album_options": "Albumi valikud", "album_remove_user": "Eemalda kasutaja?", "album_remove_user_confirmation": "Kas oled kindel, et soovid kasutaja {user} eemaldada?", + "album_search_not_found": "Otsingule vastavaid albumeid ei leitud", "album_share_no_users": "Paistab, et oled seda albumit kõikide kasutajatega jaganud, või pole ühtegi kasutajat, kellega jagada.", "album_updated": "Album muudetud", "album_updated_setting_description": "Saa teavitus e-posti teel, kui jagatud albumis on uusi üksuseid", @@ -407,6 +435,7 @@ "albums_default_sort_order": "Vaikimisi albumi järjestus", "albums_default_sort_order_description": "Uute albumite lisamisel üksuste esialgne järjekord.", "albums_feature_description": "Üksuste kollektsioonid, mida saab teiste kasutajatega jagada.", + "albums_on_device_count": "Albumid seadmel ({count})", "all": "Kõik", "all_albums": "Kõik albumid", "all_people": "Kõik isikud", @@ -427,6 +456,7 @@ "app_settings": "Rakenduse seaded", "appears_in": "Albumid", "archive": "Arhiiv", + "archive_action_prompt": "{count} lisatud arhiivi", "archive_or_unarchive_photo": "Arhiveeri või taasta foto", "archive_page_no_archived_assets": "Arhiveeritud üksuseid ei leitud", "archive_page_title": "Arhiveeri ({count})", @@ -464,7 +494,6 @@ "assets": "Üksused", "assets_added_count": "{count, plural, one {# üksus} other {# üksust}} lisatud", "assets_added_to_album_count": "{count, plural, one {# üksus} other {# üksust}} albumisse lisatud", - "assets_added_to_name_count": "{count, plural, one {# üksus} other {# üksust}} lisatud {hasName, select, true {albumisse {name}} other {uude albumisse}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Üksust} other {Üksuseid}} ei saa albumisse lisada", "assets_count": "{count, plural, one {# üksus} other {# üksust}}", "assets_deleted_permanently": "{count} üksus(t) jäädavalt kustutatud", @@ -490,6 +519,7 @@ "back_close_deselect": "Tagasi, sulge või tühista valik", "background_location_permission": "Taustal asukoha luba", "background_location_permission_content": "Et taustal töötades võrguühendust vahetada, peab Immich'il *alati* olema täpse asukoha luba, et rakendus saaks WiFi-võrgu nime lugeda", + "backup": "Varundamine", "backup_album_selection_page_albums_device": "Albumid seadmel ({count})", "backup_album_selection_page_albums_tap": "Puuduta kaasamiseks, topeltpuuduta välistamiseks", "backup_album_selection_page_assets_scatter": "Üksused võivad olla jaotatud mitme albumi vahel. Seega saab albumeid varundamise protsessi kaasata või välistada.", @@ -553,6 +583,8 @@ "backup_options_page_title": "Varundamise valikud", "backup_setting_subtitle": "Halda taustal ja esiplaanil üleslaadimise seadeid", "backward": "Tagasi", + "beta_sync": "Beeta sünkroonimise staatus", + "beta_sync_subtitle": "Halda uut sünkroonimissüsteemi", "biometric_auth_enabled": "Biomeetriline autentimine lubatud", "biometric_locked_out": "Biomeetriline autentimine on blokeeritud", "biometric_no_options": "Biomeetrilisi valikuid ei ole", @@ -570,7 +602,7 @@ "cache_settings_clear_cache_button": "Tühjenda puhver", "cache_settings_clear_cache_button_title": "Tühjendab rakenduse puhvri. See mõjutab oluliselt rakenduse jõudlust, kuni puhver uuesti täidetakse.", "cache_settings_duplicated_assets_clear_button": "TÜHJENDA", - "cache_settings_duplicated_assets_subtitle": "Fotod ja videod, mis on rakenduse poolt mustfiltreeritud", + "cache_settings_duplicated_assets_subtitle": "Fotod ja videod, mis on rakenduse poolt ignoreeritud", "cache_settings_duplicated_assets_title": "Dubleeritud üksused ({count})", "cache_settings_statistics_album": "Kogu pisipildid", "cache_settings_statistics_full": "Täismõõdus pildid", @@ -587,6 +619,7 @@ "cancel": "Katkesta", "cancel_search": "Katkesta otsing", "canceled": "Tühistatud", + "canceling": "Tühistamine", "cannot_merge_people": "Ei saa isikuid ühendada", "cannot_undo_this_action": "Sa ei saa seda tagasi võtta!", "cannot_update_the_description": "Kirjelduse muutmine ebaõnnestus", @@ -700,10 +733,11 @@ "current_server_address": "Praegune serveri aadress", "custom_locale": "Kohandatud lokaat", "custom_locale_description": "Vorminda kuupäevad ja arvud vastavalt keelele ja regioonile", + "custom_url": "Kohandatud URL", "daily_title_text_date": "d. MMMM", "daily_title_text_date_year": "d. MMMM yyyy", "dark": "Tume", - "darkTheme": "Lülita tume teema", + "dark_theme": "Lülita tume teema", "date_after": "Kuupäev pärast", "date_and_time": "Kuupäev ja kellaaeg", "date_before": "Kuupäev enne", @@ -719,6 +753,8 @@ "default_locale": "Vaikimisi lokaat", "default_locale_description": "Vorminda kuupäevad ja numbrid vastavalt brauseri lokaadile", "delete": "Kustuta", + "delete_action_confirmation_message": "Kas oled kindel, et soovid selle üksuse kustutada? See toiming liigutab üksuse serveri prügikasti ja küsib, kas soovid selle lokaalselt kustutada", + "delete_action_prompt": "{count} kustutatud", "delete_album": "Kustuta album", "delete_api_key_prompt": "Kas oled kindel, et soovid selle API võtme kustutada?", "delete_dialog_alert": "Need üksused kustutatakse jäädavalt Immich'ist ja sinu seadmest", @@ -732,9 +768,12 @@ "delete_key": "Kustuta võti", "delete_library": "Kustuta kogu", "delete_link": "Kustuta link", + "delete_local_action_prompt": "{count} kustutatud lokaalselt", "delete_local_dialog_ok_backed_up_only": "Kustuta ainult varundatud", "delete_local_dialog_ok_force": "Kustuta sellegipoolest", "delete_others": "Kustuta teised", + "delete_permanently": "Kustuta jäädavalt", + "delete_permanently_action_prompt": "{count} jäädavalt kustutatud", "delete_shared_link": "Kustuta jagatud link", "delete_shared_link_dialog_title": "Kustuta jagatud link", "delete_tag": "Kustuta silt", @@ -745,6 +784,7 @@ "description": "Kirjeldus", "description_input_hint_text": "Lisa kirjeldus...", "description_input_submit_error": "Viga kirjelduse muutmisel, rohkem infot leiad logist", + "deselect_all": "Eemalda kõik valikust", "details": "Üksikasjad", "direction": "Suund", "disabled": "Välja lülitatud", @@ -762,6 +802,7 @@ "documentation": "Dokumentatsioon", "done": "Tehtud", "download": "Laadi alla", + "download_action_prompt": "{count} üksust laaditakse alla", "download_canceled": "Allalaadimine katkestatud", "download_complete": "Allalaadimine lõpetatud", "download_enqueue": "Allalaadimine ootel", @@ -788,6 +829,7 @@ "edit": "Muuda", "edit_album": "Muuda albumit", "edit_avatar": "Muuda avatari", + "edit_birthday": "Muuda sünnipäeva", "edit_date": "Muuda kuupäeva", "edit_date_and_time": "Muuda kuupäeva ja kellaaega", "edit_description": "Muuda kirjeldust", @@ -799,6 +841,7 @@ "edit_key": "Muuda võtit", "edit_link": "Muuda linki", "edit_location": "Muuda asukohta", + "edit_location_action_prompt": "{count} asukoht muudetud", "edit_location_dialog_title": "Asukoht", "edit_name": "Muuda nime", "edit_people": "Muuda isikuid", @@ -817,6 +860,7 @@ "empty_trash": "Tühjenda prügikast", "empty_trash_confirmation": "Kas oled kindel, et soovid prügikasti tühjendada? See eemaldab kõik seal olevad üksused Immich'ist jäädavalt.\nSeda tegevust ei saa tagasi võtta!", "enable": "Luba", + "enable_backup": "Luba varundus", "enable_biometric_auth_description": "Biomeetrilise autentimise lubamiseks sisesta oma PIN-kood", "enabled": "Lubatud", "end_date": "Lõppkuupäev", @@ -953,6 +997,7 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "Lisa kirjeldus...", + "exif_bottom_sheet_description_error": "Viga kirjelduse muutmisel", "exif_bottom_sheet_details": "ÜKSIKASJAD", "exif_bottom_sheet_location": "ASUKOHT", "exif_bottom_sheet_people": "ISIKUD", @@ -973,6 +1018,8 @@ "explorer": "Brauser", "export": "Ekspordi", "export_as_json": "Ekspordi JSON-formaati", + "export_database": "Ekspordi andmebaas", + "export_database_description": "Ekspordi SQLite andmebaas", "extension": "Laiend", "external": "Väline", "external_libraries": "Välised kogud", @@ -984,6 +1031,7 @@ "failed_to_load_assets": "Üksuste laadimine ebaõnnestus", "failed_to_load_folder": "Kausta laadimine ebaõnnestus", "favorite": "Lemmik", + "favorite_action_prompt": "{count} lisatud lemmikutesse", "favorite_or_unfavorite_photo": "Lisa foto lemmikutesse või eemalda lemmikutest", "favorites": "Lemmikud", "favorites_page_no_favorites": "Lemmikuid üksuseid ei leitud", @@ -1023,6 +1071,9 @@ "haptic_feedback_switch": "Luba haptiline tagasiside", "haptic_feedback_title": "Haptiline tagasiside", "has_quota": "On kvoot", + "hash_asset": "Arvuta üksuse räsi", + "hashed_assets": "Räsiga üksused", + "hashing": "Räsi arvutamine", "header_settings_add_header_tip": "Lisa päis", "header_settings_field_validator_msg": "Väärtus ei saa olla tühi", "header_settings_header_name_input": "Päise nimi", @@ -1055,6 +1106,7 @@ "host": "Host", "hour": "Tund", "id": "ID", + "idle": "Jõude", "ignore_icloud_photos": "Ignoreeri iCloud fotosid", "ignore_icloud_photos_description": "Fotosid, mis on iCloud'is, ei laadita üles Immich'i serverisse", "image": "Pilt", @@ -1099,7 +1151,7 @@ "ios_debug_info_no_processes_queued": "Taustaprotsesse pole järjekorras", "ios_debug_info_no_sync_yet": "Taustal sünkroonimise tööde pole veel käinud", "ios_debug_info_processes_queued": "{count, plural, one {{count} taustaprotsess järjekorras} other {{count} taustaprotsessi järjekorras}}", - "ios_debug_info_processing_ran_at": "Töötlemine käis {dateTime}", + "ios_debug_info_processing_ran_at": "Töötlemine toimus {dateTime}", "items_count": "{count, plural, one {# üksus} other {# üksust}}", "jobs": "Tööted", "keep": "Jäta alles", @@ -1112,6 +1164,7 @@ "language_no_results_title": "Ühtegi keelt ei leitud", "language_search_hint": "Otsi keeli...", "language_setting_description": "Vali oma eelistatud keel", + "large_files": "Suured failid", "last_seen": "Viimati nähtud", "latest_version": "Uusim versioon", "latitude": "Laiuskraad", @@ -1127,16 +1180,18 @@ "library_page_sort_created": "Loomise aeg", "library_page_sort_last_modified": "Viimase muutmise aeg", "library_page_sort_title": "Albumi pealkiri", + "licenses": "Litsentsid", "light": "Hele", "like_deleted": "Meeldimine kustutatud", "link_motion_video": "Lingi liikuv video", - "link_options": "Lingi valikud", "link_to_oauth": "Ühenda OAuth", "linked_oauth_account": "OAuth konto ühendatud", "list": "Loend", "loading": "Laadimine", "loading_search_results_failed": "Otsitulemuste laadimine ebaõnnestus", + "local": "Lokaalsed", "local_asset_cast_failed": "Ei saa edastada üksust, mis pole serverisse üles laaditud", + "local_assets": "Lokaalsed üksused", "local_network": "Kohalik võrk", "local_network_sheet_info": "Rakendus ühendub valitud Wi-Fi võrgus olles serveriga selle URL-i kaudu", "location_permission": "Asukoha luba", @@ -1193,8 +1248,7 @@ "manage_your_devices": "Halda oma autenditud seadmeid", "manage_your_oauth_connection": "Halda oma OAuth ühendust", "map": "Kaart", - "map_assets_in_bound": "{count} foto", - "map_assets_in_bounds": "{count} fotot", + "map_assets_in_bounds": "{count, plural, one {# foto} other {# fotot}}", "map_cannot_get_user_location": "Ei saa kasutaja asukohta tuvastada", "map_location_dialog_yes": "Jah", "map_location_picker_page_use_location": "Kasuta seda asukohta", @@ -1246,6 +1300,7 @@ "more": "Rohkem", "move": "Liiguta", "move_off_locked_folder": "Liiguta lukustatud kaustast välja", + "move_to_lock_folder_action_prompt": "{count} lisatud lukustatud kausta", "move_to_locked_folder": "Liiguta lukustatud kausta", "move_to_locked_folder_confirmation": "Need fotod ja videod eemaldatakse kõigist albumitest ning nad on nähtavad ainult lukustatud kaustas", "moved_to_archive": "{count, plural, one {# üksus} other {# üksust}} liigutatud arhiivi", @@ -1292,6 +1347,7 @@ "no_results": "Vasteid pole", "no_results_description": "Proovi sünonüümi või üldisemat märksõna", "no_shared_albums_message": "Lisa album, et fotosid ja videosid teistega jagada", + "no_uploads_in_progress": "Üleslaadimisi käimas ei ole", "not_in_any_album": "Pole üheski albumis", "not_selected": "Ei ole valitud", "note_apply_storage_label_to_previously_uploaded assets": "Märkus: Et rakendada talletussilt varem üleslaaditud üksustele, käivita", @@ -1329,6 +1385,7 @@ "original": "originaal", "other": "Muud", "other_devices": "Muud seadmed", + "other_entities": "Muud objektid", "other_variables": "Muud muutujad", "owned": "Minu omad", "owner": "Omanik", @@ -1460,6 +1517,7 @@ "purchase_server_description_2": "Toetaja staatus", "purchase_server_title": "Server", "purchase_settings_server_activated": "Serveri tootevõtit haldab administraator", + "queue_status": "Järjekorras {count}/{total}", "rating": "Hinnang", "rating_clear": "Tühjenda hinnang", "rating_count": "{count, plural, one {# tärn} other {# tärni}}", @@ -1488,6 +1546,8 @@ "refreshing_faces": "Nägude värskendamine", "refreshing_metadata": "Metaandmete värskendamine", "regenerating_thumbnails": "Pisipiltide uuesti genereerimine", + "remote": "Serveris", + "remote_assets": "Kaugüksused", "remove": "Eemalda", "remove_assets_album_confirmation": "Kas oled kindel, et soovid {count, plural, one {# üksuse} other {# üksust}} albumist eemaldada?", "remove_assets_shared_link_confirmation": "Kas oled kindel, et soovid eemaldada {count, plural, one {# üksuse} other {# üksust}} sellelt jagatud lingilt?", @@ -1495,7 +1555,9 @@ "remove_custom_date_range": "Eemalda kohandatud kuupäevavahemik", "remove_deleted_assets": "Eemalda kustutatud üksused", "remove_from_album": "Eemalda albumist", + "remove_from_album_action_prompt": "{count} eemaldatud albumist", "remove_from_favorites": "Eemalda lemmikutest", + "remove_from_lock_folder_action_prompt": "{count} eemaldatud lukustatud kaustast", "remove_from_locked_folder": "Eemalda lukustatud kaustast", "remove_from_locked_folder_confirmation": "Kas oled kindel, et soovid need fotod ja videod lukustatud kaustast välja liigutada? Need muutuvad su kogus nähtavaks.", "remove_from_shared_link": "Eemalda jagatud lingist", @@ -1523,19 +1585,25 @@ "reset_password": "Lähtesta parool", "reset_people_visibility": "Lähtesta isikute nähtavus", "reset_pin_code": "Lähtesta PIN-kood", + "reset_sqlite": "Lähtesta SQLite andmebaas", + "reset_sqlite_confirmation": "Kas oled kindel, et soovid SQLite andmebaasi lähtestada? Andmete uuesti sünkroonimiseks pead välja ja jälle sisse logima", + "reset_sqlite_success": "SQLite andmebaas edukalt lähtestatud", "reset_to_default": "Lähtesta", "resolve_duplicates": "Lahenda duplikaadid", "resolved_all_duplicates": "Kõik duplikaadid lahendatud", "restore": "Taasta", "restore_all": "Taasta kõik", + "restore_trash_action_prompt": "{count} prügikastust taastatud", "restore_user": "Taasta kasutaja", "restored_asset": "Üksus taastatud", "resume": "Jätka", "retry_upload": "Proovi üleslaadimist uuesti", "review_duplicates": "Vaata duplikaadid läbi", + "review_large_files": "Vaata suured failid läbi", "role": "Roll", "role_editor": "Muutja", "role_viewer": "Vaataja", + "running": "Käimas", "save": "Salvesta", "save_to_gallery": "Salvesta galeriisse", "saved_api_key": "API võti salvestatud", @@ -1667,6 +1735,7 @@ "settings_saved": "Seaded salvestatud", "setup_pin_code": "Seadista PIN-kood", "share": "Jaga", + "share_action_prompt": "Jagatud {count} üksust", "share_add_photos": "Lisa fotosid", "share_assets_selected": "{count} valitud", "share_dialog_preparing": "Ettevalmistamine...", @@ -1688,6 +1757,7 @@ "shared_link_clipboard_copied_massage": "Kopeeritud lõikelauale", "shared_link_clipboard_text": "Link: {link}\nParool: {password}", "shared_link_create_error": "Viga jagatud lingi loomisel", + "shared_link_custom_url_description": "Ligipääs jagatud lingile kohandatud URL-i kaudu", "shared_link_edit_description_hint": "Sisesta jagatud lingi kirjeldus", "shared_link_edit_expire_after_option_day": "1 päev", "shared_link_edit_expire_after_option_days": "{count} päeva", @@ -1713,6 +1783,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Halda jagatud linke", "shared_link_options": "Jagatud lingi valikud", + "shared_link_password_description": "Nõua jagatud lingile ligi pääsemiseks parooli", "shared_links": "Jagatud lingid", "shared_links_description": "Jaga fotosid ja videosid lingiga", "shared_photos_and_videos_count": "{assetCount, plural, other {# jagatud fotot ja videot.}}", @@ -1768,6 +1839,7 @@ "sort_title": "Pealkiri", "source": "Lähtekood", "stack": "Virnasta", + "stack_action_prompt": "{count} virnastatud", "stack_duplicates": "Virnasta duplikaadid", "stack_select_one_photo": "Vali virnale kaanefoto", "stack_selected_photos": "Virnasta valitud fotod", @@ -1787,6 +1859,7 @@ "storage_quota": "Talletuskvoot", "storage_usage": "{used}/{available} kasutatud", "submit": "Saada", + "success": "Õnnestus", "suggestions": "Soovitused", "sunrise_on_the_beach": "Päikesetõus rannal", "support": "Tugi", @@ -1796,6 +1869,8 @@ "sync": "Sünkrooni", "sync_albums": "Sünkrooni albumid", "sync_albums_manual_subtitle": "Sünkrooni kõik üleslaaditud videod ja fotod valitud varundusalbumitesse", + "sync_local": "Sünkrooni lokaalsed üksused", + "sync_remote": "Sünkrooni kaugüksused", "sync_upload_album_setting_subtitle": "Loo ja laadi oma pildid ja videod üles Immich'isse valitud albumitesse", "tag": "Silt", "tag_assets": "Sildista üksuseid", @@ -1806,6 +1881,7 @@ "tag_updated": "Muudetud silt: {tag}", "tagged_assets": "{count, plural, one {# üksus} other {# üksust}} sildistatud", "tags": "Sildid", + "tap_to_run_job": "Puuduta tööte käivitamiseks", "template": "Mall", "theme": "Teema", "theme_selection": "Teema valik", @@ -1838,6 +1914,7 @@ "total": "Kokku", "total_usage": "Kogukasutus", "trash": "Prügikast", + "trash_action_prompt": "{count} liigutatud prügikasti", "trash_all": "Kõik prügikasti", "trash_count": "Liiguta {count, number} prügikasti", "trash_delete_asset": "Kustuta üksus", @@ -1855,9 +1932,11 @@ "unable_to_change_pin_code": "PIN-koodi muutmine ebaõnnestus", "unable_to_setup_pin_code": "PIN-koodi seadistamine ebaõnnestus", "unarchive": "Taasta arhiivist", + "unarchive_action_prompt": "{count} eemaldatud arhiivist", "unarchived_count": "{count, plural, other {# arhiivist taastatud}}", "undo": "Võta tagasi", "unfavorite": "Eemalda lemmikutest", + "unfavorite_action_prompt": "{count} eemaldatud lemmikutest", "unhide_person": "Ära peida isikut", "unknown": "Teadmata", "unknown_country": "Tundmatu riik", @@ -1875,15 +1954,20 @@ "unselect_all_duplicates": "Ära vali duplikaate", "unselect_all_in": "Ära vali ühtegi grupis {group}", "unstack": "Eralda", + "unstack_action_prompt": "{count} eraldatud", "unstacked_assets_count": "{count, plural, one {# üksus} other {# üksust}} eraldatud", + "untagged": "Sildistamata", "up_next": "Järgmine", "updated_at": "Uuendatud", "updated_password": "Parool muudetud", "upload": "Laadi üles", + "upload_action_prompt": "{count} üleslaadimise ootel", "upload_concurrency": "Üleslaadimise samaaegsus", + "upload_details": "Üleslaadimise üksikasjad", "upload_dialog_info": "Kas soovid valitud üksuse(d) serverisse varundada?", "upload_dialog_title": "Üksuse üleslaadimine", "upload_errors": "Üleslaadimine lõpetatud {count, plural, one {# veaga} other {# veaga}}, uute üksuste nägemiseks värskenda lehte.", + "upload_finished": "Üleslaadimine lõpetatud", "upload_progress": "Ootel {remaining, number} - Töödeldud {processed, number}/{total, number}", "upload_skipped_duplicates": "{count, plural, one {# dubleeritud üksus} other {# dubleeritud üksust}} vahele jäetud", "upload_status_duplicates": "Duplikaadid", @@ -1892,6 +1976,7 @@ "upload_success": "Üleslaadimine õnnestus, uute üksuste nägemiseks värskenda lehte.", "upload_to_immich": "Laadi Immich'isse ({count})", "uploading": "Üleslaadimine", + "uploading_media": "Meediumi üleslaadimine", "url": "URL", "usage": "Kasutus", "use_biometric": "Kasuta biomeetriat", @@ -1912,6 +1997,7 @@ "user_usage_stats_description": "Vaata konto kasutuse statistikat", "username": "Kasutajanimi", "users": "Kasutajad", + "users_added_to_album_count": "{count, plural, one {# kasutaja} other {# kasutajat}} lisatud albumisse", "utilities": "Tööriistad", "validate": "Valideeri", "validate_endpoint_error": "Sisesta korrektne URL", @@ -1930,6 +2016,7 @@ "view_album": "Vaata albumit", "view_all": "Vaata kõiki", "view_all_users": "Vaata kõiki kasutajaid", + "view_details": "Vaata üksikasju", "view_in_timeline": "Vaata ajajoonel", "view_link": "Vaata linki", "view_links": "Vaata linke", diff --git a/i18n/fa.json b/i18n/fa.json index 84857479b3..5b463655b2 100644 --- a/i18n/fa.json +++ b/i18n/fa.json @@ -471,7 +471,6 @@ "library": "کتابخانه", "library_options": "گزینه‌های کتابخانه", "light": "روشن", - "link_options": "گزینه‌های لینک", "link_to_oauth": "اتصال به OAuth", "linked_oauth_account": "حساب OAuth متصل شده", "list": "لیست", @@ -493,7 +492,6 @@ "manage_your_devices": "مدیریت دستگاه‌های متصل", "manage_your_oauth_connection": "مدیریت اتصال OAuth", "map": "نقشه", - "map_assets_in_bound": "{count} عکس", "map_assets_in_bounds": "{count} عکس ها", "map_cannot_get_user_location": "موقعیت مکانی در دسترس نیست", "map_location_dialog_yes": "بله", diff --git a/i18n/fi.json b/i18n/fi.json index f41d0cf631..73ce337de0 100644 --- a/i18n/fi.json +++ b/i18n/fi.json @@ -166,6 +166,20 @@ "metadata_settings_description": "Hallitse metatietoja", "migration_job": "Migraatio", "migration_job_description": "Migroi aineiston pikkukuvat ja kasvot uusimpaan kansiorakenteeseen", + "nightly_tasks_cluster_faces_setting_description": "Aja kasvojen tunnistus uusiin tunnistettuihin kasvoihin", + "nightly_tasks_cluster_new_faces_setting": "Kokoa uudet kasvot", + "nightly_tasks_database_cleanup_setting": "Tietokannan puhdistuksen tehtävät", + "nightly_tasks_database_cleanup_setting_description": "Siivoa vanhentunut data tietokannasta", + "nightly_tasks_generate_memories_setting": "Luo muistoja", + "nightly_tasks_generate_memories_setting_description": "Luo kohteista uusia muistoja", + "nightly_tasks_missing_thumbnails_setting": "Luo puuttuvat pikkukuvat", + "nightly_tasks_missing_thumbnails_setting_description": "Laita ilman pikkukuvia olevat kohteet jonoon pikkukuvien luontia varten", + "nightly_tasks_settings": "Yöllisten tehtävien asetukset", + "nightly_tasks_settings_description": "Hallitse yöllisiä tehtäviä", + "nightly_tasks_start_time_setting": "Aloitusaika", + "nightly_tasks_start_time_setting_description": "Aika jolloin palvelin aloittaa yöllisten tehtävien ajon", + "nightly_tasks_sync_quota_usage_setting": "Synkronointikiintiön käyttö", + "nightly_tasks_sync_quota_usage_setting_description": "Päivitä käyttäjän tallennustilan kiintiö nykyisen käytön mukaan", "no_paths_added": "Polkuja ei asetettu", "no_pattern_added": "Kaavoja ei lisättynä", "note_apply_storage_label_previous_assets": "Huom: Asettaaksesi nimikkeen aiemmin ladatulle aineistolle, aja", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "Mobiilin uudellenohjaus-URI", "oauth_mobile_redirect_uri_override": "Ohita mobiilin uudelleenohjaus-URI", "oauth_mobile_redirect_uri_override_description": "Ota käyttöön kun OAuth tarjoaja ei salli mobiili URI:a, kuten ''{callback}''", + "oauth_role_claim": "Roolin vaatimus", + "oauth_role_claim_description": "Salli pääkäyttäjän pääsyoikeus automaattisesti tämän vaatimuksen perusteella. Vaatimus voi sisältää, joko 'käyttäjän' tai 'pääkäyttäjän'.", "oauth_settings": "OAuth", "oauth_settings_description": "Hallitse OAuth-kirjautumisen asetuksia", "oauth_settings_more_details": "Saadaksesi lisätietoja tästä toiminnosta, katso dokumentaatio.", @@ -244,6 +260,7 @@ "storage_template_migration_info": "Tallennusmalli muuntaa kaikki tiedostopäätteet pieniksi kirjaimiksi. Mallipohjan muutokset koskevat vain uusia resursseja. Jos haluat käyttää mallipohjaa takautuvasti aiemmin ladattuihin resursseihin, suorita {job}.", "storage_template_migration_job": "Tallennustilan mallin muutostyö", "storage_template_more_details": "Saadaksesi lisätietoa tästä ominaisuudesta, katso Tallennustilan Mallit sekä mihin se vaikuttaa", + "storage_template_onboarding_description_v2": "Päälle kytkettynä, toiminto järjestestelee tiedostot automaattisesti käyttäjän määrittämän mallin mukaisesti. Lisätietoja dokumentaatiosta..", "storage_template_path_length": "Arvioitu tiedostopolun pituusrajoitus: {length, number}/{limit, number}", "storage_template_settings": "Tallennustilan malli", "storage_template_settings_description": "Hallitse palvelimelle ladatun aineiston kansiorakennetta ja tiedostonimiä", @@ -356,6 +373,7 @@ "admin_password": "Ylläpitäjän salasana", "administration": "Ylläpito", "advanced": "Edistyneet", + "advanced_settings_beta_timeline_subtitle": "Kokeile uutta sovelluskokemusta", "advanced_settings_enable_alternate_media_filter_subtitle": "Käytä tätä vaihtoehtoa suodattaaksesi mediaa synkronoinnin aikana vaihtoehtoisten kriteerien perusteella. Kokeile tätä vain, jos sovelluksessa on ongelmia kaikkien albumien tunnistamisessa.", "advanced_settings_enable_alternate_media_filter_title": "[KOKEELLINEN] Käytä vaihtoehtoisen laitteen albumin synkronointisuodatinta", "advanced_settings_log_level_title": "Kirjaustaso: {level}", @@ -403,6 +421,9 @@ "album_with_link_access": "Anna kenen tahansa nähdä linkin kautta tämän albumin valokuvat ja henkilöt.", "albums": "Albumit", "albums_count": "{count, plural, one {{count, number} albumi} other {{count, number} albumia}}", + "albums_default_sort_order": "Albumin oletuslajittelujärjestys", + "albums_default_sort_order_description": "Kohteiden ensisijainen lajittelujärjestys uusia albumeja luotaessa.", + "albums_feature_description": "Kokoelma kohteita, jotka voidaan jakaa muille käyttäjille.", "all": "Kaikki", "all_albums": "Kaikki albumit", "all_people": "Kaikki henkilöt", @@ -423,6 +444,7 @@ "app_settings": "Sovellusasetukset", "appears_in": "Esiintyy albumeissa", "archive": "Arkisto", + "archive_action_prompt": "{count} lisätty arkistoon", "archive_or_unarchive_photo": "Arkistoi kuva tai palauta arkistosta", "archive_page_no_archived_assets": "Arkistoituja kohteita ei löytynyt", "archive_page_title": "Arkisto ({count})", @@ -460,10 +482,12 @@ "assets": "Kohteet", "assets_added_count": "Lisätty {count, plural, one {# kohde} other {# kohdetta}}", "assets_added_to_album_count": "Albumiin lisätty {count, plural, one {# kohde} other {# kohdetta}}", - "assets_added_to_name_count": "Lisätty {count, plural, one {# kohde} other {# kohdetta}} {hasName, select, true {{name}} other {uuteen albumiin}}", + "assets_cannot_be_added_to_album_count": "{count, plural, one {Kohdetta} other {Kohdetta}} ei voida lisätä albumiin", "assets_count": "{count, plural, one {# media} other {# mediaa}}", "assets_deleted_permanently": "{count} kohdetta poistettu pysyvästi", "assets_deleted_permanently_from_server": "{count} objektia poistettu pysyvästi Immich-palvelimelta", + "assets_downloaded_failed": "{count, plural, one {Ladattu # tiedosto - {error} tiedosto epäonnistui} other {ladattu # tiedostoa - {error} tiedostot epäonnistuivat}}", + "assets_downloaded_successfully": "{count, plural, one {Ladattu # tiedosto onnistuneesti} other {Ladattu # tiedostoa onnistuneesti}}", "assets_moved_to_trash_count": "Siirretty {count, plural, one {# media} other {# mediaa}} roskakoriin", "assets_permanently_deleted_count": "{count, plural, one {# media} other {# mediaa}} poistettu pysyvästi", "assets_removed_count": "{count, plural, one {# media} other {# mediaa}} poistettu", @@ -478,10 +502,12 @@ "authorized_devices": "Valtuutetut laitteet", "automatic_endpoint_switching_subtitle": "Yhdistä paikallisesti nimetyn Wi-Fi-yhteyden kautta, kun se on saatavilla, ja käytä vaihtoehtoisia yhteyksiä muualla", "automatic_endpoint_switching_title": "Automaattinen URL-osoitteen vaihto", + "autoplay_slideshow": "Toista diaesitys automaattisesti", "back": "Takaisin", "back_close_deselect": "Palaa, sulje tai poista valinnat", "background_location_permission": "Taustasijainnin käyttöoikeus", "background_location_permission_content": "Jotta sovellus voi vaihtaa verkkoa taustalla toimiessaan, Immichillä on *aina* oltava pääsy tarkkaan sijaintiin, jotta se voi lukea Wi-Fi-verkon nimen", + "backup": "Varmuuskopiointi", "backup_album_selection_page_albums_device": "Laitteen albumit ({count})", "backup_album_selection_page_albums_tap": "Napauta sisällyttääksesi, kaksoisnapauta jättääksesi pois", "backup_album_selection_page_assets_scatter": "Kohteet voivat olla hajaantuneina useisiin albumeihin. Albumeita voidaan sisällyttää varmuuskopiointiin tai jättää siitä pois.", @@ -582,7 +608,8 @@ "cannot_merge_people": "Ihmisiä ei voitu yhdistää", "cannot_undo_this_action": "Et voi perua tätä toimintoa!", "cannot_update_the_description": "Kuvausta ei voi päivittää", - "cast": "Lähettää", + "cast": "Suoratoisto", + "cast_description": "Määritä saatavilla olevat suoratoistopalvelut", "change_date": "Vaihda päiväys", "change_description": "Muuta kuvausta", "change_display_order": "Muuta näyttöjärjestystä", @@ -641,6 +668,7 @@ "confirm_password": "Vahvista salasana", "confirm_tag_face": "Haluatko merkitä nämä kasvot nimellä {name}?", "confirm_tag_face_unnamed": "Merkitäänkö nämä kasvot?", + "connected_device": "Yhdistetty laite", "connected_to": "Yhdistetty", "contain": "Mahduta", "context": "Konteksti", @@ -693,6 +721,7 @@ "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Tumma", + "dark_theme": "Vaihda tumma teema", "date_after": "Päivämäärän jälkeen", "date_and_time": "Päivämäärä ja aika", "date_before": "Päivä ennen", @@ -708,6 +737,7 @@ "default_locale": "Oletuskieliasetus", "default_locale_description": "Muotoile päivämäärät ja numerot selaimesi kielen mukaan", "delete": "Poista", + "delete_action_prompt": "{count} poistettu pysyvästi", "delete_album": "Poista albumi", "delete_api_key_prompt": "Haluatko varmasti poistaa tämän API-avaimen?", "delete_dialog_alert": "Nämä kohteet poistetaan pysyvästi Immich:stä ja laitteeltasi", @@ -740,12 +770,13 @@ "disallow_edits": "Älä salli muokkauksia", "discord": "Discord", "discover": "Tutki", + "discovered_devices": "Löydetyt laitteet", "dismiss_all_errors": "Sivuuta kaikki virheet", "dismiss_error": "Sivuuta virhe", "display_options": "Näyttöasetukset", "display_order": "Näyttöjärjestys", "display_original_photos": "Näytä alkuperäiset kuvat", - "display_original_photos_setting_description": "Näytä mieluiten alkuperäinen kuva peukalokuvan sijasta kun alkuperäinen aineisto on web-yhteensopiva. Tämä voi aiheuttaa kuvien näyttämisen hitautta.", + "display_original_photos_setting_description": "Näytä mieluiten alkuperäinen kuva esikatselukuvan sijasta, kun alkuperäinen kuva on web-yhteensopiva. Tämä voi aiheuttaa kuvien näyttämisen hitautta.", "do_not_show_again": "Älä näytä tätä enää", "documentation": "Dokumentaatio", "done": "Valmis", @@ -787,6 +818,7 @@ "edit_key": "Muokkaa avainta", "edit_link": "Muokkaa linkkiä", "edit_location": "Muokkaa sijaintia", + "edit_location_action_prompt": "{count} sijaintia muokattu", "edit_location_dialog_title": "Sijainti", "edit_name": "Muokkaa nimeä", "edit_people": "Muokkaa henkilöitä", @@ -972,6 +1004,7 @@ "failed_to_load_assets": "Kohteiden lataus epäonnistui", "failed_to_load_folder": "Kansion lataaminen epäonnistui", "favorite": "Suosikki", + "favorite_action_prompt": "{count} lisätty suosikkeihin", "favorite_or_unfavorite_photo": "Suosikki- tai ei-suosikkikuva", "favorites": "Suosikit", "favorites_page_no_favorites": "Suosikkikohteita ei löytynyt", @@ -1086,6 +1119,8 @@ "ios_debug_info_last_sync_at": "Viimeisin synkronisointi {dateTime}", "ios_debug_info_no_processes_queued": "Ei taustaprosesseja jonossa", "ios_debug_info_no_sync_yet": "Taustasynkronisointia ei ole suoritettu vielä", + "ios_debug_info_processes_queued": "{count, plural, one {{count} taustaprosessi jonossa} other {{count} taustaprosessia jonossa}}", + "ios_debug_info_processing_ran_at": "Prosessi valmistui {dateTime}", "items_count": "{count, plural, one {# kpl} other {# kpl}}", "jobs": "Taustatehtävät", "keep": "Säilytä", @@ -1094,6 +1129,9 @@ "kept_this_deleted_others": "Tämä kohde säilytettiin. {count, plural, one {# asset} other {# assets}} poistettiin", "keyboard_shortcuts": "Pikanäppäimet", "language": "Kieli", + "language_no_results_subtitle": "Yritä säätää hakuehtoja", + "language_no_results_title": "Kieliä ei löydetty", + "language_search_hint": "Etsi kieliä...", "language_setting_description": "Valitse suosimasi kieli", "last_seen": "Viimeksi nähty", "latest_version": "Viimeisin versio", @@ -1113,12 +1151,12 @@ "light": "Vaalea", "like_deleted": "Tykkäys poistettu", "link_motion_video": "Linkitä liikevideo", - "link_options": "Linkin asetukset", "link_to_oauth": "Linkki OAuth", "linked_oauth_account": "Linkitetty OAuth-tili", "list": "Lista", "loading": "Ladataan", "loading_search_results_failed": "Hakutulosten lataaminen epäonnistui", + "local_asset_cast_failed": "Kohdetta, joka ei ole ladattuna palvelimelle, ei voida striimata", "local_network": "Lähiverkko", "local_network_sheet_info": "Sovellus muodostaa yhteyden palvelimeen tämän URL-osoitteen kautta, kun käytetään määritettyä Wi-Fi-verkkoa", "location_permission": "Sijainnin käyttöoikeus", @@ -1132,6 +1170,7 @@ "locked_folder": "Lukittu kansio", "log_out": "Kirjaudu ulos", "log_out_all_devices": "Kirjaudu ulos kaikilta laitteilta", + "logged_in_as": "Kirjautunut käyttäjänä {user}", "logged_out_all_devices": "Kaikki laitteet kirjattu ulos", "logged_out_device": "Laite kirjattu ulos", "login": "Kirjaudu", @@ -1174,7 +1213,6 @@ "manage_your_devices": "Hallitse sisäänkirjautuneita laitteitasi", "manage_your_oauth_connection": "Hallitse OAuth-yhteyttäsi", "map": "Kartta", - "map_assets_in_bound": "{count} kuva", "map_assets_in_bounds": "{count} kuvaa", "map_cannot_get_user_location": "Käyttäjän sijaintia ei voitu määrittää", "map_location_dialog_yes": "Kyllä", @@ -1220,13 +1258,14 @@ "merged_people_count": "{count, plural, one {# Henkilö} other {# henkilöä}} yhdistetty", "minimize": "PIenennä", "minute": "Minuutti", - "missing": "Puuttuu", + "missing": "Puuttuvat", "model": "Malli", "month": "Kuukauden mukaan", "monthly_title_text_date_format": "MMMM y", "more": "Enemmän", "move": "Siirrä", "move_off_locked_folder": "Siirrä pois lukitusta kansiosta", + "move_to_lock_folder_action_prompt": "{count} lisätty lukittuun kansioon", "move_to_locked_folder": "Siirrä lukittuun kansioon", "move_to_locked_folder_confirmation": "Nämä kuvat ja videot poistetaan kaikista albumeista, ja ne ovat nähtävissä vain lukitussa kansiossa", "moved_to_archive": "Siirretty {count, plural, one {# kohde} other {# kohdetta}} arkistoon", @@ -1259,6 +1298,7 @@ "no_archived_assets_message": "Arkistoi kuvia ja videoita piilottaaksesi ne kuvat näkymästä", "no_assets_message": "NAPAUTA LATAAKSESI ENSIMMÄISEN KUVASI", "no_assets_to_show": "Ei näytettäviä kohteita", + "no_cast_devices_found": "Cast-laitteita ei löytynyt", "no_duplicates_found": "Kaksoiskappaleita ei löytynyt.", "no_exif_info_available": "EXIF-tietoa ei saatavilla", "no_explore_results_message": "Lataa lisää kuvia tutkiaksesi kokoelmaasi.", @@ -1291,8 +1331,11 @@ "oldest_first": "Vanhin ensin", "on_this_device": "Laitteella", "onboarding": "Käyttöönotto", - "onboarding_privacy_description": "Seuraavat (valinnaiset) ominaisuudet perustuvat ulkoisiin palveluihin, ja ne voidaan poistaa käytöstä milloin tahansa hallinta asetuksista.", + "onboarding_locale_description": "Valitse haluamasi kieli. Voit muuttaa kieliasetuksia myöhemmin asetuksista.", + "onboarding_privacy_description": "Seuraavat (valinnaiset) ominaisuudet perustuvat ulkoisiin palveluihin ja ne voidaan milloin tahansa poistaa käytöstä asetuksista.", + "onboarding_server_welcome_description": "Määritellään seuraavaksi järjestelmäsi muutamalla yleisellä asetuksella.", "onboarding_theme_description": "Valitse väriteema istunnollesi. Voit muuttaa tämän myöhemmin asetuksistasi.", + "onboarding_user_welcome_description": "Aloitetaan!", "onboarding_welcome_user": "Tervetuloa {user}", "online": "Online", "only_favorites": "Vain suosikit", @@ -1392,7 +1435,7 @@ "previous_or_next_photo": "Kuva seuraava/edellinen", "previous_or_next_year": "Vuosi seuraava/edellinen", "primary": "Ensisijainen", - "privacy": "Yksityisyys", + "privacy": "Tietosuoja", "profile": "Profiili", "profile_drawer_app_logs": "Lokit", "profile_drawer_client_out_of_date_major": "Sovelluksen mobiiliversio on vanhentunut. Päivitä viimeisimpään merkittävään versioon.", @@ -1472,12 +1515,15 @@ "remove_custom_date_range": "Poista aikaväliltä", "remove_deleted_assets": "Poista Offline-tiedostot", "remove_from_album": "Poista albumista", + "remove_from_album_action_prompt": "{count} poistettu albumista", "remove_from_favorites": "Poista suosikeista", + "remove_from_lock_folder_action_prompt": "{count} poistettu lukitusta albumista", "remove_from_locked_folder": "Poista lukitusta kansiosta", "remove_from_locked_folder_confirmation": "Haluatko varmasti siirtää nämä kuvat ja videot pois lukitusta kansiosta? Ne näkyvät sen jälkeen kirjastossasi.", "remove_from_shared_link": "Poista jakolinkistä", "remove_memory": "Tyhjennä muisti", "remove_photo_from_memory": "Poista kuva muistista", + "remove_tag": "Poista tunniste", "remove_url": "Poista URL", "remove_user": "Poista käyttäjä", "removed_api_key": "API-avain {name} poistettu", @@ -1584,6 +1630,7 @@ "select_album_cover": "Valitse albmin kansi", "select_all": "Valitse kaikki", "select_all_duplicates": "Valitse kaikki kaksoiskappaleet", + "select_all_in": "Valitse kaikki {group}", "select_avatar_color": "Valitse avatarin väri", "select_face": "Valitse kasvo", "select_featured_photo": "Valitse esittelykuva", @@ -1604,6 +1651,7 @@ "server_info_box_server_url": "Palvelimen URL-osoite", "server_offline": "Palvelin Offline-tilassa", "server_online": "Palvelin Online-tilassa", + "server_privacy": "Palvelimen tietosuoja", "server_stats": "Palvelimen tilastot", "server_version": "Palvelimen versio", "set": "Aseta", @@ -1613,6 +1661,7 @@ "set_date_of_birth": "Aseta syntymäaika", "set_profile_picture": "Aseta profiilikuva", "set_slideshow_to_fullscreen": "Näytä diaesitys koko ruudulla", + "set_stack_primary_asset": "Aseta pääkohteeksi", "setting_image_viewer_help": "Kuvaa katseltaessa ensin ladataan pikkukuva, sitten keskilaatuinen pikkukuva (jos käytössä) ja lopuksi alkuperäinen (jos käytössä).", "setting_image_viewer_original_subtitle": "Ota käyttöön ladataksesi alkuperäinen täysitarkkuuksinen kuva (suuri!). Poista käytöstä vähentääksesi datan käyttöä (sekä verkossa että laitteen välimuistissa).", "setting_image_viewer_original_title": "Lataa alkuperäinen kuva", @@ -1750,6 +1799,7 @@ "start_date": "Alkupäivä", "state": "Maakunta", "status": "Tila", + "stop_casting": "Lopeta suoratoisto", "stop_motion_photo": "Pysäytä liikkuva kuva", "stop_photo_sharing": "Lopetetaanko kuvien jakaminen?", "stop_photo_sharing_description": "{partner} ei enää pääse kuviisi.", @@ -1769,7 +1819,7 @@ "sync_albums": "Synkronoi albumit", "sync_albums_manual_subtitle": "Synkronoi kaikki ladatut videot ja valokuvat valittuihin varmuuskopioalbumeihin", "sync_upload_album_setting_subtitle": "Luo ja lataa valokuvasi ja videosi valittuihin albumeihin Immichissä", - "tag": "Lisää tunniste", + "tag": "Tunniste", "tag_assets": "Lisää tunnisteita", "tag_created": "Luotu tunniste: {tag}", "tag_feature_description": "Selaa valokuvia ja videoita, jotka on ryhmitelty loogisten tunnisteotsikoiden mukaan", @@ -1810,6 +1860,7 @@ "total": "Yhteensä", "total_usage": "Käyttö yhteensä", "trash": "Roskakori", + "trash_action_prompt": "{count} siirretty roskakoriin", "trash_all": "Vie kaikki roskakoriin", "trash_count": "Roskakori {count, number}", "trash_delete_asset": "Poista / vie roskakoriin", @@ -1827,8 +1878,11 @@ "unable_to_change_pin_code": "PIN-koodin vaihtaminen epäonnistui", "unable_to_setup_pin_code": "PIN-koodin määrittäminen epäonnistui", "unarchive": "Palauta arkistosta", + "unarchive_action_prompt": "{count} poistettu arkistosta", "unarchived_count": "{count, plural, other {# poistettu arkistosta}}", + "undo": "Kumoa", "unfavorite": "Poista suosikeista", + "unfavorite_action_prompt": "{count} poistettu suosikeista", "unhide_person": "Poista henkilö piilosta", "unknown": "Tuntematon", "unknown_country": "Tuntematon maa", @@ -1844,8 +1898,10 @@ "unsaved_change": "Tallentamaton muutos", "unselect_all": "Poista valinnat", "unselect_all_duplicates": "Poista kaikkien kaksoiskappaleiden valinta", + "unselect_all_in": "Poista kaikki valinnat {group}", "unstack": "Pura pino", "unstacked_assets_count": "Poistettu pinosta {count, plural, one {# kohde} other {# kohdetta}}", + "untagged": "Ilman tunnistetta", "up_next": "Seuraavaksi", "updated_at": "Päivitetty", "updated_password": "Salasana päivitetty", @@ -1861,7 +1917,7 @@ "upload_status_uploaded": "Ladattu", "upload_success": "Lataus onnistui. Päivitä sivu jotta näet latauksesi.", "upload_to_immich": "Lähetä Immichiin ({count})", - "uploading": "Lähettään", + "uploading": "Lähettää", "url": "URL", "usage": "Käyttö", "use_biometric": "Käytä biometriikkaa", @@ -1873,6 +1929,7 @@ "user_liked": "{user} tykkäsi {type, select, photo {kuvasta} video {videosta} asset {mediasta} other {tästä}}", "user_pin_code_settings": "PIN-koodi", "user_pin_code_settings_description": "Hallinnoi PIN-koodiasi", + "user_privacy": "Käyttäjän tietosuoja", "user_purchase_settings": "Osta", "user_purchase_settings_description": "Hallitse ostostasi", "user_role_set": "Tee käyttäjästä {user} {role}", diff --git a/i18n/fil.json b/i18n/fil.json index 12e74f7bad..12e6086064 100644 --- a/i18n/fil.json +++ b/i18n/fil.json @@ -14,10 +14,13 @@ "add_a_location": "Dagdagan ng lugar", "add_a_name": "Dagdagan ng pangalan", "add_a_title": "Dagdagan ng pamagat", + "add_endpoint": "Dagdagan ng dulo", "add_location": "Magdagdag ng lugar", "add_more_users": "Magdagdag ng mga user", "add_partner": "Magdagdag ng kasangga", + "add_path": "Magdagdag ng path", "add_photos": "Magdagdag ng litrato", + "add_tag": "Magdagdag ng tag", "add_to": "Idagdag sa…", "add_to_album": "Idagdag sa album", "add_to_album_bottom_sheet_added": "Naidagdag sa {album}", diff --git a/i18n/fr.json b/i18n/fr.json index bf57469944..872c2e4984 100644 --- a/i18n/fr.json +++ b/i18n/fr.json @@ -17,7 +17,7 @@ "add_endpoint": "Ajouter une adresse", "add_exclusion_pattern": "Ajouter un schéma d'exclusion", "add_import_path": "Ajouter un chemin à importer", - "add_location": "Ajouter un lieu", + "add_location": "Ajouter une localisation", "add_more_users": "Ajouter plus d'utilisateurs", "add_partner": "Ajouter un partenaire", "add_path": "Ajouter un chemin", @@ -166,6 +166,20 @@ "metadata_settings_description": "Gestion des paramètres de métadonnées", "migration_job": "Migration", "migration_job_description": "Migration des miniatures pour les médias et les visages vers la dernière structure de dossiers", + "nightly_tasks_cluster_faces_setting_description": "Démarrer la reconnaissance faciale sur les visages nouvellement détectés", + "nightly_tasks_cluster_new_faces_setting": "Regrouper les nouveaux visages", + "nightly_tasks_database_cleanup_setting": "Tâches de nettoyage de la base de données", + "nightly_tasks_database_cleanup_setting_description": "Nettoyage ancien, données de la base de données expirées", + "nightly_tasks_generate_memories_setting": "Générer les souvenirs", + "nightly_tasks_generate_memories_setting_description": "Créer des souvenirs à partir des éléments", + "nightly_tasks_missing_thumbnails_setting": "Générer les miniatures manquantes", + "nightly_tasks_missing_thumbnails_setting_description": "Mettre en file d'attente les éléments sans miniature pour la création de miniature", + "nightly_tasks_settings": "Paramètres des tâches de nuit", + "nightly_tasks_settings_description": "Gérer les tâches de nuit", + "nightly_tasks_start_time_setting": "Heure de démarrage", + "nightly_tasks_start_time_setting_description": "Heure à laquelle le serveur commence à exécuter les tâches de nuit", + "nightly_tasks_sync_quota_usage_setting": "Synchroniser les quota d'usage", + "nightly_tasks_sync_quota_usage_setting_description": "Mettre à jour les quota d'usage de l'utilisateur, en se basant sur l'utilisation actuelle", "no_paths_added": "Aucun chemin n'a été ajouté", "no_pattern_added": "Aucun schéma d'exclusion n'a été ajouté", "note_apply_storage_label_previous_assets": "Remarque : pour appliquer l'étiquette de stockage à des médias précédemment envoyés, exécutez", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "URI de redirection mobile", "oauth_mobile_redirect_uri_override": "Remplacer l'URI de redirection mobile", "oauth_mobile_redirect_uri_override_description": "Activer quand le fournisseur d'OAuth ne permet pas un URI mobile, comme ''{callback}''", + "oauth_role_claim": "Attribut de rôle", + "oauth_role_claim_description": "Donne automatiquement un accès en tant qu'admin, en se basant sur la présence de cet attribut. L'attribut peut avoir soit 'user' (utilisateur) soit 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Gérer les paramètres de connexion OAuth", "oauth_settings_more_details": "Pour plus de détails sur cette fonctionnalité, consultez ce lien.", @@ -244,7 +260,7 @@ "storage_template_migration_info": "L'enregistrement des modèles va convertir toutes les extensions en minuscule. Les changements de modèle ne s'appliqueront qu'aux nouveaux médias. Pour appliquer rétroactivement le modèle aux médias précédemment envoyés, exécutez la tâche {job}.", "storage_template_migration_job": "Tâche de migration du modèle de stockage", "storage_template_more_details": "Pour plus de détails sur cette fonctionnalité, reportez-vous au Modèle de stockage et à ses implications", - "storage_template_onboarding_description_v2": "Quand elle est activée, cette fonctionnalité organise automatiquement les fichiers, sur base d'un modèle défini par l'utilisateur. Pour plus d'informations, se répéter à la documentation.", + "storage_template_onboarding_description_v2": "Quand elle est activée, cette fonctionnalité organise automatiquement les fichiers, sur base d'un modèle défini par l'utilisateur. Pour plus d'informations, se référer à la documentation.", "storage_template_path_length": "Limite approximative de la longueur du chemin : {length, number}/{limit, number}", "storage_template_settings": "Modèle de stockage", "storage_template_settings_description": "Gérer la structure des dossiers et le nom des fichiers du média envoyé", @@ -357,10 +373,12 @@ "admin_password": "Mot de passe Admin", "administration": "Administration", "advanced": "Avancé", + "advanced_settings_beta_timeline_subtitle": "Essayer la nouvelle application", + "advanced_settings_beta_timeline_title": "Timeline de la béta", "advanced_settings_enable_alternate_media_filter_subtitle": "Utilisez cette option pour filtrer les média durant la synchronisation avec des critères alternatifs. N'utilisez cela que lorsque l'application n'arrive pas à détecter tout les albums.", "advanced_settings_enable_alternate_media_filter_title": "[EXPÉRIMENTAL] Utiliser le filtre de synchronisation d'album alternatif", "advanced_settings_log_level_title": "Niveau de journalisation : {level}", - "advanced_settings_prefer_remote_subtitle": "Certains appareils sont très lents à charger des miniatures à partir de ressources présentes sur l'appareil. Activez ce paramètre pour charger des images externes à la place.", + "advanced_settings_prefer_remote_subtitle": "Certains appareils sont très lents à charger des miniatures à partir de ressources locales. Activez ce paramètre pour charger des images externes à la place.", "advanced_settings_prefer_remote_title": "Préférer les images externes", "advanced_settings_proxy_headers_subtitle": "Ajoutez des en-têtes personnalisés à chaque requête réseau", "advanced_settings_proxy_headers_title": "En-têtes de proxy", @@ -379,6 +397,7 @@ "album_cover_updated": "Couverture de l'album mise à jour", "album_delete_confirmation": "Êtes-vous sûr de vouloir supprimer l'album {album} ?", "album_delete_confirmation_description": "Si cet album est partagé, les autres utilisateurs ne pourront plus y accéder.", + "album_deleted": "Album supprimé", "album_info_card_backup_album_excluded": "EXCLUS", "album_info_card_backup_album_included": "INCLUS", "album_info_updated": "Détails de l'album mis à jour", @@ -388,6 +407,7 @@ "album_options": "Options de l'album", "album_remove_user": "Supprimer l'utilisateur ?", "album_remove_user_confirmation": "Êtes-vous sûr de vouloir supprimer {user} ?", + "album_search_not_found": "Aucun album trouvé ne correspond à votre recherche", "album_share_no_users": "Il semble que vous ayez partagé cet album avec tous les utilisateurs ou que vous n'ayez aucun utilisateur avec lequel le partager.", "album_updated": "Album mis à jour", "album_updated_setting_description": "Recevoir une notification par courriel lorsqu'un album partagé a de nouveaux médias", @@ -407,14 +427,15 @@ "albums_default_sort_order": "Ordre de tri par défaut des albums", "albums_default_sort_order_description": "Ordre de tri des médias pour les nouveaux albums créés.", "albums_feature_description": "Bibliothèques de médias pouvant être partagés avec d'autres utilisateurs.", + "albums_on_device_count": "Album sur l'appareil ({count})", "all": "Tout", "all_albums": "Tous les albums", "all_people": "Toutes les personnes", "all_videos": "Toutes les vidéos", "allow_dark_mode": "Autoriser le mode sombre", "allow_edits": "Autoriser les modifications", - "allow_public_user_to_download": "Permettre aux utilisateurs non connectés de télécharger", - "allow_public_user_to_upload": "Autoriser l'envoi aux utilisateurs non connectés", + "allow_public_user_to_download": "Permettre le téléchargement par des utilisateurs non connectés", + "allow_public_user_to_upload": "Permettre l'envoi par des utilisateurs non connectés", "alt_text_qr_code": "Image du code QR", "anti_clockwise": "Sens anti-horaire", "api_key": "Clé API", @@ -427,6 +448,7 @@ "app_settings": "Paramètres de l'application", "appears_in": "Apparaît dans", "archive": "Archiver", + "archive_action_prompt": "{count} ajouté(s) à l'archive", "archive_or_unarchive_photo": "Archiver ou désarchiver une photo", "archive_page_no_archived_assets": "Aucun élément archivé n'a été trouvé", "archive_page_title": "Archiver ({count})", @@ -464,7 +486,6 @@ "assets": "Médias", "assets_added_count": "{count, plural, one {# média ajouté} other {# médias ajoutés}}", "assets_added_to_album_count": "{count, plural, one {# média ajouté} other {# médias ajoutés}} à l'album", - "assets_added_to_name_count": "{count, plural, one {# média ajouté} other {# médias ajoutés}} à {hasName, select, true {{name}} other {new album}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Le média ne peut pas être ajouté} other {Les médias ne peuvent pas être ajoutés}} à l'album", "assets_count": "{count, plural, one {# média} other {# médias}}", "assets_deleted_permanently": "{count} média(s) supprimé(s) définitivement", @@ -490,6 +511,7 @@ "back_close_deselect": "Retournez en arrière, fermez ou désélectionnez", "background_location_permission": "Permission de localisation en arrière plan", "background_location_permission_content": "Afin de pouvoir changer d'adresse en arrière plan, Immich doit avoir *en permanence* accès à la localisation précise, afin d'accéder au le nom du réseau Wi-Fi utilisé", + "backup": "Sauvegarde", "backup_album_selection_page_albums_device": "Albums sur l'appareil ({count})", "backup_album_selection_page_albums_tap": "Tapez pour inclure, tapez deux fois pour exclure", "backup_album_selection_page_assets_scatter": "Les éléments peuvent être répartis sur plusieurs albums. De ce fait, les albums peuvent être inclus ou exclus pendant le processus de sauvegarde.", @@ -542,7 +564,7 @@ "backup_controller_page_to_backup": "Albums à sauvegarder", "backup_controller_page_total_sub": "Toutes les photos et vidéos uniques des albums sélectionnés", "backup_controller_page_turn_off": "Désactiver la sauvegarde", - "backup_controller_page_turn_on": "Activer la sauvegarde", + "backup_controller_page_turn_on": "Activer la sauvegarde au premier plan", "backup_controller_page_uploading_file_info": "Envoi des informations du fichier", "backup_err_only_album": "Impossible de retirer le seul album", "backup_info_card_assets": "éléments", @@ -553,6 +575,8 @@ "backup_options_page_title": "Options de sauvegarde", "backup_setting_subtitle": "Ajuster les paramètres d'envoi au premier et en arrière-plan", "backward": "Arrière", + "beta_sync": "Statut de la synchronisation béta", + "beta_sync_subtitle": "Gérer le nouveau système de synchronisation", "biometric_auth_enabled": "Authentification biométrique activée", "biometric_locked_out": "L'authentification biométrique est verrouillé", "biometric_no_options": "Aucune option biométrique disponible", @@ -570,7 +594,7 @@ "cache_settings_clear_cache_button": "Effacer le cache", "cache_settings_clear_cache_button_title": "Efface le cache de l'application. Cela aura un impact significatif sur les performances de l'application jusqu'à ce que le cache soit reconstruit.", "cache_settings_duplicated_assets_clear_button": "EFFACER", - "cache_settings_duplicated_assets_subtitle": "Photos et vidéos qui sont exclues par l'application", + "cache_settings_duplicated_assets_subtitle": "Photos et vidéos qui sont ignorées par l'application", "cache_settings_duplicated_assets_title": "Médias dupliqués ({count})", "cache_settings_statistics_album": "Miniatures de la bibliothèque", "cache_settings_statistics_full": "Images complètes", @@ -587,6 +611,7 @@ "cancel": "Annuler", "cancel_search": "Annuler la recherche", "canceled": "Annulé", + "canceling": "Annulation", "cannot_merge_people": "Impossible de fusionner les personnes", "cannot_undo_this_action": "Vous ne pouvez pas annuler cette action !", "cannot_update_the_description": "Impossible de mettre à jour la description", @@ -700,10 +725,11 @@ "current_server_address": "Adresse actuelle du serveur", "custom_locale": "Paramètres régionaux personnalisés", "custom_locale_description": "Afficher les dates et nombres en fonction des paramètres régionaux", + "custom_url": "URL personnalisée", "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Sombre", - "darkTheme": "Basculer sur le thème sombre", + "dark_theme": "Activer le thème sombre", "date_after": "Date après", "date_and_time": "Date et heure", "date_before": "Date avant", @@ -719,6 +745,8 @@ "default_locale": "Région par défaut", "default_locale_description": "Afficher les dates et nombres en fonction des paramètres de votre navigateur", "delete": "Supprimer", + "delete_action_confirmation_message": "Êtes-vous sûr de vouloir supprimer ce média ? Cela déplacera le média dans la poubelle du serveur et vous demandera si vous voulez le supprimer localement", + "delete_action_prompt": "{count} supprimé(s)", "delete_album": "Supprimer l'album", "delete_api_key_prompt": "Voulez-vous vraiment supprimer cette clé API ?", "delete_dialog_alert": "Ces médias seront définitivement supprimés de Immich et de votre appareil", @@ -732,9 +760,12 @@ "delete_key": "Supprimer la clé", "delete_library": "Supprimer la bibliothèque", "delete_link": "Supprimer le lien", + "delete_local_action_prompt": "{count} supprimé(s) localement", "delete_local_dialog_ok_backed_up_only": "Suppression des données sauvegardées uniquement", "delete_local_dialog_ok_force": "Supprimer tout de même", "delete_others": "Supprimer les autres", + "delete_permanently": "Supprimer définitivement", + "delete_permanently_action_prompt": "{count} supprimé(s) définitivement", "delete_shared_link": "Supprimer le lien partagé", "delete_shared_link_dialog_title": "Supprimer le lien partagé", "delete_tag": "Supprimer l'étiquette", @@ -745,6 +776,7 @@ "description": "Description", "description_input_hint_text": "Ajouter une description...", "description_input_submit_error": "Erreur de mise à jour de la description, vérifier le journal pour plus de détails", + "deselect_all": "Tout désélectionner", "details": "Détails", "direction": "Ordre", "disabled": "Désactivé", @@ -762,6 +794,7 @@ "documentation": "Documentation", "done": "Terminé", "download": "Télécharger", + "download_action_prompt": "Téléchargement de {count} éléments", "download_canceled": "Téléchargement annulé", "download_complete": "Téléchargement terminé", "download_enqueue": "Téléchargement en attente", @@ -799,6 +832,7 @@ "edit_key": "Modifier la clé", "edit_link": "Modifier le lien", "edit_location": "Modifier la localisation", + "edit_location_action_prompt": "{count} localisation(s) mise(s) à jour", "edit_location_dialog_title": "Localisation", "edit_name": "Modifier le nom", "edit_people": "Modifier les personnes", @@ -817,10 +851,11 @@ "empty_trash": "Vider la corbeille", "empty_trash_confirmation": "Êtes-vous sûr de vouloir vider la corbeille ? Cela supprimera définitivement de Immich tous les médias qu'elle contient.\nVous ne pouvez pas annuler cette action !", "enable": "Active", + "enable_backup": "Activer la sauvegarde", "enable_biometric_auth_description": "Entrez votre code PIN pour activer l'authentification biométrique", "enabled": "Activé", "end_date": "Date de fin", - "enqueued": "Mis en file", + "enqueued": "Mis en file d'attente", "enter_wifi_name": "Entrez le nom du réseau wifi", "enter_your_pin_code": "Entrez votre code PIN", "enter_your_pin_code_subtitle": "Entrez votre code PIN pour accéder au dossier verrouillé", @@ -973,6 +1008,8 @@ "explorer": "Explorateur", "export": "Exporter", "export_as_json": "Exporter en JSON", + "export_database": "Exporter la base de données", + "export_database_description": "Exporter la base de données SQLite", "extension": "Extension", "external": "Externe", "external_libraries": "Bibliothèques externes", @@ -984,6 +1021,7 @@ "failed_to_load_assets": "Échec du chargement des ressources", "failed_to_load_folder": "Échec de chargement du dossier", "favorite": "Favori", + "favorite_action_prompt": "{count} ajouté(s) aux Favoris", "favorite_or_unfavorite_photo": "Ajouter ou supprimer des favoris", "favorites": "Favoris", "favorites_page_no_favorites": "Aucun élément favori n'a été trouvé", @@ -1023,6 +1061,9 @@ "haptic_feedback_switch": "Activer le retour haptique", "haptic_feedback_title": "Retour haptique", "has_quota": "Quota", + "hash_asset": "Hasher le média", + "hashed_assets": "Média hashés", + "hashing": "Hash", "header_settings_add_header_tip": "Ajouter un en-tête", "header_settings_field_validator_msg": "Cette valeur ne peut pas être vide", "header_settings_header_name_input": "Nom de l'en-tête", @@ -1036,7 +1077,7 @@ "hide_password": "Masquer le mot de passe", "hide_person": "Masquer la personne", "hide_unnamed_people": "Cacher les personnes non nommées", - "home_page_add_to_album_conflicts": "{added} éléments ajoutés à l'album {album}. Les éléments {failed} sont déjà dans l'album.", + "home_page_add_to_album_conflicts": "{added} éléments ajoutés à l'album {album}. {failed} éléments sont déjà dans l'album.", "home_page_add_to_album_err_local": "Impossible d'ajouter des médias locaux aux albums, ils sont ignorés", "home_page_add_to_album_success": "{added} éléments ajoutés à l'album {album}.", "home_page_album_err_partner": "Impossible d'ajouter des médias d'un partenaire à un album, ils sont ignorés", @@ -1055,6 +1096,7 @@ "host": "Hôte", "hour": "Heure", "id": "ID", + "idle": "Inactif", "ignore_icloud_photos": "Ignorer les photos iCloud", "ignore_icloud_photos_description": "Les photos stockées sur iCloud ne seront pas envoyées sur le serveur Immich", "image": "Image", @@ -1112,6 +1154,7 @@ "language_no_results_title": "Aucune langue trouvée", "language_search_hint": "Recherche de langues...", "language_setting_description": "Sélectionnez votre langue préférée", + "large_files": "Fichiers volumineux", "last_seen": "Dernièrement utilisé", "latest_version": "Dernière version", "latitude": "Latitude", @@ -1127,16 +1170,18 @@ "library_page_sort_created": "Créations les plus récentes", "library_page_sort_last_modified": "Dernière modification", "library_page_sort_title": "Titre de l'album", + "licenses": "Licences", "light": "Clair", "like_deleted": "Réaction « j'aime » supprimée", "link_motion_video": "Lier la photo animée", - "link_options": "Options de lien", "link_to_oauth": "Lien au service OAuth", "linked_oauth_account": "Compte OAuth rattaché", "list": "Liste", "loading": "Chargement", "loading_search_results_failed": "Chargement des résultats échoué", + "local": "Local", "local_asset_cast_failed": "Impossible de caster un média qui n'a pas envoyé vers le serveur", + "local_assets": "Média locaux", "local_network": "Réseau local", "local_network_sheet_info": "L'application va se connecter au serveur via cette URL quand l'appareil est connecté à ce réseau Wi-Fi", "location_permission": "Autorisation de localisation", @@ -1193,8 +1238,7 @@ "manage_your_devices": "Gérer vos appareils", "manage_your_oauth_connection": "Gérer votre connexion OAuth", "map": "Carte", - "map_assets_in_bound": "{count} photo", - "map_assets_in_bounds": "{count} photos", + "map_assets_in_bounds": "{count, plural, one {# photo} other {# photos}}", "map_cannot_get_user_location": "Impossible d'obtenir la localisation de l'utilisateur", "map_location_dialog_yes": "Oui", "map_location_picker_page_use_location": "Utiliser ma position", @@ -1246,6 +1290,7 @@ "more": "Plus", "move": "Déplacer", "move_off_locked_folder": "Déplacer en dehors du dossier verrouillé", + "move_to_lock_folder_action_prompt": "{count} ajouté(s) au dossier verrouillé", "move_to_locked_folder": "Déplacer dans le dossier verrouillé", "move_to_locked_folder_confirmation": "Ces photos et vidéos seront retirés de tout les albums et ne seront visibles que dans le dossier verrouillé", "moved_to_archive": "{count, plural, one {# élément déplacé} other {# éléments déplacés}} vers les archives", @@ -1292,6 +1337,7 @@ "no_results": "Aucun résultat", "no_results_description": "Essayez un synonyme ou un mot-clé plus général", "no_shared_albums_message": "Créer un album pour partager vos photos et vidéos avec les personnes de votre réseau", + "no_uploads_in_progress": "Pas d'envoi en cours", "not_in_any_album": "Dans aucun album", "not_selected": "Non sélectionné", "note_apply_storage_label_to_previously_uploaded assets": "Note : Pour appliquer l'étiquette de stockage aux médias précédemment envoyés, exécutez", @@ -1329,6 +1375,7 @@ "original": "original", "other": "Autre", "other_devices": "Autres appareils", + "other_entities": "Autres entités", "other_variables": "Autres variables", "owned": "Possédé", "owner": "Propriétaire", @@ -1390,7 +1437,7 @@ "photos_and_videos": "Photos et vidéos", "photos_count": "{count, plural, one {{count, number} Photo} other {{count, number} Photos}}", "photos_from_previous_years": "Photos des années précédentes", - "pick_a_location": "Choisissez un lieu", + "pick_a_location": "Choisissez une localisation", "pin_code_changed_successfully": "Code PIN changé avec succès", "pin_code_reset_successfully": "Réinitialisation du code PIN réussie", "pin_code_setup_successfully": "Définition du code PIN réussie", @@ -1460,6 +1507,7 @@ "purchase_server_description_2": "Statut de contributeur", "purchase_server_title": "Serveur", "purchase_settings_server_activated": "La clé du produit pour le Serveur est gérée par l'administrateur", + "queue_status": "{count}/{total} en file d'attente", "rating": "Étoile d'évaluation", "rating_clear": "Effacer l'évaluation", "rating_count": "{count, plural, one {# étoile} other {# étoiles}}", @@ -1488,6 +1536,8 @@ "refreshing_faces": "Actualisation des visages", "refreshing_metadata": "Actualisation des métadonnées", "regenerating_thumbnails": "Regénération des miniatures", + "remote": "À distance", + "remote_assets": "Média à distance", "remove": "Supprimer", "remove_assets_album_confirmation": "Êtes-vous sûr de vouloir supprimer {count, plural, one {# média} other {# médias}} de l'album ?", "remove_assets_shared_link_confirmation": "Êtes-vous sûr de vouloir supprimer {count, plural, one {# média} other {# médias}} de ce lien partagé ?", @@ -1495,7 +1545,9 @@ "remove_custom_date_range": "Supprimer la plage de date personnalisée", "remove_deleted_assets": "Supprimer les fichiers hors ligne", "remove_from_album": "Supprimer de l'album", + "remove_from_album_action_prompt": "{count} supprimé(s) de l'album", "remove_from_favorites": "Supprimer des favoris", + "remove_from_lock_folder_action_prompt": "{count} supprimé(s) du dossier verrouillé", "remove_from_locked_folder": "Supprimer du dossier verrouillé", "remove_from_locked_folder_confirmation": "Êtes vous sûr de vouloir déplacer ces photos et vidéos en dehors du dossier verrouillé ? Elles seront visibles dans votre galerie.", "remove_from_shared_link": "Supprimer des liens partagés", @@ -1510,7 +1562,7 @@ "removed_from_favorites_count": "{count, plural, one {# supprimé} other {# supprimés}} des favoris", "removed_memory": "Souvenir supprimé", "removed_photo_from_memory": "Photo supprimée du souvenir", - "removed_tagged_assets": "Tag supprimé de {count, plural, one {# média} other {# médias}}", + "removed_tagged_assets": "Étiquette supprimée de {count, plural, one {# média} other {# médias}}", "rename": "Renommer", "repair": "Réparer", "repair_no_results_message": "Les fichiers non importés ou absents s'afficheront ici", @@ -1523,19 +1575,25 @@ "reset_password": "Réinitialiser le mot de passe", "reset_people_visibility": "Réinitialiser la visibilité des personnes", "reset_pin_code": "Réinitialiser le code PIN", + "reset_sqlite": "Réinitialiser la base de données SQLite", + "reset_sqlite_confirmation": "Êtes-vous certain de vouloir réinitialiser la base de données SQLite ? Vous devrez vous déconnecter puis vous reconnecter à nouveau pour resynchroniser les données", + "reset_sqlite_success": "La base de données SQLite à été réinitialisé avec succès", "reset_to_default": "Rétablir les valeurs par défaut", "resolve_duplicates": "Résoudre les doublons", "resolved_all_duplicates": "Résolution de tous les doublons", "restore": "Restaurer", "restore_all": "Tout restaurer", + "restore_trash_action_prompt": "{count} restauré de la corbeille", "restore_user": "Restaurer l'utilisateur", "restored_asset": "Média restauré", "resume": "Reprendre", "retry_upload": "Réessayer l'envoi", "review_duplicates": "Consulter les doublons", + "review_large_files": "Consulter les fichiers volumineux", "role": "Rôle", "role_editor": "Éditeur", "role_viewer": "Visionneuse", + "running": "En cours", "save": "Sauvegarder", "save_to_gallery": "Enregistrer", "saved_api_key": "Clé API sauvegardée", @@ -1566,8 +1624,8 @@ "search_filter_display_option_not_in_album": "Pas dans un album", "search_filter_display_options": "Options d'affichage", "search_filter_filename": "Recherche par nom de fichier", - "search_filter_location": "Lieu", - "search_filter_location_title": "Sélectionner un lieu", + "search_filter_location": "Localisation", + "search_filter_location_title": "Sélectionner une localisation", "search_filter_media_type": "Type de média", "search_filter_media_type_title": "Sélectionner type de média", "search_filter_people_title": "Sélectionner une personne", @@ -1667,6 +1725,7 @@ "settings_saved": "Paramètres sauvegardés", "setup_pin_code": "Définir un code PIN", "share": "Partager", + "share_action_prompt": "{count} éléments partagés", "share_add_photos": "Ajouter des photos", "share_assets_selected": "{count} sélectionné(s)", "share_dialog_preparing": "Préparation...", @@ -1688,6 +1747,7 @@ "shared_link_clipboard_copied_massage": "Copié dans le presse-papier", "shared_link_clipboard_text": "Lien : {link}\nMot de passe : {password}", "shared_link_create_error": "Erreur pendant la création du lien partagé", + "shared_link_custom_url_description": "Accéder à ce lien partagé avec une URL personnalisée", "shared_link_edit_description_hint": "Saisir la description du partage", "shared_link_edit_expire_after_option_day": "1 jour", "shared_link_edit_expire_after_option_days": "{count} jours", @@ -1713,6 +1773,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Gérer les liens partagés", "shared_link_options": "Options de lien partagé", + "shared_link_password_description": "Demander un mot de passe pour accéder à ce lien partagé", "shared_links": "Liens partagés", "shared_links_description": "Partager les photos et vidéos via un lien", "shared_photos_and_videos_count": "{assetCount, plural, other {# photos et vidéos partagées.}}", @@ -1768,6 +1829,7 @@ "sort_title": "Titre", "source": "Source", "stack": "Empiler", + "stack_action_prompt": "{count} groupé(s)", "stack_duplicates": "Empiler les doublons", "stack_select_one_photo": "Sélectionnez une photo principale pour la pile", "stack_selected_photos": "Empiler les photos sélectionnées", @@ -1787,6 +1849,7 @@ "storage_quota": "Quota de stockage", "storage_usage": "{used} sur {available} utilisé", "submit": "Soumettre", + "success": "Réussi", "suggestions": "Suggestions", "sunrise_on_the_beach": "Lever de soleil sur la plage", "support": "Soutenir", @@ -1796,6 +1859,8 @@ "sync": "Synchroniser", "sync_albums": "Synchroniser dans des albums", "sync_albums_manual_subtitle": "Synchroniser toutes les vidéos et photos envoyées dans les albums sélectionnés", + "sync_local": "Synchronisation locale", + "sync_remote": "Synchronisation à distance", "sync_upload_album_setting_subtitle": "Créez et envoyez vos photos et vidéos dans les albums sélectionnés sur Immich", "tag": "Étiquette", "tag_assets": "Étiqueter les médias", @@ -1806,6 +1871,7 @@ "tag_updated": "Étiquette mise à jour : {tag}", "tagged_assets": "Étiquette ajoutée à {count, plural, one {# média} other {# médias}}", "tags": "Étiquettes", + "tap_to_run_job": "Appuyez pour démarrer la tâche", "template": "Modèle", "theme": "Thème", "theme_selection": "Sélection du thème", @@ -1838,6 +1904,7 @@ "total": "Total", "total_usage": "Utilisation globale", "trash": "Corbeille", + "trash_action_prompt": "{count} mis à la corbeille", "trash_all": "Tout supprimer", "trash_count": "Corbeille {count, number}", "trash_delete_asset": "Mettre à la corbeille/Supprimer un média", @@ -1855,9 +1922,11 @@ "unable_to_change_pin_code": "Impossible de changer le code PIN", "unable_to_setup_pin_code": "Impossible de définir le code PIN", "unarchive": "Désarchiver", + "unarchive_action_prompt": "{count} supprimé(s) de l'archive", "unarchived_count": "{count, plural, one {# supprimé} other {# supprimés}} de l'archive", "undo": "Annuler", "unfavorite": "Enlever des favoris", + "unfavorite_action_prompt": "{count} supprimé(s) des favoris", "unhide_person": "Afficher la personne", "unknown": "Inconnu", "unknown_country": "Pays non connu", @@ -1875,15 +1944,20 @@ "unselect_all_duplicates": "Désélectionner tous les doublons", "unselect_all_in": "Tout désélectionner dans {group}", "unstack": "Désempiler", + "unstack_action_prompt": "{count} non groupés", "unstacked_assets_count": "{count, plural, one {# média dépilé} other {# médias dépilés}}", + "untagged": "Étiquette supprimée", "up_next": "Suite", "updated_at": "Mis à jour à", "updated_password": "Mot de passe mis à jour", "upload": "Envoyer", + "upload_action_prompt": "{count} en attente d'envoi", "upload_concurrency": "Envois simultanés", + "upload_details": "Détails des envois", "upload_dialog_info": "Voulez-vous sauvegarder la sélection vers le serveur ?", "upload_dialog_title": "Envoyer le média", "upload_errors": "L'envoi s'est complété avec {count, plural, one {# erreur} other {# erreurs}}. Rafraîchissez la page pour voir les nouveaux médias envoyés.", + "upload_finished": "Envoi fini", "upload_progress": "{remaining, number} restant(s) - {processed, number} traité(s)/{total, number}", "upload_skipped_duplicates": "{count, plural, one {# doublon ignoré} other {# doublons ignorés}}", "upload_status_duplicates": "Doublons", @@ -1892,6 +1966,7 @@ "upload_success": "Envoi réussi. Rafraîchissez la page pour voir les nouveaux médias envoyés.", "upload_to_immich": "Envoyer vers Immich ({count})", "uploading": "Envoi", + "uploading_media": "Envoi du média", "url": "URL", "usage": "Utilisation", "use_biometric": "Utiliser l'authentification biométrique", @@ -1912,6 +1987,7 @@ "user_usage_stats_description": "Voir les statistiques d'utilisation du compte", "username": "Nom d'utilisateur", "users": "Utilisateurs", + "users_added_to_album_count": "{count, plural, one {# utilisateur ajouté} other {# utilisateurs ajoutés}} à l'album", "utilities": "Utilitaires", "validate": "Valider", "validate_endpoint_error": "Merci d'entrer un lien valide", @@ -1930,6 +2006,7 @@ "view_album": "Afficher l'album", "view_all": "Voir tout", "view_all_users": "Voir tous les utilisateurs", + "view_details": "Voir les détails", "view_in_timeline": "Voir dans la vue chronologique", "view_link": "Voir le lien", "view_links": "Voir les liens", diff --git a/i18n/gl.json b/i18n/gl.json index 558cc48900..92b3c8fb13 100644 --- a/i18n/gl.json +++ b/i18n/gl.json @@ -455,7 +455,6 @@ "assets": "Activos", "assets_added_count": "Engadido {count, plural, one {# activo} other {# activos}}", "assets_added_to_album_count": "Engadido {count, plural, one {# activo} other {# activos}} ao álbum", - "assets_added_to_name_count": "Engadido {count, plural, one {# activo} other {# activos}} a {hasName, select, true {{name}} other {novo álbum}}", "assets_count": "{count, plural, one {# activo} other {# activos}}", "assets_deleted_permanently": "{count} activo(s) eliminado(s) permanentemente", "assets_deleted_permanently_from_server": "{count} activo(s) eliminado(s) permanentemente do servidor Immich", @@ -477,6 +476,7 @@ "back_close_deselect": "Atrás, pechar ou deseleccionar", "background_location_permission": "Permiso de ubicación en segundo plano", "background_location_permission_content": "Para cambiar de rede cando se executa en segundo plano, Immich debe ter *sempre* acceso á ubicación precisa para que a aplicación poida ler o nome da rede wifi", + "backup": "Copia de Seguridade", "backup_album_selection_page_albums_device": "Álbums no dispositivo ({count})", "backup_album_selection_page_albums_tap": "Tocar para incluír, dobre toque para excluír", "backup_album_selection_page_assets_scatter": "Os activos poden dispersarse por varios álbums. Polo tanto, os álbums poden incluírse ou excluírse durante o proceso de copia de seguridade.", @@ -1071,7 +1071,6 @@ "light": "Claro", "like_deleted": "Gústame eliminado", "link_motion_video": "Ligar vídeo en movemento", - "link_options": "Opcións da ligazón", "link_to_oauth": "Ligar a OAuth", "linked_oauth_account": "Conta OAuth ligada", "list": "Lista", @@ -1130,7 +1129,6 @@ "manage_your_devices": "Xestionar os teus dispositivos con sesión iniciada", "manage_your_oauth_connection": "Xestionar a túa conexión OAuth", "map": "Mapa", - "map_assets_in_bound": "{count} foto", "map_assets_in_bounds": "{count} fotos", "map_cannot_get_user_location": "Non se pode obter a ubicación do usuario", "map_location_dialog_yes": "Si", diff --git a/i18n/he.json b/i18n/he.json index 737c307f31..3139c2cde6 100644 --- a/i18n/he.json +++ b/i18n/he.json @@ -14,6 +14,7 @@ "add_a_location": "הוספת מיקום", "add_a_name": "הוספת שם", "add_a_title": "הוספת כותרת", + "add_birthday": "הוספת יום הולדת", "add_endpoint": "הוסף נקודת קצה", "add_exclusion_pattern": "הוספת דפוס החרגה", "add_import_path": "הוספת נתיב יבוא", @@ -34,6 +35,7 @@ "added_to_favorites_count": "{count, number} נוספו למועדפים", "admin": { "add_exclusion_pattern_description": "הוספת דפוסי החרגה. נתמכת התאמת דפוסים באמצעות *, ** ו-?. כדי להתעלם מכל הקבצים בתיקיה כלשהי בשם \"Raw\", יש להשתמש ב \"**/Raw/**\". כדי להתעלם מכל הקבצים המסתיימים ב \"tif.\", יש להשתמש ב \"tif.*/**\". כדי להתעלם מנתיב מוחלט, יש להשתמש ב \"**/נתיב/להתעלמות\".", + "admin_user": "מנהל מערכת", "asset_offline_description": "תמונה מספרייה חיצונית זו לא נמצאת יותר בדיסק והועברה לאשפה. אם הקובץ הועבר מתוך הספרייה, נא לבדוק את ציר הזמן שלך עבור התמונה המקבילה החדש. כדי לשחזר תמונה זו, נא לוודא ש-Immich יכול לגשת אל נתיב הקובץ למטה ולסרוק מחדש את הספרייה.", "authentication_settings": "הגדרות התחברות", "authentication_settings_description": "ניהול סיסמה, OAuth, והגדרות התחברות אחרות", @@ -43,6 +45,13 @@ "backup_database": "גיבוי מסד נתונים", "backup_database_enable_description": "אפשר גיבויי מסד נתונים", "backup_keep_last_amount": "כמות של גיבויים קודמים שיש לשמור", + "backup_onboarding_1_description": "העתק בענן או במיקום פיזי אחר מחוץ למקום השרת.", + "backup_onboarding_2_description": "העתקים מקומיים במכשירים שונים. זה כולל את הקבצים הראשיים וגיבוי של הקבצים האלה באופן מקומי.", + "backup_onboarding_3_description": "סך כל ההעתקים של הנתונים שלך, כולל הקבצים המקוריים. זה כולל העתק אחד מחוץ למקום השרת ושני העתקים מקומיים.", + "backup_onboarding_description": "אסטרטגיית גיבוי 3-2-1 הינה מומלצת על מנת להגן על הנתונים שלך. עליך להשאיר העתקים של תמונות/סרטונים שהועלו כמו גם את מסד הנתונים של Immich עבור פתרון גיבוי מקיף.", + "backup_onboarding_footer": "עבור מידע נוסף על גיבוי Immich, נא לפנות אל התיעוד.", + "backup_onboarding_parts_title": "גיבוי 3-2-1 כולל:", + "backup_onboarding_title": "גיבויים", "backup_settings": "הגדרות גיבוי", "backup_settings_description": "ניהול הגדרות גיבוי מסד נתונים.", "cleared_jobs": "נוקו משימות עבור: {job}", @@ -155,16 +164,30 @@ "map_settings": "מפה", "map_settings_description": "ניהול הגדרות מפה", "map_style_description": "כתובת אתר לערכת נושא של מפה style.json", - "memory_cleanup_job": "ניקוי זיכרון", - "memory_generate_job": "יצירת זיכרון", + "memory_cleanup_job": "ניקוי זיכרון (היום לפני..)", + "memory_generate_job": "יצירת זיכרון (היום לפני..)", "metadata_extraction_job": "חלץ מטא-נתונים", "metadata_extraction_job_description": "חלץ מטא-נתונים מכל תמונה, כגון GPS, פנים ורזולוציה", "metadata_faces_import_setting": "אפשר יבוא פנים", "metadata_faces_import_setting_description": "יבא פנים מנתוני EXIF של תמונה ומקבצים נלווים", "metadata_settings": "הגדרות מטא-נתונים", - "metadata_settings_description": "ניהול הגדרות מטא-נתונים", - "migration_job": "העברה", + "metadata_settings_description": "ניהול הגדרות metadata", + "migration_job": "נדידה", "migration_job_description": "העבר תמונות ממוזערות של תמונות ופנים למבנה התיקיות העדכני ביותר", + "nightly_tasks_cluster_faces_setting_description": "בצע זיהוי פנים עבור פרצופים שזוהו לאחרונה", + "nightly_tasks_cluster_new_faces_setting": "קבץ פנים חדשות", + "nightly_tasks_database_cleanup_setting": "משימות תחזוקה וניקוי של מסד הנתונים", + "nightly_tasks_database_cleanup_setting_description": "נקה נתונים ישנים שפג תוקפם ממסד הנתונים", + "nightly_tasks_generate_memories_setting": "יצירת זכרונות", + "nightly_tasks_generate_memories_setting_description": "צור זכרונות חדשים מהתמונות שלך", + "nightly_tasks_missing_thumbnails_setting": "צור תמונות ממוזערות חסרות", + "nightly_tasks_missing_thumbnails_setting_description": "הוסף לתור קבצים ללא תמונות ממוזערות ליצירה של תמונות ממוזערות", + "nightly_tasks_settings": "הגדרות של משימות ליליות", + "nightly_tasks_settings_description": "נהל משימות ליליות", + "nightly_tasks_start_time_setting": "זמן התחלה", + "nightly_tasks_start_time_setting_description": "השעה שבה השרת מתחיל להריץ את המשימות הליליות", + "nightly_tasks_sync_quota_usage_setting": "סנכרון מכסת שימוש", + "nightly_tasks_sync_quota_usage_setting_description": "עדכן את מכסת האחסון של המשתמש בהתאם לשימוש הנוכחי", "no_paths_added": "לא נוספו נתיבים", "no_pattern_added": "לא נוספה תבנית", "note_apply_storage_label_previous_assets": "הערה: כדי להחיל את תווית האחסון על תמונות שהועלו בעבר, הפעל את", @@ -195,6 +218,8 @@ "oauth_mobile_redirect_uri": "URI להפניה מחדש בנייד", "oauth_mobile_redirect_uri_override": "עקיפת URI להפניה מחדש בנייד", "oauth_mobile_redirect_uri_override_description": "אפשר כאשר ספק OAuth לא מאפשר כתובת URI לנייד, כמו ''{callback}''", + "oauth_role_claim": "דרישת תפקיד", + "oauth_role_claim_description": "הענק גישת מנהל באופן אוטומטי אם תביעה זו קיימת. ערך התביעה יכול להיות 'user' או 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "ניהול הגדרות התחברות עם OAuth", "oauth_settings_more_details": "למידע נוסף אודות תכונה זו, בדוק את התיעוד.", @@ -203,7 +228,7 @@ "oauth_storage_quota_claim": "דרישת מכסת אחסון", "oauth_storage_quota_claim_description": "הגדר אוטומטית את מכסת האחסון של המשתמש לערך של דרישה זו.", "oauth_storage_quota_default": "מכסת אחסון ברירת מחדל (GiB)", - "oauth_storage_quota_default_description": "מכסה ב-GiB לשימוש כאשר לא מסופקת דרישה (הזן 0 עבור מכסה בלתי מוגבלת).", + "oauth_storage_quota_default_description": "מכסה ב-GiB לשימוש כאשר לא מסופקת דרישה.", "oauth_timeout": "הבקשה נכשלה – הזמן הקצוב הסתיים", "oauth_timeout_description": "זמן קצוב לבקשות (במילישניות)", "password_enable_description": "התחבר עם דוא\"ל וסיסמה", @@ -243,6 +268,7 @@ "storage_template_migration_info": "תבנית האחסון תמיר את כל ההרחבות לאותיות קטנות. שינויים בתבנית יחולו רק על תמונות חדשות. כדי להחיל באופן רטרואקטיבי את התבנית על תמונות שהועלו בעבר, הפעל את {job}.", "storage_template_migration_job": "משימת העברת תבנית אחסון", "storage_template_more_details": "לפרטים נוספים אודות תכונה זו, עיין בתבנית האחסון ובהשלכותיה", + "storage_template_onboarding_description_v2": "כאשר פיצ’ר זה מופעל, הקבצים יאורגנו אוטומטית לפי תבנית שהוגדרה על ידי המשתמש. למידע נוסף, עיין ב־תיעוד.", "storage_template_path_length": "מגבלת אורך נתיב משוערת: {length, number}/{limit, number}", "storage_template_settings": "תבנית אחסון", "storage_template_settings_description": "ניהול מבנה התיקיות ואת שם הקובץ של התמונה שהועלתה", @@ -355,10 +381,12 @@ "admin_password": "סיסמת מנהל", "administration": "ניהול", "advanced": "מתקדם", + "advanced_settings_beta_timeline_subtitle": "נסה את חווית האפליקציה החדשה", + "advanced_settings_beta_timeline_title": "ציר זמן (בטא)", "advanced_settings_enable_alternate_media_filter_subtitle": "השתמש באפשרות זו כדי לסנן מדיה במהלך הסנכרון לפי קריטריונים חלופיים. מומלץ להשתמש בזה רק אם יש בעיה בזיהוי כל האלבומים באפליקציה.", "advanced_settings_enable_alternate_media_filter_title": "[ניסיוני] השתמש במסנן סנכרון אלבום חלופי שמבכשיר", "advanced_settings_log_level_title": "רמת רישום ביומן: {level}", - "advanced_settings_prefer_remote_subtitle": "חלק מהמכשירים הם איטיים מאד לטעינה של תמונות ממוזערות מתמונות שבמכשיר. הפעל הגדרה זו כדי לטעון תמונות מרוחקות במקום.", + "advanced_settings_prefer_remote_subtitle": "במכשירים מסוימים טעינת תמונות ממוזערות מקבצים מקומיים עלולה להיות איטית במיוחד. הפעל הגדרה זו כדי לטעון תמונות מרוחקות במקום זאת.", "advanced_settings_prefer_remote_title": "העדף תמונות מרוחקות", "advanced_settings_proxy_headers_subtitle": "הגדר proxy headers שהיישום צריך לשלוח עם כל בקשת רשת", "advanced_settings_proxy_headers_title": "כותרות פרוקסי", @@ -377,6 +405,7 @@ "album_cover_updated": "עטיפת האלבום עודכנה", "album_delete_confirmation": "האם באמת ברצונך למחוק את האלבום {album}?", "album_delete_confirmation_description": "אם האלבום הזה משותף, משתמשים אחרים לא יוכלו לגשת אליו יותר.", + "album_deleted": "אלבום נמחק", "album_info_card_backup_album_excluded": "הוחרגו", "album_info_card_backup_album_included": "נכללו", "album_info_updated": "מידע האלבום עודכן", @@ -386,6 +415,7 @@ "album_options": "אפשרויות האלבום", "album_remove_user": "להסיר משתמש?", "album_remove_user_confirmation": "האם באמת ברצונך להסיר את {user}?", + "album_search_not_found": "לא נמצאו אלבומים התואמים לחיפוש שלך", "album_share_no_users": "נראה ששיתפת את האלבום הזה עם כל המשתמשים או שאין לך אף משתמש לשתף איתו.", "album_updated": "אלבום עודכן", "album_updated_setting_description": "קבל הודעת דוא\"ל כאשר לאלבום משותף יש תמונות חדשות", @@ -405,6 +435,7 @@ "albums_default_sort_order": "סדר מיון אלבומים ברירת מחדל", "albums_default_sort_order_description": "סדר מיון תמונות ראשוני בעת יצירת אלבומים חדשים.", "albums_feature_description": "אוספים של תמונות אשר ניתנים לשיתוף עם משתמשים אחרים.", + "albums_on_device_count": "אלבומים במכשיר ({count})", "all": "הכל", "all_albums": "כל האלבומים", "all_people": "כל האנשים", @@ -425,6 +456,7 @@ "app_settings": "הגדרות יישום", "appears_in": "מופיע ב", "archive": "ארכיון", + "archive_action_prompt": "{count} נוספו לארכיון", "archive_or_unarchive_photo": "העבר תמונה לארכיון או הוצא אותה משם", "archive_page_no_archived_assets": "לא נמצאו תמונות בארכיון", "archive_page_title": "בארכיון ({count})", @@ -462,7 +494,6 @@ "assets": "תמונות", "assets_added_count": "{count, plural, one {נוספה תומנה #} other {נוספו # תמונות}}", "assets_added_to_album_count": "{count, plural, one {נוספה תמונה #} other {נוספו # תמונות}} לאלבום", - "assets_added_to_name_count": "{count, plural, one {תמונה # נוספה} other {# תמונות נוספו}} אל {hasName, select, true {{name}} other {אלבום חדש}}", "assets_cannot_be_added_to_album_count": "לא ניתן להוסיף את ה{count, plural, one {תמונה} other {תמונות}} לאלבום", "assets_count": "{count, plural, one {תמונה #} other {# תמונות}}", "assets_deleted_permanently": "{count} תמונות נמחקו לצמיתות", @@ -488,6 +519,7 @@ "back_close_deselect": "חזור, סגור, או בטל בחירה", "background_location_permission": "הרשאת מיקום ברקע", "background_location_permission_content": "כדי להחליף רשתות בעת ריצה ברקע, היישום צריך *תמיד* גישה למיקום מדויק על מנת לקרוא את השם של רשת האינטרנט האלחוטי", + "backup": "גיבוי", "backup_album_selection_page_albums_device": "({count}) אלבומים במכשיר", "backup_album_selection_page_albums_tap": "הקש כדי לכלול, הקש פעמיים כדי להחריג", "backup_album_selection_page_assets_scatter": "תמונות יכולות להתפזר על פני אלבומים מרובים. לפיכך, ניתן לכלול או להחריג אלבומים במהלך תהליך הגיבוי.", @@ -532,11 +564,11 @@ "backup_controller_page_none_selected": "אין בחירה", "backup_controller_page_remainder": "בהמתנה לגיבוי", "backup_controller_page_remainder_sub": "תמונות וסרטונים הנותרים לגיבוי מתוך בחירה", - "backup_controller_page_server_storage": "אחסון שרת", + "backup_controller_page_server_storage": "אחסון בשרת", "backup_controller_page_start_backup": "התחל גיבוי", "backup_controller_page_status_off": "גיבוי חזית אוטומטי כבוי", "backup_controller_page_status_on": "גיבוי חזית אוטומטי מופעל", - "backup_controller_page_storage_format": "{total} מתוך {used} בשימוש", + "backup_controller_page_storage_format": "{used}מתוך {total} בשימוש", "backup_controller_page_to_backup": "אלבומים לגבות", "backup_controller_page_total_sub": "כל התמונות והסרטונים הייחודיים מאלבומים שנבחרו", "backup_controller_page_turn_off": "כיבוי גיבוי חזית", @@ -551,6 +583,8 @@ "backup_options_page_title": "אפשרויות גיבוי", "backup_setting_subtitle": "ניהול הגדרות העלאת רקע וחזית", "backward": "אחורה", + "beta_sync": "סטטוס סנכרון (בטא)", + "beta_sync_subtitle": "נהל את מערכת הסנכרון החדשה", "biometric_auth_enabled": "אימות ביומטרי הופעל", "biometric_locked_out": "גישה לאימות הביומטרי נחסמה", "biometric_no_options": "אין אפשרויות זמינות עבור אימות ביומטרי", @@ -568,7 +602,7 @@ "cache_settings_clear_cache_button": "ניקוי מטמון", "cache_settings_clear_cache_button_title": "מנקה את המטמון של היישום. זה ישפיע באופן משמעותי על הביצועים של היישום עד שהמטמון מתמלא מחדש.", "cache_settings_duplicated_assets_clear_button": "נקה", - "cache_settings_duplicated_assets_subtitle": "תמונות וסרטונים שנמצאים ברשימה השחורה של היישום", + "cache_settings_duplicated_assets_subtitle": "תמונות וסרטונים שנמצאים ברשימת ההתעלמות של האפליקציה", "cache_settings_duplicated_assets_title": "({count}) תמונות משוכפלות", "cache_settings_statistics_album": "תמונות ממוזערות של ספרייה", "cache_settings_statistics_full": "תמונות מלאות", @@ -585,6 +619,7 @@ "cancel": "ביטול", "cancel_search": "ביטול חיפוש", "canceled": "בוטל", + "canceling": "מבטל", "cannot_merge_people": "לא ניתן למזג אנשים", "cannot_undo_this_action": "אין באפשרותך לבטל את הפעולה הזו!", "cannot_update_the_description": "לא ניתן לעדכן את התיאור", @@ -698,10 +733,11 @@ "current_server_address": "כתובת שרת נוכחית", "custom_locale": "אזור שפה מותאם אישית", "custom_locale_description": "עצב תאריכים ומספרים על סמך השפה והאזור", + "custom_url": "קישור מותאם אישית", "daily_title_text_date": "E, MMM dd", "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "כהה", - "darkTheme": "החלפה למצב חושך", + "dark_theme": "הפעל/כבה מצב כהה", "date_after": "תאריך אחרי", "date_and_time": "תאריך ושעה", "date_before": "תאריך לפני", @@ -711,12 +747,14 @@ "day": "יום", "deduplicate_all": "ביטול כל הכפילויות", "deduplication_criteria_1": "גודל תמונה בבתים", - "deduplication_criteria_2": "ספירת נתוני EXIF", + "deduplication_criteria_2": "כמות נתוני EXIF", "deduplication_info": "מידע על ביטול כפילויות", "deduplication_info_description": "כדי לבחור מראש תמונות באופן אוטומטי ולהסיר כפילויות בכמות גדולה, אנו מסתכלים על:", "default_locale": "שפת ברירת מחדל", "default_locale_description": "פורמט תאריכים ומספרים מבוסס שפת הדפדפן שלך", "delete": "מחק", + "delete_action_confirmation_message": "האם אתה בטוח שברצונך למחוק את התמונה הזאת? פעולה זו תעביר אותו לאשפה של השרת, ותשאל אם ברצונך למחוק אותו גם מהמכשיר המקומי", + "delete_action_prompt": "{count} נמחקו", "delete_album": "מחק אלבום", "delete_api_key_prompt": "האם אתה בטוח שברצונך למחוק מפתח ה-API הזה?", "delete_dialog_alert": "הפריטים האלה ימחקו לצמיתות מהשרת ומהמכשיר שלך", @@ -730,9 +768,12 @@ "delete_key": "מחק מפתח", "delete_library": "מחק ספרייה", "delete_link": "מחק קישור", + "delete_local_action_prompt": "{count} נמחקו באופן מקומי", "delete_local_dialog_ok_backed_up_only": "מחק את מה שמגובה בלבד", "delete_local_dialog_ok_force": "מחק בכל זאת", "delete_others": "מחק אחרים", + "delete_permanently": "מחק לצמיתות", + "delete_permanently_action_prompt": "{count} נמחקו לצמיתות", "delete_shared_link": "מחק קישור משותף", "delete_shared_link_dialog_title": "מחק קישור משותף", "delete_tag": "מחק תג", @@ -743,6 +784,7 @@ "description": "תיאור", "description_input_hint_text": "הוסף תיאור...", "description_input_submit_error": "שגיאה בעדכון תיאור, בדוק את היומן לפרטים נוספים", + "deselect_all": "בטל הכל", "details": "פרטים", "direction": "כיוון", "disabled": "מושבת", @@ -760,6 +802,7 @@ "documentation": "תיעוד", "done": "סיום", "download": "הורדה", + "download_action_prompt": "מוריד {count} תמונות", "download_canceled": "הורדה בוטלה", "download_complete": "הורדה הושלמה", "download_enqueue": "הורדה נוספה לתור", @@ -786,6 +829,7 @@ "edit": "ערוך", "edit_album": "ערוך אלבום", "edit_avatar": "ערוך תמונת פרופיל", + "edit_birthday": "עריכת יום הולדת", "edit_date": "ערוך תאריך", "edit_date_and_time": "ערוך תאריך ושעה", "edit_description": "ערוך תיאור", @@ -797,6 +841,7 @@ "edit_key": "ערוך מפתח", "edit_link": "ערוך קישור", "edit_location": "ערוך מיקום", + "edit_location_action_prompt": "{count} מיקומים נערכו", "edit_location_dialog_title": "מיקום", "edit_name": "ערוך שם", "edit_people": "ערוך אנשים", @@ -815,6 +860,7 @@ "empty_trash": "רוקן אשפה", "empty_trash_confirmation": "האם באמת ברצונך לרוקן את האשפה? זה יסיר לצמיתות את כל התמונות מהאשפה של השרת.\nאין באפשרותך לבטל פעולה זו!", "enable": "אפשר", + "enable_backup": "הפעל גיבוי", "enable_biometric_auth_description": "הזן את קוד ה־PIN שלך כדי להפעיל אימות ביומטרי", "enabled": "מופעל", "end_date": "תאריך סיום", @@ -951,6 +997,7 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "הוסף תיאור...", + "exif_bottom_sheet_description_error": "שגיאה בעדכון התיאור", "exif_bottom_sheet_details": "פרטים", "exif_bottom_sheet_location": "מיקום", "exif_bottom_sheet_people": "אנשים", @@ -971,6 +1018,8 @@ "explorer": "סייר", "export": "ייצוא", "export_as_json": "ייצוא כ-JSON", + "export_database": "ייצא מסד נתונים", + "export_database_description": "ייצא מסד נתונים SQL", "extension": "סיומת", "external": "חיצוני", "external_libraries": "ספריות חיצוניות", @@ -982,6 +1031,7 @@ "failed_to_load_assets": "טעינת תמונות נכשלה", "failed_to_load_folder": "טעינת תיקיה נכשלה", "favorite": "מועדף", + "favorite_action_prompt": "{count} נוספו למועדפים", "favorite_or_unfavorite_photo": "הוסף או הסר תמונה מהמועדפים", "favorites": "מועדפים", "favorites_page_no_favorites": "לא נמצאו תמונות מועדפים", @@ -1021,6 +1071,9 @@ "haptic_feedback_switch": "אפשר משוב ברטט", "haptic_feedback_title": "משוב ברטט", "has_quota": "יש מכסה", + "hash_asset": "גיבוב תמונה", + "hashed_assets": "תמונות מגובבות", + "hashing": "מגבב", "header_settings_add_header_tip": "הוסף כותרת", "header_settings_field_validator_msg": "ערך אינו יכול להיות ריק", "header_settings_header_name_input": "שם כותרת", @@ -1053,6 +1106,7 @@ "host": "מארח", "hour": "שעה", "id": "מזהה", + "idle": "ממתין", "ignore_icloud_photos": "התעלם מתמונות iCloud", "ignore_icloud_photos_description": "תמונות שמאוחסנות ב-iCloud לא יועלו לשרת", "image": "תמונה", @@ -1110,6 +1164,7 @@ "language_no_results_title": "לא נמצאה שפה", "language_search_hint": "חפש שפות...", "language_setting_description": "בחר את השפה המועדפת עליך", + "large_files": "קבצים גדולים", "last_seen": "נראה לאחרונה", "latest_version": "גרסה עדכנית ביותר", "latitude": "קו רוחב", @@ -1125,16 +1180,18 @@ "library_page_sort_created": "תאריך יצירה", "library_page_sort_last_modified": "שונה לאחרונה", "library_page_sort_title": "כותרת אלבום", + "licenses": "רישיונות", "light": "בהיר", "like_deleted": "לייק נמחק", "link_motion_video": "קשר סרטון תנועה", - "link_options": "אפשרויות קישור", "link_to_oauth": "קישור ל-OAuth", "linked_oauth_account": "חשבון OAuth מקושר", "list": "רשימה", "loading": "טוען", "loading_search_results_failed": "טעינת תוצאות החיפוש נכשלה", + "local": "מקומי", "local_asset_cast_failed": "לא ניתן לשדר תמונה שלא הועלתה לשרת", + "local_assets": "תמונות מקומיות", "local_network": "רשת מקומית", "local_network_sheet_info": "היישום יתחבר לשרת דרך הכתובת הזאת כאשר משתמשים ברשת האינטרנט האלחוטי שמצוינת", "location_permission": "הרשאת מיקום", @@ -1148,6 +1205,7 @@ "locked_folder": "תיקיה נעולה", "log_out": "התנתק", "log_out_all_devices": "התנתק מכל המכשירים", + "logged_in_as": "מחובר כ {user}", "logged_out_all_devices": "מנותק מכל המכשירים", "logged_out_device": "מכשיר מנותק", "login": "כניסה", @@ -1190,8 +1248,7 @@ "manage_your_devices": "ניהול המכשירים המחוברים שלך", "manage_your_oauth_connection": "ניהול חיבור ה-OAuth שלך", "map": "מפה", - "map_assets_in_bound": "תמונה {count}", - "map_assets_in_bounds": "{count} תמונות", + "map_assets_in_bounds": "{count, plural, one {תמונה #} other {# תמונות}}", "map_cannot_get_user_location": "לא ניתן לקבוע את מיקום המשתמש", "map_location_dialog_yes": "כן", "map_location_picker_page_use_location": "השתמש במיקום הזה", @@ -1225,7 +1282,7 @@ "memories_setting_description": "נהל את מה שרואים בזכרונות שלך", "memories_start_over": "התחל מחדש", "memories_swipe_to_close": "החלק למעלה כדי לסגור", - "memory": "זיכרון", + "memory": "זיכרון (היום לפני..)", "memory_lane_title": "משעול הזיכרונות {title}", "menu": "תפריט", "merge": "מזג", @@ -1243,6 +1300,7 @@ "more": "עוד", "move": "העבר", "move_off_locked_folder": "הוצאה מהתיקייה הנעולה", + "move_to_lock_folder_action_prompt": "{count} נוספו לתיקייה הנעולה", "move_to_locked_folder": "העבר לתיקיה הנעולה", "move_to_locked_folder_confirmation": "התמונות והסרטונים האלו יוסרו מכל האלבומים, ויהיו מוצגים רק בתיקיה הנעולה", "moved_to_archive": "{count, plural, one {הועברה תמונה # } other {# תמונות הועברו}} לארכיון", @@ -1289,6 +1347,7 @@ "no_results": "אין תוצאות", "no_results_description": "נסה להשתמש במילה נרדפת או במילת מפתח יותר כללית", "no_shared_albums_message": "צור אלבום כדי לשתף תמונות וסרטונים עם אנשים ברשת שלך", + "no_uploads_in_progress": "אין העלאות בתהליך", "not_in_any_album": "לא בשום אלבום", "not_selected": "לא נבחרו", "note_apply_storage_label_to_previously_uploaded assets": "הערה: כדי להחיל את תווית האחסון על תמונות שהועלו בעבר, הפעל את", @@ -1326,6 +1385,7 @@ "original": "מקורי", "other": "אחר", "other_devices": "מכשירים אחרים", + "other_entities": "ישויות אחרות", "other_variables": "משתנים אחרים", "owned": "בבעלות", "owner": "בעלים", @@ -1457,6 +1517,7 @@ "purchase_server_description_2": "מעמד תומך", "purchase_server_title": "שרת", "purchase_settings_server_activated": "מפתח המוצר של השרת מנוהל על ידי מנהל המערכת", + "queue_status": "בתור {count}/{total}", "rating": "דירוג כוכב", "rating_clear": "נקה דירוג", "rating_count": "{count, plural, one {כוכב #} other {# כוכבים}}", @@ -1485,6 +1546,8 @@ "refreshing_faces": "מרענן פרצופים", "refreshing_metadata": "מרענן מטא-נתונים", "regenerating_thumbnails": "מחדש תמונות ממוזערות", + "remote": "מרוחק", + "remote_assets": "תמונות מרוחקות", "remove": "הסר", "remove_assets_album_confirmation": "האם באמת ברצונך להסיר {count, plural, one {תמונה #} other {# תמונות}} מהאלבום?", "remove_assets_shared_link_confirmation": "האם אתה בטוח שברצונך להסיר {count, plural, one {תמונה #} other {# תמונות}} מהקישור המשותף הזה?", @@ -1492,7 +1555,9 @@ "remove_custom_date_range": "הסר טווח תאריכים מותאם", "remove_deleted_assets": "הסר קבצים לא מקוונים", "remove_from_album": "הסר מאלבום", + "remove_from_album_action_prompt": "{count} הוסרו מהאלבום", "remove_from_favorites": "הסר מהמועדפים", + "remove_from_lock_folder_action_prompt": "{count} הוסרו מהתיקייה הנעולה", "remove_from_locked_folder": "הסר מהתיקייה הנעולה", "remove_from_locked_folder_confirmation": "האם אתה בטוח שברצונך להעביר את התמונות והסרטונים האלה מחוץ לתיקייה הנעולה? הם יהיו מוצגים בספרייה שלך.", "remove_from_shared_link": "הסר מקישור משותף", @@ -1520,19 +1585,25 @@ "reset_password": "איפוס סיסמה", "reset_people_visibility": "אפס את נראות האנשים", "reset_pin_code": "אפס קוד PIN", + "reset_sqlite": "אפס את מסד הנתונים SQLite", + "reset_sqlite_confirmation": "האם אתה בטוח שברצונך לאפס את מסד הנתונים SQLite? יהיה עליך להתנתק ולהתחבר מחדש כדי לסנכרן את הנתונים מחדש", + "reset_sqlite_success": "איפוס מסד הנתונים SQLite בוצע בהצלחה", "reset_to_default": "אפס לברירת מחדל", "resolve_duplicates": "פתור כפילויות", "resolved_all_duplicates": "כל הכפילויות נפתרו", "restore": "שחזר", "restore_all": "שחזר הכל", + "restore_trash_action_prompt": "{count} שוחזרו מהשאפה", "restore_user": "שחזר משתמש", "restored_asset": "התמונה שוחזרה", "resume": "המשך", "retry_upload": "נסה שוב להעלות", "review_duplicates": "בדוק כפילויות", + "review_large_files": "צפייה בקבצים גדולים", "role": "תפקיד", "role_editor": "עורך", "role_viewer": "צופה", + "running": "פועל", "save": "שמור", "save_to_gallery": "שמור לגלריה", "saved_api_key": "מפתח API שמור", @@ -1605,6 +1676,7 @@ "select_album_cover": "בחר עטיפת אלבום", "select_all": "בחר הכל", "select_all_duplicates": "בחר את כל הכפילויות", + "select_all_in": "בחר הכול בתוך {group}", "select_avatar_color": "בחר צבע תמונת פרופיל", "select_face": "בחר פנים", "select_featured_photo": "בחר תמונה מייצגת", @@ -1663,6 +1735,7 @@ "settings_saved": "ההגדרות נשמרו", "setup_pin_code": "הגדר קוד PIN", "share": "שתף", + "share_action_prompt": "שותפו {count} תמונות", "share_add_photos": "הוסף תמונות", "share_assets_selected": "{count} נבחרו", "share_dialog_preparing": "מכין...", @@ -1684,6 +1757,7 @@ "shared_link_clipboard_copied_massage": "הועתק ללוח", "shared_link_clipboard_text": "קישור: {password}\nסיסמה: {link}", "shared_link_create_error": "שגיאה ביצירת קישור משותף", + "shared_link_custom_url_description": "גש לקישור ששותף באמצעות כתובת URL מותאמת אישית", "shared_link_edit_description_hint": "הכנס את תיאור השיתוף", "shared_link_edit_expire_after_option_day": "1 יום", "shared_link_edit_expire_after_option_days": "{count} ימים", @@ -1709,6 +1783,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "ניהול קישורים משותפים", "shared_link_options": "אפשרויות קישור משותף", + "shared_link_password_description": "דרוש סיסמה כדי לגשת לקישור המשותף הזה", "shared_links": "קישורים משותפים", "shared_links_description": "שתף תמונות וסרטונים עם קישור", "shared_photos_and_videos_count": "{assetCount, plural, other {# תמונות וסרטונים משותפים.}}", @@ -1764,6 +1839,7 @@ "sort_title": "כותרת", "source": "קוד מקור", "stack": "ערימה", + "stack_action_prompt": "{count} קובצו", "stack_duplicates": "צור ערימת כפילויות", "stack_select_one_photo": "בחר תמונה ראשית אחת עבור הערימה", "stack_selected_photos": "צור ערימת תמונות נבחרות", @@ -1783,6 +1859,7 @@ "storage_quota": "מכסת האחסון", "storage_usage": "{used} בשימוש מתוך {available}", "submit": "שלח", + "success": "בוצע בהצלחה", "suggestions": "הצעות", "sunrise_on_the_beach": "Sunrise on the beach (מומלץ לחפש באנגלית לתוצאות טובות יותר)", "support": "תמיכה", @@ -1792,6 +1869,8 @@ "sync": "סנכרן", "sync_albums": "סנכרן אלבומים", "sync_albums_manual_subtitle": "סנכרן את כל הסרטונים והתמונות שהועלו לאלבומי הגיבוי שנבחרו", + "sync_local": "סנכרן מקומי", + "sync_remote": "סנכרן מרוחק", "sync_upload_album_setting_subtitle": "צור והעלה תמונות וסרטונים שלך לאלבומים שנבחרו ביישום", "tag": "תג", "tag_assets": "תיוג תמונות", @@ -1802,6 +1881,7 @@ "tag_updated": "תג מעודכן: {tag}", "tagged_assets": "תויגו {count, plural, one {תמונה #} other {# תמונות}}", "tags": "תגים", + "tap_to_run_job": "לחץ על מנת להפעיל משימה", "template": "תבנית", "theme": "ערכת נושא", "theme_selection": "בחירת ערכת נושא", @@ -1834,6 +1914,7 @@ "total": "סה\"כ", "total_usage": "שימוש כולל", "trash": "אשפה", + "trash_action_prompt": "{count} הועברו לאשפה", "trash_all": "העבר הכל לאשפה", "trash_count": "העבר לאשפה {count, number}", "trash_delete_asset": "העבר לאשפה/מחק תמונה", @@ -1851,9 +1932,11 @@ "unable_to_change_pin_code": "לא ניתן לשנות את קוד ה PIN", "unable_to_setup_pin_code": "לא ניתן להגדיר קוד PIN", "unarchive": "הוצא מארכיון", + "unarchive_action_prompt": "{count} הוסרו מהארכיון", "unarchived_count": "{count, plural, other {# הוצאו מהארכיון}}", "undo": "לבטל", "unfavorite": "לא מועדף", + "unfavorite_action_prompt": "{count} הוסרו מהמועדפים", "unhide_person": "בטל הסתרת אדם", "unknown": "לא ידוע", "unknown_country": "מדינה לא ידועה", @@ -1869,16 +1952,22 @@ "unsaved_change": "שינוי לא נשמר", "unselect_all": "בטל בחירה בהכל", "unselect_all_duplicates": "בטל בחירת כל הכפילויות", + "unselect_all_in": "בטל את הבחירה של הכל ב {group}", "unstack": "בטל ערימה", + "unstack_action_prompt": "{count} הופרדו", "unstacked_assets_count": "{count, plural, one {תמונה # הוסרה} other {# תמונות הוסרו}} מהערימה", + "untagged": "לא מתיוגים", "up_next": "הבא בתור", "updated_at": "עודכן", "updated_password": "סיסמה עודכנה", "upload": "העלאה", + "upload_action_prompt": "{count} נוספו לתור להעלאה", "upload_concurrency": "בו-זמניות של העלאה", + "upload_details": "פרטי העלאה", "upload_dialog_info": "האם ברצונך לגבות את התמונות שנבחרו לשרת?", "upload_dialog_title": "העלאת תמונה", "upload_errors": "העלאה הושלמה עם {count, plural, one {שגיאה #} other {# שגיאות}}, רענן את הדף כדי לראות תמונות שהועלו.", + "upload_finished": "העלאה הסתיימה", "upload_progress": "נותרו {remaining, number} - טופלו {processed, number}/{total, number}", "upload_skipped_duplicates": "דילג על {count, plural, one {תמונה כפולה #} other {# תמונות כפולות}}", "upload_status_duplicates": "כפילויות", @@ -1887,6 +1976,7 @@ "upload_success": "ההעלאה בוצעה בהצלחה. רענן את הדף כדי לצפות בתמונות שהועלו.", "upload_to_immich": "העלה לשרת ({count})", "uploading": "מעלה", + "uploading_media": "מעלה מדיה", "url": "URL", "usage": "שימוש", "use_biometric": "השתמש באימות ביומטרי", @@ -1907,6 +1997,7 @@ "user_usage_stats_description": "הצג סטטיסטיקות שימוש בחשבון", "username": "שם משתמש", "users": "משתמשים", + "users_added_to_album_count": "נוספו {count, plural, one {משתמש #} other {# משתמשים}} לאלבום", "utilities": "כלים", "validate": "לאמת", "validate_endpoint_error": "נא להזין כתובת תקנית", @@ -1925,6 +2016,7 @@ "view_album": "הצג אלבום", "view_all": "הצג הכל", "view_all_users": "הצג את כל המשתמשים", + "view_details": "הצג פרטים", "view_in_timeline": "ראה בציר הזמן", "view_link": "הצג קישור", "view_links": "הצג קישורים", diff --git a/i18n/hi.json b/i18n/hi.json index abb1552154..08c1933adc 100644 --- a/i18n/hi.json +++ b/i18n/hi.json @@ -22,6 +22,7 @@ "add_partner": "जोड़ीदार डालें", "add_path": "पथ डालें", "add_photos": "फ़ोटो डालें", + "add_tag": "चिह्नित करें", "add_to": "इसमें डालें…", "add_to_album": "एल्बम में डालें", "add_to_album_bottom_sheet_added": "{album} में डालें", @@ -33,6 +34,7 @@ "added_to_favorites_count": "पसंदीदा में {count, number} डाला गया", "admin": { "add_exclusion_pattern_description": "बहिष्करण पैटर्न जोड़ें. *, **, और ? का उपयोग करके ग्लोबिंग करना समर्थित है। \"Raw\" नामक किसी भी निर्देशिका की सभी फ़ाइलों को अनदेखा करने के लिए, \"**/Raw/**\" का उपयोग करें। \".tif\" से समाप्त होने वाली सभी फ़ाइलों को अनदेखा करने के लिए, \"**/*.tif\" का उपयोग करें। किसी पूर्ण पथ को अनदेखा करने के लिए, \"/path/to/ignore/**\" का उपयोग करें।", + "admin_user": "व्यवस्थापक उपयोगकर्ता", "asset_offline_description": "यह बाहरी लाइब्रेरी एसेट अब डिस्क पर मौजूद नहीं है और इसे ट्रैश में डाल दिया गया है। यदि फ़ाइल को लाइब्रेरी के भीतर कहीं ले जाया गया था, तो नई संबंधित एसेट के लिए अपनी टाइमलाइन देखें। इस एसेट को वापस पाने के लिए, कृपया सुनिश्चित करें कि नीचे दिए गए फ़ाइल पथ को इम्मिच द्वारा एक्सेस किया जा सकता है और फिर लाइब्रेरी को स्कैन करें।", "authentication_settings": "प्रमाणीकरण सेटिंग्स", "authentication_settings_description": "पासवर्ड, OAuth और अन्य प्रमाणीकरण सेटिंग्स प्रबंधित करें", @@ -43,7 +45,7 @@ "backup_database_enable_description": "Enable database dumps", "backup_keep_last_amount": "रखने के लिए पिछले डंप की मात्रा", "backup_settings": "डेटाबेस डंप सेटिंग्स", - "backup_settings_description": "डेटाबेस डंप सेटिंग्स प्रबंधित करें। ध्यान दें: इन कार्यों की निगरानी नहीं की जाती है और विफलता की स्थिति में आपको सूचित नहीं किया जाएगा।", + "backup_settings_description": "डेटाबेस डंप सेटिंग्स प्रबंधित करें।", "cleared_jobs": "{job}: के लिए कार्य साफ़ कर दिए गए", "config_set_by_file": "Config वर्तमान में एक config फ़ाइल द्वारा सेट किया गया है", "confirm_delete_library": "क्या आप वाकई {library} लाइब्रेरी को हटाना चाहते हैं?", @@ -164,12 +166,26 @@ "metadata_settings_description": "मेटाडेटा सेटिंग प्रबंधित करें", "migration_job": "प्रवास", "migration_job_description": "संपत्तियों और चेहरों के थंबनेल को नवीनतम फ़ोल्डर संरचना में माइग्रेट करें", + "nightly_tasks_cluster_faces_setting_description": "नए पहचाने गए चेहरों पर चेहरे की पहचान चलाएँ", + "nightly_tasks_cluster_new_faces_setting": "नए चेहरों को समूह में शामिल करें", + "nightly_tasks_database_cleanup_setting": "डेटाबेस क्लीनअप कार्य", + "nightly_tasks_database_cleanup_setting_description": "डेटाबेस से पुराना, समाप्त हो चुका डेटा साफ़ करें", + "nightly_tasks_generate_memories_setting": "यादें उत्पन्न करें", + "nightly_tasks_generate_memories_setting_description": "संपत्तियों से नई यादें बनाएँ", + "nightly_tasks_missing_thumbnails_setting": "गायब थंबनेल उत्पन्न करें", + "nightly_tasks_missing_thumbnails_setting_description": "थंबनेल निर्माण के लिए थंबनेल के बिना कतारबद्ध परिसंपत्तियाँ", + "nightly_tasks_settings": "रात्रिकालीन कार्य सेटिंग्स", + "nightly_tasks_settings_description": "रात्रिकालीन कार्यों का प्रबंधन करें", + "nightly_tasks_start_time_setting": "समय शुरू", + "nightly_tasks_start_time_setting_description": "वह समय जब सर्वर रात्रिकालीन कार्य चलाना शुरू करता है", + "nightly_tasks_sync_quota_usage_setting": "सिंक कोटा उपयोग", + "nightly_tasks_sync_quota_usage_setting_description": "वर्तमान उपयोग के आधार पर उपयोगकर्ता संग्रहण कोटा अपडेट करें", "no_paths_added": "कोई पथ नहीं डाला गया", "no_pattern_added": "कोई पैटर्न नहीं डाला गया", "note_apply_storage_label_previous_assets": "नोट: पहले अपलोड की गई संपत्तियों पर स्टोरेज लेबल लागू करने के लिए, चलाएँ", "note_cannot_be_changed_later": "नोट: इसे बाद में बदला नहीं जा सकता!", "notification_email_from_address": "इस पते से", - "notification_email_from_address_description": "प्रेषक का ईमेल पता, उदाहरण के लिए: \"इमिच फोटो सर्वर \"", + "notification_email_from_address_description": "प्रेषक का ईमेल पता, उदाहरण के लिए: \"इमिच फोटो सर्वर \"। यह सुनिश्चित करें कि आप उसी पते का उपयोग करें जिससे आपको ईमेल भेजने की अनुमति है।", "notification_email_host_description": "ईमेल सर्वर का होस्ट (उदा. smtp.immitch.app)", "notification_email_ignore_certificate_errors": "प्रमाणपत्र त्रुटियों पर ध्यान न दें", "notification_email_ignore_certificate_errors_description": "टीएलएस प्रमाणपत्र सत्यापन त्रुटियों पर ध्यान न दें (अनुशंसित नहीं)", @@ -193,7 +209,9 @@ "oauth_enable_description": "OAuth से लॉगिन करें", "oauth_mobile_redirect_uri": "मोबाइल रीडायरेक्ट यूआरआई", "oauth_mobile_redirect_uri_override": "मोबाइल रीडायरेक्ट यूआरआई ओवरराइड", - "oauth_mobile_redirect_uri_override_description": "सक्षम करें जब 'app.immitch:/' एक अमान्य रीडायरेक्ट यूआरआई हो।", + "oauth_mobile_redirect_uri_override_description": "जब OAuth प्रदाता किसी मोबाइल URI, जैसे ''{callback}'' की अनुमति नहीं देता, तब सक्षम करें", + "oauth_role_claim": "भूमिका का दावा", + "oauth_role_claim_description": "इस दावे की उपस्थिति के आधार पर स्वचालित रूप से व्यवस्थापक पहुँच प्रदान करें। दावे में 'उपयोगकर्ता' या 'व्यवस्थापक' हो सकता है।", "oauth_settings": "ओऑथ", "oauth_settings_description": "OAuth लॉगिन सेटिंग प्रबंधित करें", "oauth_settings_more_details": "इस सुविधा के बारे में अधिक जानकारी के लिए, देखें डॉक्स।", @@ -202,7 +220,7 @@ "oauth_storage_quota_claim": "भंडारण कोटा का दावा", "oauth_storage_quota_claim_description": "उपयोगकर्ता के संग्रहण कोटा को इस दावे के मूल्य पर स्वचालित रूप से सेट करें।", "oauth_storage_quota_default": "डिफ़ॉल्ट संग्रहण कोटा (GiB)", - "oauth_storage_quota_default_description": "GiB में कोटा का उपयोग तब किया जाएगा जब कोई दावा प्रदान नहीं किया गया हो (असीमित कोटा के लिए 0 दर्ज करें)।", + "oauth_storage_quota_default_description": "GiB में कोटा का उपयोग तब किया जाएगा जब कोई दावा प्रदान नहीं किया गया हो ।", "oauth_timeout": "ब्रेक का अनुरोध", "oauth_timeout_description": "अनुरोधों के लिए समय-सीमा मिलीसेकंड में", "password_enable_description": "ईमेल और पासवर्ड से लॉगिन करें", @@ -242,6 +260,7 @@ "storage_template_migration_info": "स्टोरेज टेम्प्लेट सभी एक्सटेंशन को लोअरकेस में बदल देगा। टेम्प्लेट में किए गए बदलाव सिर्फ़ नई संपत्तियों पर लागू होंगे। टेम्प्लेट को पहले अपलोड की गई संपत्तियों पर पूर्वव्यापी रूप से लागू करने के लिए, {job} चलाएँ।", "storage_template_migration_job": "संग्रहण टेम्पलेट माइग्रेशन कार्य", "storage_template_more_details": "इस सुविधा के बारे में अधिक जानकारी के लिए, देखें भंडारण टेम्पलेट और इसके आशय", + "storage_template_onboarding_description_v2": "सक्षम होने पर, यह सुविधा उपयोगकर्ता-निर्धारित टेम्पलेट के आधार पर फ़ाइलों को स्वतः व्यवस्थित करेगी। अधिक जानकारी के लिए, कृपया दस्तावेज़ीकरण देखें।", "storage_template_path_length": "अनुमानित पथ लंबाई सीमा: {length, number}/{limit, number}", "storage_template_settings": "भंडारण टेम्पलेट", "storage_template_settings_description": "अपलोड संपत्ति की फ़ोल्डर संरचना और फ़ाइल नाम प्रबंधित करें", @@ -354,10 +373,12 @@ "admin_password": "व्यवस्थापक पासवर्ड", "administration": "प्रशासन", "advanced": "विकसित", + "advanced_settings_beta_timeline_subtitle": "नए ऐप अनुभव को आज़माएँ", + "advanced_settings_beta_timeline_title": "बीटा टाइमलाइन", "advanced_settings_enable_alternate_media_filter_subtitle": "सिंक के दौरान वैकल्पिक मानदंडों के आधार पर मीडिया को फ़िल्टर करने के लिए इस विकल्प का उपयोग करें। इसे केवल तभी आज़माएँ जब आपको ऐप द्वारा सभी एल्बमों का पता लगाने में समस्या हो।", "advanced_settings_enable_alternate_media_filter_title": "[प्रयोगात्मक] वैकल्पिक डिवाइस एल्बम सिंक फ़िल्टर का उपयोग करें", "advanced_settings_log_level_title": "लॉग स्तर:{level}", - "advanced_settings_prefer_remote_subtitle": "कुछ डिवाइस पर मौजूद एसेट से थंबनेल लोड करने में काफ़ी समय लगता है। इसके बजाय रिमोट इमेज लोड करने के लिए इस सेटिंग को सक्रिय करें।", + "advanced_settings_prefer_remote_subtitle": "कुछ डिवाइस स्थानीय एसेट से थंबनेल लोड करने में बहुत धीमे होते हैं। इसके बजाय, दूरस्थ इमेज लोड करने के लिए इस सेटिंग को सक्रिय करें।", "advanced_settings_prefer_remote_title": "दूरस्थ छवियों को प्राथमिकता दें", "advanced_settings_proxy_headers_subtitle": "प्रत्येक नेटवर्क अनुरोध के साथ इम्मिच द्वारा भेजे जाने वाले प्रॉक्सी हेडर को परिभाषित करें", "advanced_settings_proxy_headers_title": "प्रॉक्सी हेडर", @@ -376,6 +397,7 @@ "album_cover_updated": "एल्बम कवर अपडेट किया गया", "album_delete_confirmation": "क्या आप वाकई एल्बम {album} हटाना चाहते हैं?", "album_delete_confirmation_description": "यदि यह एल्बम साझा किया गया है, तो अन्य उपयोगकर्ता इसे एक्सेस नहीं कर पाएंगे।", + "album_deleted": "एल्बम हटा दिया गया", "album_info_card_backup_album_excluded": "छोड़ा गया", "album_info_card_backup_album_included": "शामिल", "album_info_updated": "एल्बम की जानकारी अपडेट की गई", @@ -385,6 +407,7 @@ "album_options": "एल्बम विकल्प", "album_remove_user": "उपयोगकर्ता हटाएं?", "album_remove_user_confirmation": "क्या आप वाकई {user} को हटाना चाहते हैं?", + "album_search_not_found": "आपकी खोज से मेल खाता कोई एल्बम नहीं मिला", "album_share_no_users": "ऐसा लगता है कि आपने यह एल्बम सभी उपयोगकर्ताओं के साथ साझा कर दिया है या आपके पास साझा करने के लिए कोई उपयोगकर्ता नहीं है।", "album_updated": "एल्बम अपडेट किया गया", "album_updated_setting_description": "जब किसी साझा एल्बम में नई संपत्तियाँ हों तो एक ईमेल सूचना प्राप्त करें", @@ -400,7 +423,11 @@ "album_viewer_page_share_add_users": "उपयोगकर्ता जोड़ें", "album_with_link_access": "लिंक वाले किसी भी व्यक्ति को इस एल्बम में फ़ोटो और लोगों को देखने दें।", "albums": "एलबम", - "albums_count": "{गिनती, बहुवचन, एक {{count, number} एल्बम} अन्य {{count, number} एल्बम}}", + "albums_count": "{count, plural, one {{count, number} Album} other {{count, number} Albums}}", + "albums_default_sort_order": "डिफ़ॉल्ट एल्बम सॉर्ट क्रम", + "albums_default_sort_order_description": "नये एल्बम बनाते समय आरंभिक परिसंपत्ति सॉर्ट क्रम।", + "albums_feature_description": "परिसंपत्तियों का संग्रह जिसे अन्य उपयोगकर्ताओं के साथ साझा किया जा सकता है।", + "albums_on_device_count": "डिवाइस पर एल्बम ({count})", "all": "सभी", "all_albums": "सभी एलबम", "all_people": "सभी लोग", @@ -421,13 +448,14 @@ "app_settings": "एप्लिकेशन सेटिंग", "appears_in": "प्रकट होता है", "archive": "संग्रहालय", + "archive_action_prompt": "{count} को संग्रह में जोड़ा गया", "archive_or_unarchive_photo": "फ़ोटो को संग्रहीत या असंग्रहीत करें", "archive_page_no_archived_assets": "कोई संग्रहीत संपत्ति नहीं मिली", "archive_page_title": "पुरालेख ({count})", "archive_size": "पुरालेख आकार", "archive_size_description": "डाउनलोड के लिए संग्रह आकार कॉन्फ़िगर करें (GiB में)", "archived": "संग्रहित", - "archived_count": "{गणना, बहुवचन, अन्य {संग्रहीत #}}", + "archived_count": "{count, बहुवचन, अन्य {संग्रहीत #}}", "are_these_the_same_person": "क्या ये वही व्यक्ति हैं?", "are_you_sure_to_do_this": "क्या आप वास्तव में इसे करना चाहते हैं?", "asset_action_delete_err_read_only": "केवल पढ़ने योग्य परिसंपत्ति(ओं) को हटाया नहीं जा सकता, छोड़ा जा सकता है", @@ -440,50 +468,175 @@ "asset_hashing": "हैशिंग...।", "asset_list_group_by_sub_title": "द्वारा समूह बनाएं", "asset_list_layout_settings_dynamic_layout_title": "गतिशील लेआउट", + "asset_list_layout_settings_group_automatically": "स्वचालित", + "asset_list_layout_settings_group_by": "समूह परिसंपत्तियों द्वारा", + "asset_list_layout_settings_group_by_month_day": "महीना + दिन", + "asset_list_layout_sub_title": "लेआउट", + "asset_list_settings_subtitle": "फ़ोटो ग्रिड लेआउट सेटिंग्स", + "asset_list_settings_title": "चित्र की जाली", "asset_offline": "संपत्ति ऑफ़लाइन", "asset_offline_description": "यह संपत्ति ऑफ़लाइन है।", "asset_restored_successfully": "संपत्ति(याँ) सफलतापूर्वक पुनर्स्थापित की गईं", "asset_skipped": "छोड़ा गया", + "asset_skipped_in_trash": "कचरे में", "asset_uploaded": "अपलोड किए गए", - "asset_uploading": "अपलोड हो रहा है..।", + "asset_uploading": "अपलोड हो रहा है…", + "asset_viewer_settings_subtitle": "अपनी गैलरी व्यूअर सेटिंग प्रबंधित करें", + "asset_viewer_settings_title": "एसेट व्यूअर", "assets": "संपत्तियां", + "assets_added_count": "{count, plural, one {# asset} other {# assets}} जोड़ा गया", + "assets_added_to_album_count": "एल्बम में {count, plural, one {# asset} other {# assets}} जोड़ा गया", + "assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} को एल्बम में नहीं जोड़ा जा सकता", + "assets_count": "{count, बहुवचन, एक {# संपत्ति} अन्य {# संपत्ति}}", "assets_deleted_permanently": "{count} संपत्ति(याँ) स्थायी रूप से हटा दी गईं", "assets_deleted_permanently_from_server": "{count} संपत्ति(याँ) इमिच सर्वर से स्थायी रूप से हटा दी गईं", + "assets_downloaded_failed": "{count, plural, one {Downloaded # file - {error} file failed} other {Downloaded # files - {error} files failed}}", + "assets_downloaded_successfully": "{count, plural, one {Downloaded # file successfully} अन्य {Downloaded # files successfully}}", + "assets_moved_to_trash_count": "{count, plural, one {# asset} other {# assets}} को ट्रैश में ले जाया गया", + "assets_permanently_deleted_count": "स्थायी रूप से हटा दिया गया {count, plural, one {# asset} other {# assets}}", + "assets_removed_count": "{count, plural, one {# asset} other {# assets}} हटा दिया गया", "assets_removed_permanently_from_device": "{count} संपत्ति(याँ) आपके डिवाइस से स्थायी रूप से हटा दी गईं", - "assets_restore_confirmation": "क्या आप वाकई अपनी सभी नष्ट की गई संपत्तियों को पुनर्स्थापित करना चाहते हैं? आप इस क्रिया को पूर्ववत नहीं कर सकते!", + "assets_restore_confirmation": "क्या आप वाकई अपनी सभी नष्ट की गई संपत्तियों को पुनर्स्थापित करना चाहते हैं? आप इस क्रिया को पूर्ववत नहीं कर सकते।", + "assets_restored_count": "पुनर्स्थापित {count, plural, one {# asset} other {# assets}}", "assets_restored_successfully": "{count} संपत्ति(याँ) सफलतापूर्वक पुनर्स्थापित की गईं", "assets_trashed": "{count} संपत्ति(याँ) कचरे में डाली गईं", + "assets_trashed_count": "ट्रैश की गई {count, plural, one {# asset} other {# assets}}", "assets_trashed_from_server": "{count} संपत्ति(याँ) इमिच सर्वर से कचरे में डाली गईं", + "assets_were_part_of_album_count": "{count, plural, one {Asset was} other {Assets were}}एल्बम का पहले से ही हिस्सा थे", "authorized_devices": "अधिकृत उपकरण", + "automatic_endpoint_switching_subtitle": "उपलब्ध होने पर निर्दिष्ट वाई-फाई से स्थानीय रूप से कनेक्ट करें और अन्यत्र वैकल्पिक कनेक्शन का उपयोग करें", + "automatic_endpoint_switching_title": "स्वचालित URL स्विचिंग", + "autoplay_slideshow": "ऑटोप्ले स्लाइड शो", "back": "वापस", "back_close_deselect": "वापस जाएँ, बंद करें, या अचयनित करें", - "backup_controller_page_background_wifi": "Only on WiFi", + "background_location_permission": "पृष्ठभूमि स्थान अनुमति", + "background_location_permission_content": "पृष्ठभूमि में चलते समय नेटवर्क बदलने के लिए, Immich के पास *हमेशा* सटीक स्थान तक पहुंच होनी चाहिए ताकि ऐप वाई-फाई नेटवर्क का नाम पढ़ सके", + "backup": "बैकअप", + "backup_album_selection_page_albums_device": "डिवाइस पर एल्बम ({count})", + "backup_album_selection_page_albums_tap": "शामिल करने के लिए टैप करें, बाहर करने के लिए डबल टैप करें", + "backup_album_selection_page_assets_scatter": "एसेट कई एल्बमों में बिखरे हो सकते हैं। इसलिए, बैकअप प्रक्रिया के दौरान एल्बमों को शामिल या बाहर किया जा सकता है।", + "backup_album_selection_page_select_albums": "एल्बम चुनें", + "backup_album_selection_page_selection_info": "चयन जानकारी", + "backup_album_selection_page_total_assets": "कुल अद्वितीय संपत्तियाँ", + "backup_all": "सभी", + "backup_background_service_backup_failed_message": "संपत्तियों का बैकअप लेने में विफल. पुनः प्रयास किया जा रहा है…", + "backup_background_service_connection_failed_message": "सर्वर से कनेक्ट करने में विफल. पुनः प्रयास किया जा रहा है…", + "backup_background_service_current_upload_notification": "{filename} अपलोड हो रहा है", + "backup_background_service_default_notification": "नई परिसंपत्तियों की जांच की जा रही है…", + "backup_background_service_error_title": "बैकअप त्रुटि", + "backup_background_service_in_progress_notification": "अपनी परिसंपत्तियों का बैकअप लेना…", + "backup_background_service_upload_failure_notification": "{filename} अपलोड करने में विफल", + "backup_controller_page_albums": "बैकअप एल्बम", + "backup_controller_page_background_app_refresh_disabled_content": "बैकग्राउंड बैकअप का उपयोग करने के लिए सेटिंग्स > सामान्य > बैकग्राउंड ऐप रिफ्रेश में बैकग्राउंड ऐप रिफ्रेश सक्षम करें।", + "backup_controller_page_background_app_refresh_disabled_title": "पृष्ठभूमि ऐप रीफ़्रेश अक्षम", + "backup_controller_page_background_app_refresh_enable_button_text": "सेटिंग्स पर जाएँ", + "backup_controller_page_background_battery_info_link": "कैसे मुझे दिखाओ", + "backup_controller_page_background_battery_info_message": "सर्वोत्तम बैकग्राउंड बैकअप अनुभव के लिए, कृपया Immich के लिए बैकग्राउंड गतिविधि को प्रतिबंधित करने वाले किसी भी बैटरी ऑप्टिमाइज़ेशन को अक्षम करें।\n\nचूँकि यह डिवाइस-विशिष्ट है, इसलिए कृपया अपने डिवाइस निर्माता से आवश्यक जानकारी देखें।", + "backup_controller_page_background_battery_info_ok": "ठीक", + "backup_controller_page_background_battery_info_title": "बैटरी अनुकूलन", + "backup_controller_page_background_charging": "केवल चार्ज करते समय", + "backup_controller_page_background_configure_error": "पृष्ठभूमि सेवा कॉन्फ़िगर करने में विफल", + "backup_controller_page_background_delay": "नई संपत्ति का बैकअप विलंबित करें: {duration}", + "backup_controller_page_background_description": "ऐप खोले बिना किसी भी नई संपत्ति का स्वचालित रूप से बैकअप लेने के लिए पृष्ठभूमि सेवा चालू करें", + "backup_controller_page_background_is_off": "स्वचालित पृष्ठभूमि बैकअप बंद है", + "backup_controller_page_background_is_on": "स्वचालित पृष्ठभूमि बैकअप चालू है", + "backup_controller_page_background_turn_off": "पृष्ठभूमि सेवा बंद करें", + "backup_controller_page_background_turn_on": "पृष्ठभूमि सेवा चालू करें", + "backup_controller_page_background_wifi": "केवल वाई-फ़ाई पर", + "backup_controller_page_backup": "बैकअप", + "backup_controller_page_backup_selected": "चयनित: ", + "backup_controller_page_backup_sub": "बैकअप किए गए फ़ोटो और वीडियो", + "backup_controller_page_created": "निर्मित तिथि: {date}", + "backup_controller_page_desc_backup": "ऐप खोलते समय सर्वर पर नई संपत्तियों को स्वचालित रूप से अपलोड करने के लिए अग्रभूमि बैकअप चालू करें।", + "backup_controller_page_excluded": "छोड़ा गया: ", + "backup_controller_page_failed": "विफल ({count})", + "backup_controller_page_filename": "फ़ाइल का नाम: {{filename} [{size}]", + "backup_controller_page_id": "आईडी: {id}", + "backup_controller_page_info": "बैकअप जानकारी", + "backup_controller_page_none_selected": "कोई भी चयनित नहीं", + "backup_controller_page_remainder": "शेष", + "backup_controller_page_remainder_sub": "चयन से बैकअप लेने के लिए शेष फ़ोटो और वीडियो", + "backup_controller_page_server_storage": "सर्वर संग्रहण", + "backup_controller_page_start_backup": "बैकअप प्रारंभ करें", + "backup_controller_page_status_off": "स्वचालित अग्रभूमि बैकअप बंद है", + "backup_controller_page_status_on": "स्वचालित अग्रभूमि बैकअप चालू है", + "backup_controller_page_storage_format": "{कुल} में से {प्रयुक्त} प्रयुक्त", + "backup_controller_page_to_backup": "बैकअप किए जाने वाले एल्बम", + "backup_controller_page_total_sub": "चयनित एल्बमों से सभी अद्वितीय फ़ोटो और वीडियो", + "backup_controller_page_turn_off": "अग्रभूमि बैकअप बंद करें", + "backup_controller_page_turn_on": "अग्रभूमि बैकअप चालू करें", + "backup_controller_page_uploading_file_info": "फ़ाइल जानकारी अपलोड करना", + "backup_err_only_album": "एकमात्र एल्बम नहीं हटाया जा सकता", + "backup_info_card_assets": "संपत्ति", + "backup_manual_cancelled": "रद्द", + "backup_manual_in_progress": "अपलोड पहले से ही प्रगति पर है। कुछ देर बाद प्रयास करें", + "backup_manual_success": "सफलता", + "backup_manual_title": "अपलोड स्थिति", + "backup_options_page_title": "बैकअप विकल्प", + "backup_setting_subtitle": "पृष्ठभूमि और अग्रभूमि अपलोड सेटिंग प्रबंधित करें", "backward": "पिछला", + "beta_sync": "बीटा सिंक स्थिति", + "beta_sync_subtitle": "नए सिंक सिस्टम का प्रबंधन करें", + "biometric_auth_enabled": "बायोमेट्रिक प्रमाणीकरण सक्षम", + "biometric_locked_out": "आप बायोमेट्रिक प्रमाणीकरण से बाहर हैं", + "biometric_no_options": "कोई बायोमेट्रिक विकल्प उपलब्ध नहीं है", + "biometric_not_available": "इस डिवाइस पर बायोमेट्रिक प्रमाणीकरण उपलब्ध नहीं है", "birthdate_saved": "जन्मतिथि सफलतापूर्वक सहेजी गई", "birthdate_set_description": "जन्मतिथि का उपयोग फोटो के समय इस व्यक्ति की आयु की गणना करने के लिए किया जाता है।", "blurred_background": "धुंधली पृष्ठभूमि", + "bugs_and_feature_requests": "बग और सुविधा अनुरोध", "build": "निर्माण", "build_image": "छवि बनाएँ", + "bulk_delete_duplicates_confirmation": "क्या आप वाकई {count, plural, one {# duplicate asset} other {# duplicate assets}} को बल्क में हटाना चाहते हैं? इससे हर ग्रुप की सबसे बड़ी संपत्ति बनी रहेगी और बाकी सभी डुप्लिकेट हमेशा के लिए हट जाएँगे। आप इस क्रिया को पूर्ववत नहीं कर सकते!", + "bulk_keep_duplicates_confirmation": "क्या आप वाकई {count, plural, one {# duplicate asset} other {# duplicate assets}} रखना चाहते हैं? इससे बिना कुछ हटाए सभी डुप्लिकेट ग्रुप हल हो जाएँगे।", + "bulk_trash_duplicates_confirmation": "क्या आप वाकई {count, plural, one {# duplicate asset} other {# duplicate assets}} को बल्क ट्रैश करना चाहते हैं? इससे हर ग्रुप की सबसे बड़ी एसेट रहेगी और बाकी सभी डुप्लिकेट ट्रैश हो जाएँगे।", "buy": "इम्मीच खरीदो", + "cache_settings_clear_cache_button": "कैश को साफ़ करें", + "cache_settings_clear_cache_button_title": "ऐप का कैश साफ़ करता है। कैश के दोबारा बनने तक, यह ऐप के प्रदर्शन पर काफ़ी असर डालेगा।", + "cache_settings_duplicated_assets_clear_button": "स्पष्ट", + "cache_settings_duplicated_assets_subtitle": "ऐप द्वारा अनदेखा की गई तस्वीरें और वीडियो", + "cache_settings_duplicated_assets_title": "डुप्लिकेट संपत्तियां ({count})", + "cache_settings_statistics_album": "लाइब्रेरी थंबनेल", + "cache_settings_statistics_full": "पूर्ण चित्र", + "cache_settings_statistics_shared": "साझा किए गए एल्बम थंबनेल", + "cache_settings_statistics_thumbnail": "थंबनेल", + "cache_settings_statistics_title": "कैश उपयोग", + "cache_settings_subtitle": "Immich मोबाइल एप्लिकेशन के कैशिंग व्यवहार को नियंत्रित करें", "cache_settings_tile_subtitle": "स्थानीय संग्रहण के व्यवहार को नियंत्रित करें", "cache_settings_tile_title": "स्थानीय संग्रहण", + "cache_settings_title": "कैशिंग सेटिंग्स", "camera": "कैमरा", "camera_brand": "कैमरा ब्रांड", "camera_model": "कैमरा मॉडल", "cancel": "रद्द करना", "cancel_search": "खोज रद्द करें", + "canceled": "रद्द करना", + "canceling": "रद्द कर रहा है", "cannot_merge_people": "लोगों का विलय नहीं हो सकता", "cannot_undo_this_action": "आप इस क्रिया को पूर्ववत नहीं कर सकते!", "cannot_update_the_description": "विवरण अद्यतन नहीं किया जा सकता", + "cast": "ढालना", + "cast_description": "उपलब्ध कास्ट गंतव्यों को कॉन्फ़िगर करें", "change_date": "बदलाव दिनांक", + "change_description": "विवरण बदलें", + "change_display_order": "प्रदर्शन क्रम बदलें", "change_expiration_time": "समाप्ति समय बदलें", "change_location": "स्थान बदलें", "change_name": "नाम परिवर्तन करें", - "change_name_successfully": "नाम सफलतापूर्वक बदलें", + "change_name_successfully": "नाम सफलतापूर्वक बदला गया", "change_password": "पासवर्ड बदलें", "change_password_description": "यह या तो पहली बार है जब आप सिस्टम में साइन इन कर रहे हैं या आपका पासवर्ड बदलने का अनुरोध किया गया है।", + "change_password_form_confirm_password": "पासवर्ड की पुष्टि कीजिये", + "change_password_form_description": "नमस्ते {name},\n\nया तो आप पहली बार सिस्टम में साइन इन कर रहे हैं या फिर आपका पासवर्ड बदलने का अनुरोध किया गया है। कृपया नीचे नया पासवर्ड डालें।", + "change_password_form_new_password": "नया पासवर्ड", + "change_password_form_password_mismatch": "सांकेतिक शब्द मेल नहीं खाते", + "change_password_form_reenter_new_password": "नया पासवर्ड पुनः दर्ज करें", + "change_pin_code": "पिन कोड बदलें", "change_your_password": "अपना पासवर्ड बदलें", "changed_visibility_successfully": "दृश्यता सफलतापूर्वक परिवर्तित", + "check_corrupt_asset_backup": "दूषित परिसंपत्ति बैकअप की जाँच करें", + "check_corrupt_asset_backup_button": "जाँच करें", + "check_corrupt_asset_backup_description": "यह जाँच केवल वाई-फ़ाई पर ही करें और सभी संपत्तियों का बैकअप लेने के बाद ही करें। इस प्रक्रिया में कुछ मिनट लग सकते हैं।", "check_logs": "लॉग जांचें", "choose_matching_people_to_merge": "मर्ज करने के लिए मिलते-जुलते लोगों को चुनें", "city": "शहर", @@ -492,21 +645,49 @@ "clear_all_recent_searches": "सभी हालिया खोजें साफ़ करें", "clear_message": "स्पष्ट संदेश", "clear_value": "स्पष्ट मूल्य", + "client_cert_dialog_msg_confirm": "ठीक", + "client_cert_enter_password": "पास वर्ड दर्ज करें", + "client_cert_import": "आयात", + "client_cert_import_success_msg": "क्लाइंट प्रमाणपत्र आयात किया गया है", + "client_cert_invalid_msg": "अमान्य प्रमाणपत्र फ़ाइल या गलत पासवर्ड", + "client_cert_remove_msg": "क्लाइंट प्रमाणपत्र हटा दिया गया है", + "client_cert_subtitle": "केवल PKCS12 (.p12, .pfx) फ़ॉर्मैट का समर्थन करता है। प्रमाणपत्र आयात/हटाएँ केवल लॉगिन से पहले उपलब्ध हैं", + "client_cert_title": "SSL क्लाइंट प्रमाणपत्र", + "clockwise": "दक्षिणावर्त", "close": "बंद", "collapse": "गिर जाना", "collapse_all": "सभी को संकुचित करें", + "color": "रंग", "color_theme": "रंग थीम", "comment_deleted": "टिप्पणी हटा दी गई", "comment_options": "टिप्पणी विकल्प", "comments_and_likes": "टिप्पणियाँ और पसंद", "comments_are_disabled": "टिप्पणियाँ अक्षम हैं", + "common_create_new_album": "नया एल्बम बनाएँ", + "common_server_error": "कृपया अपने नेटवर्क कनेक्शन की जांच करें, सुनिश्चित करें कि सर्वर पहुंच योग्य है और ऐप/सर्वर संस्करण संगत हैं।", + "completed": "पुरा होना", "confirm": "पुष्टि", "confirm_admin_password": "एडमिन पासवर्ड की पुष्टि करें", + "confirm_delete_face": "क्या आप वाकई एसेट से {name} चेहरा हटाना चाहते हैं?", "confirm_delete_shared_link": "क्या आप वाकई इस साझा लिंक को हटाना चाहते हैं?", + "confirm_keep_this_delete_others": "इस एसेट को छोड़कर, स्टैक की सभी अन्य एसेट हटा दी जाएँगी। क्या आप वाकई जारी रखना चाहते हैं?", + "confirm_new_pin_code": "नए पिन कोड की पुष्टि करें", "confirm_password": "पासवर्ड की पुष्टि कीजिये", + "confirm_tag_face": "क्या आप इस चेहरे को {name} के रूप में टैग करना चाहते हैं?", + "confirm_tag_face_unnamed": "क्या आप इस चेहरे को टैग करना चाहते हैं?", + "connected_device": "कनेक्टेड डिवाइस", + "connected_to": "से जुड़ा", "contain": "समाहित", "context": "संदर्भ", "continue": "जारी", + "control_bottom_app_bar_create_new_album": "नया एल्बम बनाएँ", + "control_bottom_app_bar_delete_from_immich": "Immich से हटाएं", + "control_bottom_app_bar_delete_from_local": "डिवाइस से हटाएं", + "control_bottom_app_bar_edit_location": "स्थान संपादित करें", + "control_bottom_app_bar_edit_time": "तारीख और समय संपादित करें", + "control_bottom_app_bar_share_link": "लिंक शेयर करें", + "control_bottom_app_bar_share_to": "साझा करें", + "control_bottom_app_bar_trash_from_immich": "ट्रैश में ले जाएं", "copied_image_to_clipboard": "छवि को क्लिपबोर्ड पर कॉपी किया गया।", "copied_to_clipboard": "क्लिपबोर्ड पर नकल!", "copy_error": "प्रतिलिपि त्रुटि", @@ -521,6 +702,7 @@ "covers": "आवरण", "create": "तैयार करें", "create_album": "एल्बम बनाओ", + "create_album_page_untitled": "शीर्षकहीन", "create_library": "लाइब्रेरी बनाएं", "create_link": "लिंक बनाएं", "create_link_to_share": "शेयर करने के लिए लिंक बनाएं", @@ -529,39 +711,75 @@ "create_new_person": "नया व्यक्ति बनाएं", "create_new_person_hint": "चयनित संपत्तियों को एक नए व्यक्ति को सौंपें", "create_new_user": "नया उपयोगकर्ता बनाएं", + "create_shared_album_page_share_add_assets": "संपत्ति जोड़ें", + "create_shared_album_page_share_select_photos": "फ़ोटो चुनें", + "create_tag": "टैग बनाएँ", + "create_tag_description": "एक नया टैग बनाएँ। नेस्टेड टैग के लिए, कृपया फ़ॉरवर्ड स्लैश सहित टैग का पूरा पथ दर्ज करें।", "create_user": "उपयोगकर्ता बनाइये", "created": "बनाया", + "created_at": "बनाया था", "crop": "छाँटें", + "curated_object_page_title": "चीज़ें", "current_device": "वर्तमान उपकरण", + "current_pin_code": "वर्तमान पिन कोड", + "current_server_address": "वर्तमान सर्वर पता", "custom_locale": "कस्टम लोकेल", "custom_locale_description": "भाषा और क्षेत्र के आधार पर दिनांक और संख्याएँ प्रारूपित करें", + "daily_title_text_date": "ई, एमएमएम डीडी", + "daily_title_text_date_year": "ई, एमएमएम दिन, वर्ष", "dark": "डार्क", + "dark_theme": "डार्क थीम टॉगल करें", "date_after": "इसके बाद की तारीख", "date_and_time": "तिथि और समय", "date_before": "पहले की तारीख", + "date_format": "ई, एलएलएल डी, वाई • एच:एमएम ए", "date_of_birth_saved": "जन्मतिथि सफलतापूर्वक सहेजी गई", "date_range": "तिथि सीमा", "day": "दिन", "deduplicate_all": "सभी को डुप्लिकेट करें", + "deduplication_criteria_1": "छवि का आकार बाइट्स में", + "deduplication_criteria_2": "EXIF डेटा की संख्या", + "deduplication_info": "डुप्लीकेशन हटाने की जानकारी", + "deduplication_info_description": "परिसंपत्तियों का स्वचालित रूप से पूर्व-चयन करने और डुप्लिकेट को थोक में हटाने के लिए, हम निम्न पर ध्यान देते हैं:", "default_locale": "डिफ़ॉल्ट स्थान", "default_locale_description": "अपने ब्राउज़र स्थान के आधार पर दिनांक और संख्याएँ प्रारूपित करें", "delete": "हटाएँ", + "delete_action_prompt": "{count} स्थायी रूप से हटा दिया गया", "delete_album": "एल्बम हटाएँ", "delete_api_key_prompt": "क्या आप वाकई इस एपीआई कुंजी को हटाना चाहते हैं?", + "delete_dialog_alert": "ये आइटम Immich और आपके डिवाइस से स्थायी रूप से हटा दिए जाएंगे", + "delete_dialog_alert_local": "ये आइटम आपके डिवाइस से स्थायी रूप से हटा दिए जाएंगे, लेकिन फिर भी Immich सर्वर पर उपलब्ध रहेंगे", + "delete_dialog_alert_local_non_backed_up": "कुछ आइटम का Immich में बैकअप नहीं लिया गया है और उन्हें आपके डिवाइस से स्थायी रूप से हटा दिया जाएगा", + "delete_dialog_alert_remote": "ये आइटम Immich सर्वर से स्थायी रूप से हटा दिए जाएंगे", + "delete_dialog_ok_force": "फिर भी हटाएं", + "delete_dialog_title": "स्थायी रूप से हटाएँ", "delete_duplicates_confirmation": "क्या आप वाकई इन डुप्लिकेट को स्थायी रूप से हटाना चाहते हैं?", + "delete_face": "चेहरा हटाएं", "delete_key": "कुंजी हटाएँ", "delete_library": "लाइब्रेरी हटाएँ", "delete_link": "लिंक हटाएँ", + "delete_local_action_prompt": "{count} स्थानीय रूप से हटा दिया गया", + "delete_local_dialog_ok_backed_up_only": "केवल बैकअप हटाएं", + "delete_local_dialog_ok_force": "फिर भी हटाएं", + "delete_others": "अन्य को हटाएँ", "delete_shared_link": "साझा किए गए लिंक को हटाएं", "delete_shared_link_dialog_title": "साझा किए गए लिंक को हटाएं", + "delete_tag": "टैग हटाएं", + "delete_tag_confirmation_prompt": "क्या आप वाकई {tagName} टैग हटाना चाहते हैं?", "delete_user": "उपभोक्ता मिटायें", "deleted_shared_link": "साझा किया गया लिंक हटा दिया गया", + "deletes_missing_assets": "डिस्क से गायब संपत्तियों को हटाता है", "description": "वर्णन", + "description_input_hint_text": "विवरण जोड़ें..।", + "description_input_submit_error": "विवरण अपडेट करते समय त्रुटि हुई, अधिक जानकारी के लिए लॉग देखें", + "deselect_all": "सबको अचयनित करो", "details": "विवरण", "direction": "दिशा", "disabled": "अक्षम", "disallow_edits": "संपादनों की अनुमति न दें", + "discord": "डिसकॉर्ड", "discover": "खोजें", + "discovered_devices": "खोजे गए उपकरण", "dismiss_all_errors": "सभी त्रुटियाँ ख़ारिज करें", "dismiss_error": "त्रुटि ख़ारिज करें", "display_options": "प्रदर्शन चुनाव", @@ -569,14 +787,18 @@ "display_original_photos": "मूल फ़ोटो प्रदर्शित करें", "display_original_photos_setting_description": "किसी संपत्ति को देखते समय थंबनेल के बजाय मूल तस्वीर प्रदर्शित करना पसंद करें जब मूल संपत्ति वेब-संगत हो।", "do_not_show_again": "इस संदेश को दुबारा मत दिखाना", + "documentation": "प्रलेखन", "done": "ठीक है", "download": "डाउनलोड करें", + "download_action_prompt": "{count} संपत्तियां डाउनलोड हो रही हैं", "download_canceled": "डाउनलोड रद्द कर दिया गया", "download_complete": "डाउनलोड पूरा", "download_enqueue": "डाउनलोड कतार में है", "download_error": "डाउनलोड त्रुटि", "download_failed": "डाउनलोड विफल", "download_finished": "डाउनलोड समाप्त", + "download_include_embedded_motion_videos": "एम्बेडेड वीडियो", + "download_include_embedded_motion_videos_description": "मोशन फ़ोटो में एम्बेड किए गए वीडियो को एक अलग फ़ाइल के रूप में शामिल करें", "download_notfound": "डाउनलोड नहीं मिला", "download_paused": "डाउनलोड स्थगित", "download_settings": "डाउनलोड करना", @@ -586,6 +808,7 @@ "download_sucess_android": "मीडिया DCIM/Immich में डाउनलोड हो गया है", "download_waiting_to_retry": "पुनः प्रयास करने का इंतजार कर रहा है", "downloading": "डाउनलोड", + "downloading_asset_filename": "संपत्ति {filename} डाउनलोड हो रही है", "downloading_media": "मीडिया डाउनलोड हो रहा है", "drop_files_to_upload": "अपलोड करने के लिए फ़ाइलें कहीं भी छोड़ें", "duplicates": "डुप्लिकेट", @@ -596,6 +819,7 @@ "edit_avatar": "अवतार को एडिट करें", "edit_date": "संपादन की तारीख", "edit_date_and_time": "दिनांक और समय संपादित करें", + "edit_description": "संपादित करें वर्णन", "edit_exclusion_pattern": "बहिष्करण पैटर्न संपादित करें", "edit_faces": "चेहरे संपादित करें", "edit_import_path": "आयात पथ संपादित करें", @@ -814,7 +1038,6 @@ "library_options": "पुस्तकालय विकल्प", "light": "रोशनी", "like_deleted": "जैसे हटा दिया गया", - "link_options": "लिंक विकल्प", "link_to_oauth": "OAuth से लिंक करें", "linked_oauth_account": "लिंक किया गया OAuth खाता", "list": "सूची", diff --git a/i18n/hr.json b/i18n/hr.json index d2edfc9a8e..fd17e12dc2 100644 --- a/i18n/hr.json +++ b/i18n/hr.json @@ -34,6 +34,7 @@ "added_to_favorites_count": "Dodano {count, number} u omiljeno", "admin": { "add_exclusion_pattern_description": "Dodajte uzorke izuzimanja. Globiranje pomoću *, ** i ? je podržano. Za ignoriranje svih datoteka u bilo kojem direktoriju pod nazivom \"Raw\", koristite \"**/Raw/**\". Da biste zanemarili sve datoteke koje završavaju na \".tif\", koristite \"**/*.tif\". Da biste zanemarili apsolutni put, koristite \"/path/to/ignore/**\".", + "admin_user": "Administrator", "asset_offline_description": "Ovo sredstvo vanjske knjižnice više nije pronađeno na disku i premješteno je u smeće. Ako je datoteka premještena unutar biblioteke, provjerite svoju vremensku traku za novo odgovarajuće sredstvo. Da biste vratili ovo sredstvo, provjerite može li Immich pristupiti donjoj stazi datoteke i skenirajte biblioteku.", "authentication_settings": "Postavke autentikacije", "authentication_settings_description": "Uredi lozinku, OAuth, i druge postavke autentikacije", @@ -165,6 +166,20 @@ "metadata_settings_description": "Upravljanje postavkama metapodataka", "migration_job": "Migracija", "migration_job_description": "Premjestite minijature za sredstva i lica u najnoviju strukturu mapa", + "nightly_tasks_cluster_faces_setting_description": "Pokreni prepoznavanje lica na novootkrivenim licima", + "nightly_tasks_cluster_new_faces_setting": "Grupiraj nova lica", + "nightly_tasks_database_cleanup_setting": "Zadaci čišćenja baze podataka", + "nightly_tasks_database_cleanup_setting_description": "Očisti stare, istekle podatke iz baze podataka", + "nightly_tasks_generate_memories_setting": "Generiraj uspomene", + "nightly_tasks_generate_memories_setting_description": "Stvori nove uspomene iz sadržaja", + "nightly_tasks_missing_thumbnails_setting": "Generiraj nedostajuće sličice", + "nightly_tasks_missing_thumbnails_setting_description": "Stavi u red čekanja sadržaje bez sličica za generiranje sličica", + "nightly_tasks_settings": "Postavke noćnih zadataka", + "nightly_tasks_settings_description": "Upravljanje noćnim zadacima", + "nightly_tasks_start_time_setting": "Vrijeme početka", + "nightly_tasks_start_time_setting_description": "Vrijeme pokretanja noćnih zadataka na poslužitelju", + "nightly_tasks_sync_quota_usage_setting": "Sinkroniziraj iskorištenost kvote", + "nightly_tasks_sync_quota_usage_setting_description": "Ažuriraj korisničku kvotu za pohranu na temelju trenutne potrošnje", "no_paths_added": "Nema dodanih putanja", "no_pattern_added": "Nije dodan uzorak", "note_apply_storage_label_previous_assets": "Napomena: da biste primijenili Oznaku Pohrane na prethodno prenesena sredstva, pokrenite", @@ -195,6 +210,8 @@ "oauth_mobile_redirect_uri": "Mobilnog Preusmjeravanja URI", "oauth_mobile_redirect_uri_override": "Nadjačavanje URI-preusmjeravanja za mobilne uređaje", "oauth_mobile_redirect_uri_override_description": "Omogući kada pružatelj OAuth ne dopušta mobilni URI, poput ''{callback}''", + "oauth_role_claim": "Dodjela uloge", + "oauth_role_claim_description": "Automatski dodijeli administratorski pristup na temelju prisutnosti ove tvrdnje. Tvrdnja može sadržavati ili 'user' ili 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Upravljanje postavkama za prijavu kroz OAuth", "oauth_settings_more_details": "Za više pojedinosti o ovoj značajci pogledajte uputstva.", @@ -203,7 +220,7 @@ "oauth_storage_quota_claim": "Zahtjev za kvotom pohrane", "oauth_storage_quota_claim_description": "Automatski postavite korisničku kvotu pohrane na vrijednost ovog zahtjeva.", "oauth_storage_quota_default": "Zadana kvota pohrane (GiB)", - "oauth_storage_quota_default_description": "Kvota u GiB koja će se koristiti kada nema zahtjeva (unesite 0 za neograničenu kvotu).", + "oauth_storage_quota_default_description": "Kvota u GiB koja će se koristiti kada nema zahtjeva", "oauth_timeout": "Istek vremena zahtjeva", "oauth_timeout_description": "Istek vremena zahtjeva je u milisekundama", "password_enable_description": "Prijava s email adresom i zaporkom", @@ -243,6 +260,7 @@ "storage_template_migration_info": "Predložak za pohranu će sve nastavke (ekstenzije) pretvoriti u mala slova. Promjene predloška primjenjivat će se samo na nova sredstva. Za retroaktivnu primjenu predloška na prethodno prenesena sredstva, pokrenite {job}.", "storage_template_migration_job": "Posao Migracije Predloška Pohrane", "storage_template_more_details": "Za više pojedinosti o ovoj značajci pogledajte Predložak pohrane i njegove implikacije", + "storage_template_onboarding_description_v2": "Kada je omogućena, ova će značajka automatski organizira datoteke prema predlošku koji je definirao korisnik. Za više informacija pogledajte dokumentaciju.", "storage_template_path_length": "Približno ograničenje duljine putanje: {length, number}/{limit, number}", "storage_template_settings": "Predložak pohrane", "storage_template_settings_description": "Upravljajte strukturom mape i nazivom datoteke učitanog sredstva", @@ -355,10 +373,12 @@ "admin_password": "Admin lozinka", "administration": "Administracija", "advanced": "Napredno", + "advanced_settings_beta_timeline_subtitle": "Isprobaj novo iskustvo aplikacije", + "advanced_settings_beta_timeline_title": "Beta vremenska crta", "advanced_settings_enable_alternate_media_filter_subtitle": "Koristite ovu opciju za filtriranje medija tijekom sinkronizacije na temelju alternativnih kriterija. Pokušajte ovo samo ako imate problema s aplikacijom koja ne prepoznaje sve albume.", "advanced_settings_enable_alternate_media_filter_title": "[EKSPERIMENTALNO] Koristite alternativni filter za sinkronizaciju albuma na uređaju", "advanced_settings_log_level_title": "Razina zapisivanja: {level}", - "advanced_settings_prefer_remote_subtitle": "Neki uređaji sporo učitavaju sličice s resursa na uređaju. Aktivirajte ovu postavku kako biste umjesto toga učitali slike s udaljenih izvora.", + "advanced_settings_prefer_remote_subtitle": "Neki uređaji sporo učitavaju sličice s lokalnih resursa. Aktivirajte ovu postavku kako biste umjesto toga učitali slike s udaljenih izvora.", "advanced_settings_prefer_remote_title": "Preferiraj udaljene slike", "advanced_settings_proxy_headers_subtitle": "Definirajte zaglavlja posrednika koja Immich treba slati sa svakim mrežnim zahtjevom.", "advanced_settings_proxy_headers_title": "Proxy zaglavlja", @@ -377,6 +397,7 @@ "album_cover_updated": "Naslovnica albuma ažurirana", "album_delete_confirmation": "Jeste li sigurni da želite izbrisati album {album}?", "album_delete_confirmation_description": "Ako se ovaj album dijeli, drugi korisnici mu više neće moći pristupiti.", + "album_deleted": "Album izbrisan", "album_info_card_backup_album_excluded": "IZUZETO", "album_info_card_backup_album_included": "UKLJUČENO", "album_info_updated": "Podaci o albumu ažurirani", @@ -386,6 +407,7 @@ "album_options": "Opcije albuma", "album_remove_user": "Ukloni korisnika?", "album_remove_user_confirmation": "Jeste li sigurni da želite ukloniti {user}?", + "album_search_not_found": "Nema albuma koji odgovaraju vašem pretraživanju", "album_share_no_users": "Čini se da ste podijelili ovaj album sa svim korisnicima ili nemate nijednog korisnika s kojim biste ga dijelili.", "album_updated": "Album ažuriran", "album_updated_setting_description": "Primite obavijest e-poštom kada dijeljeni album ima nova sredstva", @@ -405,6 +427,7 @@ "albums_default_sort_order": "Zadani redoslijed sortiranja albuma", "albums_default_sort_order_description": "Početni redoslijed sortiranja elemenata prilikom izrade novih albuma.", "albums_feature_description": "Zbirke resursa koje se mogu dijeliti s drugim korisnicima.", + "albums_on_device_count": "Albumi na uređaju ({count})", "all": "Sve", "all_albums": "Svi albumi", "all_people": "Svi ljudi", @@ -425,6 +448,7 @@ "app_settings": "Postavke Aplikacije", "appears_in": "Pojavljuje se u", "archive": "Arhiva", + "archive_action_prompt": "{count} dodano u arhivu", "archive_or_unarchive_photo": "Arhivirajte ili dearhivirajte fotografiju", "archive_page_no_archived_assets": "Nema arhiviranih resursa", "archive_page_title": "Arhiviraj ({count})", @@ -462,11 +486,12 @@ "assets": "Sredstva", "assets_added_count": "Dodano {count, plural, one {# asset} other {# assets}}", "assets_added_to_album_count": "Dodano {count, plural, one {# asset} other {# assets}} u album", - "assets_added_to_name_count": "Dodano {count, plural, one {# asset} other {# assets}} u {hasName, select, true {{name}} other {new album}}", - "assets_cannot_be_added_to_album_count": "{count, plural,\n one {Nije moguće dodati medij u album}\n few {Nije moguće dodati # medija u album}\n other {Nije moguće dodati # medija u album}\n}", + "assets_cannot_be_added_to_album_count": "{count, plural, one {Sadržaj se ne može dodati u album} other {{count} sadržaja se ne mogu dodati u album}}", "assets_count": "{count, plural, one {# asset} other {# assets}}", "assets_deleted_permanently": "{count} resurs(i) uspješno uklonjeni", "assets_deleted_permanently_from_server": "{count} resurs(i) trajno obrisan(i) sa Immich poslužitelja", + "assets_downloaded_failed": "{count, plural, one {Preuzeta # datoteka – {error} datoteka nije uspjela} other {Preuzeto je # datoteka – {error} datoteke nisu uspjele}}", + "assets_downloaded_successfully": "{count, plural, one {Uspješno preuzeta # datoteka} other {Uspješno preuzete # datoteke}}", "assets_moved_to_trash_count": "{count, plural, one {# asset} other {# asset}} premješteno u smeće", "assets_permanently_deleted_count": "Trajno izbrisano {count, plural, one {# asset} other {# assets}}", "assets_removed_count": "Uklonjeno {count, plural, one {# asset} other {# assets}}", @@ -481,10 +506,12 @@ "authorized_devices": "Ovlašteni Uređaji", "automatic_endpoint_switching_subtitle": "Povežite se lokalno preko naznačene Wi-Fi mreže kada je dostupna i koristite alternativne veze na drugim lokacijama", "automatic_endpoint_switching_title": "Automatsko prebacivanje URL-a", + "autoplay_slideshow": "Automatsko prikazivanje slajdova", "back": "Nazad", "back_close_deselect": "Natrag, zatvorite ili poništite odabir", "background_location_permission": "Dozvola za lokaciju u pozadini", "background_location_permission_content": "Kako bi prebacivao mreže dok radi u pozadini, Immich mora *uvijek* imati pristup preciznoj lokaciji kako bi aplikacija mogla pročitati naziv Wi-Fi mreže", + "backup": "Sigurnosna kopija", "backup_album_selection_page_albums_device": "Albumi na uređaju ({count})", "backup_album_selection_page_albums_tap": "Dodirnite za uključivanje, dvostruki dodir za isključivanje", "backup_album_selection_page_assets_scatter": "Resursi mogu biti raspoređeni u više albuma. Stoga, albumi mogu biti uključeni ili isključeni tijekom procesa sigurnosnog kopiranja.", @@ -548,6 +575,8 @@ "backup_options_page_title": "Opcije sigurnosnog kopiranja", "backup_setting_subtitle": "Upravljajte postavkama učitavanja u pozadini i prvom planu", "backward": "Unazad", + "beta_sync": "Beta status sinkronizacije", + "beta_sync_subtitle": "Upravljaj novim sustavom sinkronizacije", "biometric_auth_enabled": "Biometrijska autentikacija omogućena", "biometric_locked_out": "Zaključani ste iz biometrijske autentikacije", "biometric_no_options": "Nema dostupnih biometrijskih opcija", @@ -565,7 +594,7 @@ "cache_settings_clear_cache_button": "Očisti predmemoriju", "cache_settings_clear_cache_button_title": "Briše predmemoriju aplikacije. Ovo će značajno utjecati na performanse aplikacije dok se predmemorija ponovno ne izgradi.", "cache_settings_duplicated_assets_clear_button": "OČISTI", - "cache_settings_duplicated_assets_subtitle": "Fotografije i videozapisi koje je aplikacija stavila na crnu listu", + "cache_settings_duplicated_assets_subtitle": "Fotografije i videozapisi koje je aplikacija ignorira", "cache_settings_duplicated_assets_title": "Duplicirani resursi ({count})", "cache_settings_statistics_album": "Sličice biblioteke", "cache_settings_statistics_full": "Pune slike", @@ -582,6 +611,7 @@ "cancel": "Otkaži", "cancel_search": "Otkaži pretragu", "canceled": "Otkazano", + "canceling": "Otkazivanje", "cannot_merge_people": "Nije moguće spojiti osobe", "cannot_undo_this_action": "Ne možete poništiti ovu radnju!", "cannot_update_the_description": "Nije moguće ažurirati opis", @@ -645,6 +675,7 @@ "confirm_password": "Potvrdite lozinku", "confirm_tag_face": "Želite li označiti ovo lice kao {name}?", "confirm_tag_face_unnamed": "Želite li označiti ovo lice?", + "connected_device": "Uređaj povezan", "connected_to": "Povezano s", "contain": "Sadrži", "context": "Kontekst", @@ -694,9 +725,11 @@ "current_server_address": "Trenutna adresa poslužitelja", "custom_locale": "Prilagođena Lokalizacija", "custom_locale_description": "Formatiranje datuma i brojeva na temelju jezika i regije", + "custom_url": "Prilagođena URL adresa", "daily_title_text_date": "E, MMM dd", "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "Tamno", + "dark_theme": "Prebaci tamnu temu", "date_after": "Datum nakon", "date_and_time": "Datum i Vrijeme", "date_before": "Datum prije", @@ -712,6 +745,8 @@ "default_locale": "Zadana lokalizacija", "default_locale_description": "Oblikujte datume i brojeve na temelju jezika preglednika", "delete": "Izbriši", + "delete_action_confirmation_message": "Jeste li sigurni da želite izbrisati ovaj sadržaj? Ova radnja će premjestiti sadržaj u smeće na poslužitelju i upitat će vas želite li ga izbrisati i lokalno", + "delete_action_prompt": "{count} izbrisano", "delete_album": "Izbriši album", "delete_api_key_prompt": "Jeste li sigurni da želite izbrisati ovaj API ključ?", "delete_dialog_alert": "Ove stavke bit će trajno izbrisane iz Immicha i s vašeg uređaja", @@ -725,9 +760,12 @@ "delete_key": "Ključ za brisanje", "delete_library": "Izbriši knjižnicu", "delete_link": "Izbriši poveznicu", + "delete_local_action_prompt": "{count} izbrisano lokalno", "delete_local_dialog_ok_backed_up_only": "Izbriši samo sigurnosno kopirane", "delete_local_dialog_ok_force": "Izbriši svejedno", "delete_others": "Izbriši druge", + "delete_permanently": "Izbriši trajno", + "delete_permanently_action_prompt": "{count} trajno izbrisano", "delete_shared_link": "Izbriši dijeljenu poveznicu", "delete_shared_link_dialog_title": "Izbriši dijeljenu poveznicu", "delete_tag": "Izbriši oznaku", @@ -738,12 +776,14 @@ "description": "Opis", "description_input_hint_text": "Dodaj opis...", "description_input_submit_error": "Pogreška pri ažuriranju opisa, provjerite zapisnik za više detalja", + "deselect_all": "Poništi odabir svih", "details": "Detalji", "direction": "Smjer", "disabled": "Onemogućeno", "disallow_edits": "Zabrani izmjene", "discord": "Discord", "discover": "Otkrij", + "discovered_devices": "Otkriveni uređaji", "dismiss_all_errors": "Odbaci sve pogreške", "dismiss_error": "Odbaci pogrešku", "display_options": "Mogućnosti prikaza", @@ -754,6 +794,7 @@ "documentation": "Dokumentacija", "done": "Gotovo", "download": "Preuzmi", + "download_action_prompt": "Preuzimanje {count} sadržaja", "download_canceled": "Preuzimanje otkazano", "download_complete": "Preuzimanje završeno", "download_enqueue": "Preuzimanje dodano u red", @@ -791,6 +832,7 @@ "edit_key": "Ključ za uređivanje", "edit_link": "Uredi poveznicu", "edit_location": "Uredi lokaciju", + "edit_location_action_prompt": "{count} uređenih lokacija", "edit_location_dialog_title": "Lokacija", "edit_name": "Uredi ime", "edit_people": "Uredi ljude", @@ -809,6 +851,7 @@ "empty_trash": "Isprazni smeće", "empty_trash_confirmation": "Jeste li sigurni da želite isprazniti smeće? Time će se iz Immicha trajno ukloniti sva sredstva u otpadu.\nNe možete poništiti ovu radnju!", "enable": "Omogući", + "enable_backup": "Omogući sigurnosnu kopiju", "enable_biometric_auth_description": "Unesite svoj PIN kod za omogućavanje biometrijske autentikacije", "enabled": "Omogućeno", "end_date": "Datum završetka", @@ -965,6 +1008,8 @@ "explorer": "Pretraživač (Explorer)", "export": "Izvoz", "export_as_json": "Izvezi kao JSON", + "export_database": "Izvezi bazu podataka", + "export_database_description": "Izvezi SQLite bazu podataka", "extension": "Proširenje (Extension)", "external": "Vanjski", "external_libraries": "Vanjske Biblioteke", @@ -976,6 +1021,7 @@ "failed_to_load_assets": "Neuspjelo učitavanje stavki", "failed_to_load_folder": "Neuspjelo učitavanje mape", "favorite": "Omiljeno", + "favorite_action_prompt": "{count} dodano u Omiljeno", "favorite_or_unfavorite_photo": "Omiljena ili neomiljena fotografija", "favorites": "Omiljene", "favorites_page_no_favorites": "Nema pronađenih omiljenih stavki", @@ -1015,6 +1061,9 @@ "haptic_feedback_switch": "Omogući haptičku povratnu informaciju", "haptic_feedback_title": "Haptička povratna informacija", "has_quota": "Ima kvotu", + "hash_asset": "Hash sadržaja", + "hashed_assets": "Hashirani sadržaji", + "hashing": "Hashiranje", "header_settings_add_header_tip": "Dodaj zaglavlje", "header_settings_field_validator_msg": "Vrijednost ne može biti prazna", "header_settings_header_name_input": "Naziv zaglavlja", @@ -1047,6 +1096,7 @@ "host": "Domaćin", "hour": "Sat", "id": "ID", + "idle": "Neaktivan", "ignore_icloud_photos": "Ignoriraj iCloud fotografije", "ignore_icloud_photos_description": "Fotografije pohranjene na iCloudu neće biti učitane na Immich poslužitelj", "image": "Slika", @@ -1100,6 +1150,9 @@ "kept_this_deleted_others": "Zadržana je ova datoteka i izbrisano {count, plural, one {# datoteka} other {# datoteka}}", "keyboard_shortcuts": "Prečaci tipkovnice", "language": "Jezik", + "language_no_results_subtitle": "Pokušajte prilagoditi pojam za pretraživanje", + "language_no_results_title": "Nisu pronađeni jezici", + "language_search_hint": "Pretraži jezike...", "language_setting_description": "Odaberite željeni jezik", "last_seen": "Zadnji put viđen", "latest_version": "Najnovija verzija", @@ -1116,15 +1169,18 @@ "library_page_sort_created": "Datum kreiranja", "library_page_sort_last_modified": "Zadnja izmjena", "library_page_sort_title": "Naslov albuma", + "licenses": "Licence", "light": "Svjetlo", "like_deleted": "Like izbrisan", "link_motion_video": "Povežite videozapis pokreta", - "link_options": "Opcije veze", "link_to_oauth": "Veza na OAuth", "linked_oauth_account": "Povezani OAuth račun", "list": "Popis", "loading": "Učitavanje", "loading_search_results_failed": "Učitavanje rezultata pretraživanja nije uspjelo", + "local": "Lokalno", + "local_asset_cast_failed": "Nije moguće reproducirati sadržaj koji nije prenesen na poslužitelj", + "local_assets": "Lokalni sadržaji", "local_network": "Lokalna mreža", "local_network_sheet_info": "Aplikacija će se povezati s poslužiteljem putem ovog URL-a kada koristi određenu Wi-Fi mrežu", "location_permission": "Dozvola za lokaciju", @@ -1138,6 +1194,7 @@ "locked_folder": "Zaključana Mapa", "log_out": "Odjavi se", "log_out_all_devices": "Odjava sa svih uređaja", + "logged_in_as": "Prijavljeni kao {user}", "logged_out_all_devices": "Odjavljeni su svi uređaji", "logged_out_device": "Odjavljen uređaj", "login": "Prijava", @@ -1180,8 +1237,7 @@ "manage_your_devices": "Upravljajte uređajima na kojima ste prijavljeni", "manage_your_oauth_connection": "Upravljajte svojom OAuth vezom", "map": "Karta", - "map_assets_in_bound": "{count} fotografija", - "map_assets_in_bounds": "{count} fotografija", + "map_assets_in_bounds": "{count, plural, one {# fotografija} other {# fotografija}}", "map_cannot_get_user_location": "Nije moguće dohvatiti lokaciju korisnika", "map_location_dialog_yes": "Da", "map_location_picker_page_use_location": "Koristi ovu lokaciju", @@ -1233,6 +1289,7 @@ "more": "Više", "move": "Pomakni", "move_off_locked_folder": "Premjesti iz zaključane mape", + "move_to_lock_folder_action_prompt": "{count} dodano u zaključanu mapu", "move_to_locked_folder": "Premjesti u zaključanu mapu", "move_to_locked_folder_confirmation": "Ove fotografije i videozapis bit će uklonjeni iz svih albuma i bit će vidljivi samo iz zaključane mape", "moved_to_archive": "Premješteno {count, plural, one {# resurs} other {# resursa}} u arhivu", @@ -1265,6 +1322,7 @@ "no_archived_assets_message": "Arhivirajte fotografije i videozapise kako biste ih sakrili iz prikaza fotografija", "no_assets_message": "KLIKNITE DA PRENESETE SVOJU PRVU FOTOGRAFIJU", "no_assets_to_show": "Nema stavki za prikaz", + "no_cast_devices_found": "Nisu pronađeni uređaji za reprodukciju", "no_duplicates_found": "Nisu pronađeni duplikati.", "no_exif_info_available": "Nema dostupnih exif podataka", "no_explore_results_message": "Prenesite više fotografija da istražite svoju zbirku.", @@ -1278,6 +1336,7 @@ "no_results": "Nema rezultata", "no_results_description": "Pokušajte sa sinonimom ili općenitijom ključnom riječi", "no_shared_albums_message": "Stvorite album za dijeljenje fotografija i videozapisa s osobama u svojoj mreži", + "no_uploads_in_progress": "Nema aktivnih prijenosa", "not_in_any_album": "Ni u jednom albumu", "not_selected": "Nije odabrano", "note_apply_storage_label_to_previously_uploaded assets": "Napomena: Da biste primijenili Oznaku za skladištenje na prethodno prenesena sredstva, pokrenite", @@ -1297,8 +1356,11 @@ "oldest_first": "Prvo najstarije", "on_this_device": "Na ovom uređaju", "onboarding": "Uključivanje (Onboarding)", - "onboarding_privacy_description": "Sljedeće (neobavezne) značajke oslanjaju se na vanjske usluge i mogu se onemogućiti u bilo kojem trenutku u postavkama administracije.", + "onboarding_locale_description": "Odaberite željeni jezik. Kasnije ga možete promijeniti u postavkama.", + "onboarding_privacy_description": "Sljedeće (neobavezne) značajke oslanjaju se na vanjske usluge i mogu se onemogućiti u bilo kojem trenutku u postavkama.", + "onboarding_server_welcome_description": "Postavimo vašu instancu s nekim uobičajenim postavkama.", "onboarding_theme_description": "Odaberite temu boja za svoj primjer. To možete kasnije promijeniti u postavkama.", + "onboarding_user_welcome_description": "Počnimo!", "onboarding_welcome_user": "Dobro došli, {user}", "online": "Dostupan (Online)", "only_favorites": "Samo omiljeno", @@ -1312,6 +1374,7 @@ "original": "originalno", "other": "Ostalo", "other_devices": "Ostali uređaji", + "other_entities": "Ostali entiteti", "other_variables": "Ostale varijable", "owned": "Vlasništvo", "owner": "Vlasnik", @@ -1443,6 +1506,7 @@ "purchase_server_description_2": "Status podupiratelja", "purchase_server_title": "Poslužitelj (Server)", "purchase_settings_server_activated": "Ključem proizvoda poslužitelja upravlja administrator", + "queue_status": "Stavljanje u red {count}/{total}", "rating": "Broj zvjezdica", "rating_clear": "Obriši ocjenu", "rating_count": "{count, plural, one {# zvijezda} other {# zvijezde}}", @@ -1471,6 +1535,8 @@ "refreshing_faces": "Osvježavanje lica", "refreshing_metadata": "Osvježavanje metapodataka", "regenerating_thumbnails": "Obnavljanje sličica", + "remote": "Udaljeno", + "remote_assets": "Udaljeni sadržaji", "remove": "Ukloni", "remove_assets_album_confirmation": "Jeste li sigurni da želite ukloniti {count, plural, one {# datoteku} other {# datoteke}} iz albuma?", "remove_assets_shared_link_confirmation": "Jeste li sigurni da želite ukloniti {count, plural, one {# datoteku} other {# datoteke}} iz ove dijeljene veze?", @@ -1478,12 +1544,15 @@ "remove_custom_date_range": "Ukloni prilagođeni datumski raspon", "remove_deleted_assets": "Ukloni izbrisana sredstva", "remove_from_album": "Ukloni iz albuma", + "remove_from_album_action_prompt": "{count} uklonjeno iz albuma", "remove_from_favorites": "Ukloni iz favorita", + "remove_from_lock_folder_action_prompt": "{count} uklonjeno iz zaključane mape", "remove_from_locked_folder": "Ukloni iz zaključane mape", "remove_from_locked_folder_confirmation": "Jeste li sigurni da želite premjestiti ove fotografije i videozapise iz zaključane mape? Bit će vidljivi u vašoj biblioteci.", "remove_from_shared_link": "Ukloni iz dijeljene poveznice", "remove_memory": "Ukloni uspomenu", "remove_photo_from_memory": "Ukloni fotografiju iz ove uspomene", + "remove_tag": "Ukloni oznaku", "remove_url": "Ukloni URL", "remove_user": "Ukloni korisnika", "removed_api_key": "Uklonjen API ključ: {name}", @@ -1505,11 +1574,15 @@ "reset_password": "Resetiraj lozinku", "reset_people_visibility": "Poništi vidljivost ljudi", "reset_pin_code": "Resetiraj PIN kod", + "reset_sqlite": "Resetiraj SQLite bazu podataka", + "reset_sqlite_confirmation": "Jeste li sigurni da želite resetirati SQLite bazu podataka? Morat ćete se odjaviti i ponovno prijaviti kako biste ponovno sinkronizirali podatke", + "reset_sqlite_success": "SQLite baza podataka je uspješno resetirana", "reset_to_default": "Vrati na zadano", "resolve_duplicates": "Riješite duplikate", "resolved_all_duplicates": "Razriješi sve duplikate", "restore": "Oporavi", "restore_all": "Oporavi sve", + "restore_trash_action_prompt": "{count} vraćeno iz smeća", "restore_user": "Vrati korisnika", "restored_asset": "Obnovljena datoteka", "resume": "Nastavi", @@ -1518,6 +1591,7 @@ "role": "Uloga", "role_editor": "Urednik", "role_viewer": "Gledatelj", + "running": "U tijeku", "save": "Spremi", "save_to_gallery": "Spremi u galeriju", "saved_api_key": "Spremljen API ključ", @@ -1590,6 +1664,7 @@ "select_album_cover": "Odaberite omot albuma", "select_all": "Odaberi sve", "select_all_duplicates": "Odaberi sve duplikate", + "select_all_in": "Odaberi sve u {group}", "select_avatar_color": "Odaberi boju avatara", "select_face": "Odaberi lice", "select_featured_photo": "Odaberi istaknutu fotografiju", @@ -1610,6 +1685,7 @@ "server_info_box_server_url": "URL poslužitelja", "server_offline": "Server izvan mreže", "server_online": "Server na mreži", + "server_privacy": "Privatnost poslužitelja", "server_stats": "Statistike servera", "server_version": "Verzija servera", "set": "Postavi", @@ -1619,6 +1695,7 @@ "set_date_of_birth": "Postavi datum rođenja", "set_profile_picture": "Postavi profilnu sliku", "set_slideshow_to_fullscreen": "Postavi prezentaciju na cijeli zaslon", + "set_stack_primary_asset": "Postavi kao glavni sadržaj", "setting_image_viewer_help": "Preglednik detalja prvo učitava malu sličicu, zatim učitava pregled srednje veličine (ako je omogućen), te na kraju učitava original (ako je omogućen).", "setting_image_viewer_original_subtitle": "Omogućite za učitavanje originalne slike pune rezolucije (velika!). Onemogućite za smanjenje potrošnje podataka (i mrežne i na predmemoriji uređaja).", "setting_image_viewer_original_title": "Učitaj originalnu sliku", @@ -1646,6 +1723,7 @@ "settings_saved": "Postavke su spremljene", "setup_pin_code": "Postavi PIN kod", "share": "Podijeli", + "share_action_prompt": "Podijeljeno {count} sadržaja", "share_add_photos": "Dodaj fotografije", "share_assets_selected": "{count} odabrano", "share_dialog_preparing": "Priprema...", @@ -1667,6 +1745,7 @@ "shared_link_clipboard_copied_massage": "Kopirano u međuspremnik", "shared_link_clipboard_text": "Poveznica: {link}\nLozinka: {password}", "shared_link_create_error": "Pogreška pri kreiranju dijeljene poveznice", + "shared_link_custom_url_description": "Pristupite ovom dijeljenom linku pomoću prilagođene URL adrese", "shared_link_edit_description_hint": "Unesite opis dijeljenja", "shared_link_edit_expire_after_option_day": "1 dan", "shared_link_edit_expire_after_option_days": "{count} dana", @@ -1692,6 +1771,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Upravljanje dijeljenim poveznicama", "shared_link_options": "Opcije dijeljene poveznice", + "shared_link_password_description": "Zahtjevaj loziku za pristup ovom dijeljenom linku", "shared_links": "Dijeljene poveznice", "shared_links_description": "Podijelite fotografije i videozapise putem poveznice", "shared_photos_and_videos_count": "{assetCount, plural, =1 {# podijeljena fotografija ili videozapis.} few {# podijeljene fotografije i videozapisa.} other {# podijeljenih fotografija i videozapisa.}}", @@ -1747,6 +1827,7 @@ "sort_title": "Naslov", "source": "Izvor", "stack": "Složi", + "stack_action_prompt": "{count} složeno", "stack_duplicates": "Složi duplikate", "stack_select_one_photo": "Odaberi jednu glavnu fotografiju za slaganje", "stack_selected_photos": "Složi odabrane fotografije", @@ -1756,6 +1837,7 @@ "start_date": "Datum početka", "state": "Stanje", "status": "Status", + "stop_casting": "Zaustavi reprodukciju", "stop_motion_photo": "Zaustavi pokretnu fotografiju", "stop_photo_sharing": "Prestati dijeliti svoje fotografije?", "stop_photo_sharing_description": "{partner} više neće moći pristupiti vašim fotografijama.", @@ -1765,6 +1847,7 @@ "storage_quota": "Kvota Pohrane", "storage_usage": "{used} od {available} iskorišteno", "submit": "Pošalji", + "success": "Uspijeh", "suggestions": "Prijedlozi", "sunrise_on_the_beach": "Izlazak sunca na plaži", "support": "Podrška", @@ -1774,6 +1857,8 @@ "sync": "Sink.", "sync_albums": "Sinkroniziraj albume", "sync_albums_manual_subtitle": "Sinkroniziraj sve prenesene videozapise i fotografije u odabrane albume za sigurnosnu kopiju", + "sync_local": "Sinkroniziraj lokalno", + "sync_remote": "Sinkroniziraj udaljeno", "sync_upload_album_setting_subtitle": "Kreiraj i prenesi svoje fotografije i videozapise u odabrane albume na Immichu", "tag": "Oznaka", "tag_assets": "Označi stavke", @@ -1784,6 +1869,7 @@ "tag_updated": "Ažurirana oznaka: {tag}", "tagged_assets": "Označena {count, plural, =1 {# stavka} few {# stavke} other {# stavki}}", "tags": "Oznake", + "tap_to_run_job": "Dodirnite za pokretanje zadatka", "template": "Predložak", "theme": "Tema", "theme_selection": "Izbor teme", @@ -1816,6 +1902,7 @@ "total": "Ukupno", "total_usage": "Ukupna upotreba", "trash": "Smeće", + "trash_action_prompt": "{count} premješteno u smeće", "trash_all": "Stavi sve u smeće", "trash_count": "Smeće {count, number}", "trash_delete_asset": "Premjesti u smeće / Izbriši stavku", @@ -1833,8 +1920,11 @@ "unable_to_change_pin_code": "Nije moguće promijeniti PIN kod", "unable_to_setup_pin_code": "Nije moguće postaviti PIN kod", "unarchive": "Poništi arhiviranje", + "unarchive_action_prompt": "{count} uklonjeno iz arhive", "unarchived_count": "{count, plural, =1 {Poništeno arhiviranje #} few {Poništeno arhiviranje #} other {Poništeno arhiviranje #}}", + "undo": "Poništi", "unfavorite": "Ukloni iz omiljenih", + "unfavorite_action_prompt": "{count} uklonjeno iz favorita", "unhide_person": "Prikaži osobu", "unknown": "Nepoznato", "unknown_country": "Nepoznata država", @@ -1850,16 +1940,22 @@ "unsaved_change": "Nespremljena promjena", "unselect_all": "Poništi odabir svih", "unselect_all_duplicates": "Poništi odabir svih duplikata", + "unselect_all_in": "Poništi odabir svih u {group}", "unstack": "Razdvoji", + "unstack_action_prompt": "{count} razloženo", "unstacked_assets_count": "Razdvojena {count, plural, =1 {# stavka} few {# stavke} other {# stavki}}", + "untagged": "Bez oznaka", "up_next": "Sljedeće", "updated_at": "Ažurirano", "updated_password": "Lozinka ažurirana", "upload": "Prijenos", + "upload_action_prompt": "{count} u redu za prijenos", "upload_concurrency": "Istovremeni prijenosi", + "upload_details": "Detalji prijenosa", "upload_dialog_info": "Želite li sigurnosno kopirati odabranu stavku(e) na poslužitelj?", "upload_dialog_title": "Prenesi stavku", "upload_errors": "Prijenos završen s {count, plural, =1 {# greškom} few {# greške} other {# grešaka}}, osvježite stranicu da biste vidjeli nove prenesene stavke.", + "upload_finished": "Prijenos završen", "upload_progress": "Preostalo {remaining, number} - Obrađeno {processed, number}/{total, number}", "upload_skipped_duplicates": "Preskočena {count, plural, =1 {# duplicirana stavka} few {# duplicirane stavke} other {# dupliciranih stavki}}", "upload_status_duplicates": "Duplikati", @@ -1868,6 +1964,7 @@ "upload_success": "Prijenos uspješan, osvježite stranicu da biste vidjeli nove prenesene stavke.", "upload_to_immich": "Prenesi na Immich ({count})", "uploading": "Prijenos u tijeku", + "uploading_media": "Prijenos medija", "url": "URL", "usage": "Korištenje", "use_biometric": "Koristi biometriju", @@ -1879,6 +1976,7 @@ "user_liked": "{user} je označio/la sviđa mi se {type, select, photo {ovu fotografiju} video {ovaj videozapis} asset {ovu stavku} other {to}}", "user_pin_code_settings": "PIN kod", "user_pin_code_settings_description": "Upravljajte svojim PIN kodom", + "user_privacy": "Privatnost korisnika", "user_purchase_settings": "Kupnja", "user_purchase_settings_description": "Upravljajte svojom kupnjom", "user_role_set": "Postavi {user} kao {role}", @@ -1887,6 +1985,7 @@ "user_usage_stats_description": "Pregledajte statistiku korištenja računa", "username": "Korisničko ime", "users": "Korisnici", + "users_added_to_album_count": "Dodan{o/a} {count, plural, one {# korisnik} few {# korisnika} other {# korisnika}} u album.", "utilities": "Alati", "validate": "Provjeri valjanost", "validate_endpoint_error": "Molimo unesite valjanu URL adresu", @@ -1905,6 +2004,7 @@ "view_album": "Prikaži album", "view_all": "Prikaži sve", "view_all_users": "Prikaži sve korisnike", + "view_details": "Prikaži pojedinosti", "view_in_timeline": "Prikaži na vremenskoj crti", "view_link": "Prikaži poveznicu", "view_links": "Prikaži poveznice", diff --git a/i18n/hu.json b/i18n/hu.json index 22ec6f22a1..71060b5330 100644 --- a/i18n/hu.json +++ b/i18n/hu.json @@ -22,6 +22,7 @@ "add_partner": "Partner hozzáadása", "add_path": "Elérési útvonal megadása", "add_photos": "Fotók hozzáadása", + "add_tag": "Címke hozzáadása", "add_to": "Hozzáadás ide…", "add_to_album": "Felvétel albumba", "add_to_album_bottom_sheet_added": "Hozzáadva a(z) \"{album}\" albumhoz", @@ -33,6 +34,7 @@ "added_to_favorites_count": "{count, number} hozzáadva a kedvencekhez", "admin": { "add_exclusion_pattern_description": "Kihagyási minták (pattern) megadása. A *, ** és ? helyettesítő karakterek engedélyezettek. Pl. a \"Raw\" könyvtárban tárolt összes fájl kihagyásához használható a \"**/Raw/**\". Minden \".tif\" fájl kihagyása az összes mappában: \"**/*.tif\". Abszolút elérési útvonal kihagyása: \"/kihagyni/kivant/mappa/**\".", + "admin_user": "Admin felhasználó", "asset_offline_description": "Ez a külső képtárban lévő elem már nem található, ezért a lomtárba került. Ha a fájl a képtáron belül lett áthelyezve, akkor ellenőrizd, hogy továbbra is látható az idővonaladon. Az elem visszaállításához győződj meg róla, hogy az alábbi mappa az Immich számára elérhető, majd újra fésüld át a képtárat.", "authentication_settings": "Hitelesítési beállítások", "authentication_settings_description": "Jelszó, OAuth és egyéb hitelesítési beállítások kezelése", @@ -43,7 +45,7 @@ "backup_database_enable_description": "Adatbázis mentések engedélyezése", "backup_keep_last_amount": "Megőrizendő korábbi mentések száma", "backup_settings": "Adatbázis mentés beállításai", - "backup_settings_description": "Adatbázis mentés beállításainak kezelése. Megjegyzés: Ezek a feladatok nincsenek felügyelve, így nem kapsz értesítés meghiúsulás esetén.", + "backup_settings_description": "Adatbázis mentés beállításainak kezelése.", "cleared_jobs": "{job}: feladatai törölve", "config_set_by_file": "A konfigurációt jelenleg egy konfigurációs fájl állítja be", "confirm_delete_library": "Biztosan ki szeretnéd törölni a {library} képtárat?", @@ -138,7 +140,7 @@ "machine_learning_smart_search_description": "Képek szemantikai keresése CLIP beágyazások segítségével", "machine_learning_smart_search_enabled": "Okos keresés engedélyezése", "machine_learning_smart_search_enabled_description": "Ha ki van kapcsolva, a képek nem lesznek átalakítva okos kereséshez.", - "machine_learning_url_description": "Gépi tanulás szerver URL címe. Ha többi, mint egy URL van megadva, mindegyik szervert egyenként próbálja meg, amíg az egyik sikeresen nem válaszol, sorrendben az elsőtől az utólsóig. A nem válaszoló szervereket átmenetileg figyelmen kívül hagyja, amíg újra online nem lesznek.", + "machine_learning_url_description": "Gépi tanulás szerver URL címe. Ha többi, mint egy URL van megadva, mindegyik szervert egyenként próbálja meg, amíg az egyik sikeresen nem válaszol, sorrendben az elsőtől az utólsóig. A nem elérhető szervereket átmenetileg figyelmen kívül lesznek hagyva, amíg újra online nem lesznek.", "manage_concurrency": "Párhuzamos Feladatok Kezelése", "manage_log_settings": "Naplózási beállítások kezelése", "map_dark_style": "Sötét stílus", @@ -155,7 +157,7 @@ "map_settings_description": "Térkép beállítások kezelése", "map_style_description": "Egy style.json térképtémára mutató URL cím", "memory_cleanup_job": "Memória takarítás", - "memory_generate_job": "Emlék generálálsa", + "memory_generate_job": "Emlék generálása", "metadata_extraction_job": "Metaadatok kinyerése", "metadata_extraction_job_description": "Metaadat információk (pl. GPS, arcok és felbontás) kinyerése minden elemből", "metadata_faces_import_setting": "Arc importálás engedélyezése", @@ -164,12 +166,22 @@ "metadata_settings_description": "Metaadat beállítások kezelése", "migration_job": "Migrálás", "migration_job_description": "Az elemek és arcok bélyegképeinek migrálása a legújabb mappastruktúrába", + "nightly_tasks_cluster_faces_setting_description": "Arcfelismerés futtatása az újonnan érzékelt arcokon", + "nightly_tasks_cluster_new_faces_setting": "Új arcok csoportosítása", + "nightly_tasks_database_cleanup_setting": "Adatbázis-tisztítási feladatok", + "nightly_tasks_database_cleanup_setting_description": "A régi, lejárt adatok törlése az adatbázisból", + "nightly_tasks_generate_memories_setting": "Emlékek generálása", + "nightly_tasks_generate_memories_setting_description": "Új emlékek létrehozása elemekből", + "nightly_tasks_missing_thumbnails_setting": "Hiányzó indexképek generálása", + "nightly_tasks_settings": "Éjjeli Feladat Beállítások", + "nightly_tasks_settings_description": "Éjjeli feladatok kezelése", + "nightly_tasks_start_time_setting": "Kezdőidő", "no_paths_added": "Nincs megadva elérési útvonal", "no_pattern_added": "Nincs megadva minta (pattern)", "note_apply_storage_label_previous_assets": "Megjegyzés: Ha a korábban feltöltött elemekhez is szeretne Tárhely Címkéket társítani, akkor futtassa ezt", "note_cannot_be_changed_later": "FIGYELEM: ezt később nem lehet megváltoztatni!", "notification_email_from_address": "Feladó cím", - "notification_email_from_address_description": "Küldő email címe, például: \"Immich Fotószerver \"", + "notification_email_from_address_description": "Küldő email címe, például: \"Immich Fotószerver \". Figyelj hogy olyan címet adj meg ahonnan az email küldés engedélyezett.", "notification_email_host_description": "Email szerver kiszolgálója (pl. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Tanúsítvány hibák figyelmen kívül hagyása", "notification_email_ignore_certificate_errors_description": "TLS tanúsítvány érvényességi hibák figyelmen kívül hagyása (nem ajánlott)", @@ -202,7 +214,7 @@ "oauth_storage_quota_claim": "Tárhelykvóta igénylése", "oauth_storage_quota_claim_description": "A felhasználó tárhelykvótájának automatikus beállítása ennek az igényeltre.", "oauth_storage_quota_default": "Alapértelmezett tárhelykvóta (GiB)", - "oauth_storage_quota_default_description": "Alapértelmezett tárhely kvóta GiB-ban, amennyiben a felhasználó nem jelezte az igényét (A korlátlan tárhelyhez 0-t adj meg).", + "oauth_storage_quota_default_description": "Alapértelmezett tárhely kvóta GiB-ban, amennyiben a felhasználó nem jelezte az igényét.", "oauth_timeout": "Kérés időkorlátja", "oauth_timeout_description": "Kérések időkorlátja milliszekundumban", "password_enable_description": "Bejelentkezés emaillel és jelszóval", @@ -242,6 +254,7 @@ "storage_template_migration_info": "A sablon az összes kiterjesztést kisbetűssé alakítja át. A megváltozott sablon csak az újonnan feltöltött elemekre vonatkozik. A korábbi elemek visszamenőleges áthelyezéséhez ezt futtasd: {job}.", "storage_template_migration_job": "Tárhely Sablon Migrációja", "storage_template_more_details": "További részletekért erről a funkcióról lásd a Tárhely Sablon és annak következményeit a dokumentációban", + "storage_template_onboarding_description_v2": "A funkció engedélyezésével automatikusan, a felhasználó által definiált sablon alapján lesznek rendezve a fájlok. Több információért lásd a dokumentációt.", "storage_template_path_length": "Útvonal hozzávetőleges maximális hossza: {length, number}{limit, number}", "storage_template_settings": "Tárhely Sablon", "storage_template_settings_description": "A feltöltött elemek mappaszerkezetének és fájl elnevezésének kezelése", @@ -256,7 +269,7 @@ "template_email_update_album": "Album frissítve sablon", "template_email_welcome": "Üdvözlő email sablon", "template_settings": "Értesítés sablon", - "template_settings_description": "Egyéni sablonok kezelése az értesítésekhez.", + "template_settings_description": "Egyéni sablonok kezelése az értesítésekhez", "theme_custom_css_settings": "Egyedi CSS", "theme_custom_css_settings_description": "CSS Stíluslapokkal az Immich stílusa megváltoztatható.", "theme_settings": "Téma Beállítások", @@ -288,7 +301,7 @@ "transcoding_encoding_options": "Enkódolás beállítások", "transcoding_encoding_options_description": "Beállíthatod az enkódolt videók kódolási algoritmusát, felbontását, minőségét és egyéb beállításait", "transcoding_hardware_acceleration": "Hardveres Gyorsítás", - "transcoding_hardware_acceleration_description": "Kísérleti funkció. Sokkal gyorsabb, viszont azonos bitrátán is alacsonyabb minőséghez vezet", + "transcoding_hardware_acceleration_description": "Kísérleti funkció: gyorsabb transzkódolás, viszont azonos bitrátán alacsonyabb minőséghez vezethet", "transcoding_hardware_decoding": "Hardveres dekódolás", "transcoding_hardware_decoding_setting_description": "Lehetővé teszi az egész folyamat gyorsítását a pusztán kódolás gyorsítása helyett. Nem biztos, hogy minden videó esetén működik.", "transcoding_max_b_frames": "B-képkockák maximum száma", @@ -334,6 +347,7 @@ "user_delete_delay_settings_description": "Hány nappal az eltávolítás után legyen véglegesen törölve a felhasználó fiókja és tárolt elemei. A végleges törlés feladat minden éjfélkor fut le, hogy ellenőrizze, hogy van-e törlendő felhasználó. Ez a beállítás a következő futtatás során lép életbe.", "user_delete_immediately": "{user} felhasználója és összes eleme azonnal sorba állításra kerül a végleges törléshez .", "user_delete_immediately_checkbox": "Felhasználó és tárolt elemeinek sorba állítása azonnali törlésre", + "user_details": "Felhasználói adatok", "user_management": "Felhasználók Kezelése", "user_password_has_been_reset": "A felhasználó jelszava megváltoztatásra került:", "user_password_reset_description": "Juttasd el az átmeneti jelszót a felhasználóhoz és tájékoztasd, hogy a következő belépésnél azt majd meg kell változtatnia.", @@ -349,19 +363,22 @@ "video_conversion_job": "Videók Átkódolása", "video_conversion_job_description": "Videók átkódolása böngészőkkel és eszközökkel való széleskörű kompatibilitás érdekében" }, + "admin_email": "Admin e-mail", "admin_password": "Admin Jelszó", "administration": "Adminisztráció", "advanced": "Haladó", + "advanced_settings_beta_timeline_title": "Béta Idővonal", "advanced_settings_enable_alternate_media_filter_subtitle": "Ezzel a beállítással a szinkronizálás során alternatív kritériumok alapján szűrheted a fájlokat. Csak akkor próbáld ki, ha problémáid vannak azzal, hogy az alkalmazás nem ismeri fel az összes albumot.", "advanced_settings_enable_alternate_media_filter_title": "[KÍSÉRLETI] Alternatív eszköz album szinkronizálási szűrő használata", "advanced_settings_log_level_title": "Naplózás szintje: {level}", - "advanced_settings_prefer_remote_subtitle": "Néhány eszköz fájdalmasan lassan tölti be az eszközön lévő bélyegképeket. Ez a beállítás inkább a távoli képeket tölti be helyettük.", + "advanced_settings_prefer_remote_subtitle": "Néhány eszköz fájdalmasan lassan tölti be az eszközön lévő indexképeket. Ez a beállítás inkább a távoli képeket (a szerverről) tölti be helyettük.", "advanced_settings_prefer_remote_title": "Távoli képek előnyben részesítése", "advanced_settings_proxy_headers_subtitle": "Add meg azokat a proxy fejléceket, amiket az app elküldjön minden hálózati kérésnél", "advanced_settings_proxy_headers_title": "Proxy Fejlécek", "advanced_settings_self_signed_ssl_subtitle": "Nem ellenőrzi a szerver SSL tanúsítványát. Önaláírt tanúsítvány esetén szükséges beállítás.", "advanced_settings_self_signed_ssl_title": "Önaláírt SSL tanúsítványok engedélyezése", "advanced_settings_sync_remote_deletions_subtitle": "Automatikusan törölni vagy visszaállítani egy elemet ezen az eszközön, ha az adott műveletet a weben hajtották végre", + "advanced_settings_sync_remote_deletions_title": "Távoli törlések szinkronizálása [KÍSÉRLETI FUNKCIÓ]", "advanced_settings_tile_subtitle": "Haladó felhasználói beállítások", "advanced_settings_troubleshooting_subtitle": "További funkciók engedélyezése hibaelhárítás céljából", "advanced_settings_troubleshooting_title": "Hibaelhárítás", @@ -373,6 +390,7 @@ "album_cover_updated": "Album borító frissítve", "album_delete_confirmation": "Biztos, hogy ki szeretnéd törölni a(z) {album} albumot?", "album_delete_confirmation_description": "Amennyiben ez egy megosztott album, a többi felhasználó sem fog tudni többé hozzáférni.", + "album_deleted": "Album törölve", "album_info_card_backup_album_excluded": "KIHAGYVA", "album_info_card_backup_album_included": "BELEÉRTVE", "album_info_updated": "Album infó frissítve", @@ -398,6 +416,10 @@ "album_with_link_access": "A link birtokában bárki láthatja a fotókat és a személyeket ebben az albumban.", "albums": "Albumok", "albums_count": "{count, plural, one {{count, number} Album} other {{count, number} Album}}", + "albums_default_sort_order": "Alapértelmezett album rendezés", + "albums_default_sort_order_description": "Alapértelmezett sorrendezés új albumok létrehozásánál.", + "albums_feature_description": "Másokkal megosztható elemek gyűjteménye.", + "albums_on_device_count": "Albumok az eszközön ({count})", "all": "Mind", "all_albums": "Minden album", "all_people": "Minden személy", @@ -455,10 +477,12 @@ "assets": "Elemek", "assets_added_count": "{count, plural, other {# elem}} hozzáadva", "assets_added_to_album_count": "{count, plural, other {# elem}} hozzáadva az albumhoz", - "assets_added_to_name_count": "{count, plural, other {# elem}} hozzáadva {hasName, select, true {a(z) {name}} other {az új}} albumhoz", + "assets_cannot_be_added_to_album_count": "{count, plural, one {Az elem} other {Az elemek}} nem adhatóak hozzá az albumhoz", "assets_count": "{count, plural, other {# elem}}", "assets_deleted_permanently": "{count} elem véglegesen törölve", "assets_deleted_permanently_from_server": "{count} elem véglegesen törölve az Immich szerverről", + "assets_downloaded_failed": "{count, plural, one {# fájl letöltve - {error} fájl sikertelen} other {# fájl letöltve - {error} fájl sikertelen}}", + "assets_downloaded_successfully": "{count, plural, one {# fájl sikeresen letöltve} other {# fájl sikeresen letöltve}}", "assets_moved_to_trash_count": "{count, plural, other {# elem}} áthelyezve a lomtárba", "assets_permanently_deleted_count": "{count, plural, other {# elem}} véglegesen törölve", "assets_removed_count": "{count, plural, other {# elem}} eltávolítva", @@ -477,6 +501,7 @@ "back_close_deselect": "Vissza, bezárás, vagy kijelölés törlése", "background_location_permission": "Háttérben történő helymeghatározási engedély", "background_location_permission_content": "Hálózatok automatikus váltásához az Immich-nek *mindenképpen* hozzá kell férnie a pontos helyzethez, hogy az alkalmazás le tudja kérni a Wi-Fi hálózat nevét", + "backup": "Mentés", "backup_album_selection_page_albums_device": "Ezen az eszközön lévő albumok ({count})", "backup_album_selection_page_albums_tap": "Koppints a hozzáadáshoz, duplán koppints az eltávolításhoz", "backup_album_selection_page_assets_scatter": "Egy elem több albumban is lehet. Ezért a mentéshez albumokat lehet hozzáadni vagy azokat a mentésből kihagyni.", @@ -484,10 +509,10 @@ "backup_album_selection_page_selection_info": "Összegzés", "backup_album_selection_page_total_assets": "Összes egyedi elem", "backup_all": "Összes", - "backup_background_service_backup_failed_message": "Az elemek mentése sikertelen. Újrapróbálkozás...", - "backup_background_service_connection_failed_message": "A szerverhez csatlakozás sikertelen. Újrapróbálkozás...", + "backup_background_service_backup_failed_message": "Az elemek mentése sikertelen. Újrapróbálkozás…", + "backup_background_service_connection_failed_message": "A szerverhez csatlakozás sikertelen. Újrapróbálkozás…", "backup_background_service_current_upload_notification": "Feltöltés {filename}", - "backup_background_service_default_notification": "Új elemek ellenőrzése...", + "backup_background_service_default_notification": "Új elemek ellenőrzése…", "backup_background_service_error_title": "Hiba a mentés közben", "backup_background_service_in_progress_notification": "Elemek mentése folyamatban…", "backup_background_service_upload_failure_notification": "A feltöltés sikertelen {filename}", @@ -497,6 +522,7 @@ "backup_controller_page_background_app_refresh_enable_button_text": "Beállítások megnyitása", "backup_controller_page_background_battery_info_link": "Mutasd meg hogyan", "backup_controller_page_background_battery_info_message": "A sikeres háttérben történő mentéshez kérjük, tiltsd le az Immich akkumulátor optimalizálását.\n\nMivel ezt a különféle eszközökön máshogy kell, ezért kérjük, az eszközöd gyártójától tudd meg, hogyan kell.", + "backup_controller_page_background_battery_info_ok": "OK", "backup_controller_page_background_battery_info_title": "Akkumulátor optimalizálás", "backup_controller_page_background_charging": "Csak töltés közben", "backup_controller_page_background_configure_error": "A háttérszolgáltatás beállítása sikertelen", @@ -506,7 +532,7 @@ "backup_controller_page_background_is_on": "Automatikus mentés a háttérben be van kapcsolva", "backup_controller_page_background_turn_off": "Háttérszolgáltatás kikapcsolása", "backup_controller_page_background_turn_on": "Háttérszolgáltatás bekapcsolása", - "backup_controller_page_background_wifi": "Csak WiFi-n", + "backup_controller_page_background_wifi": "Csak Wi-Fi-n", "backup_controller_page_backup": "Mentés", "backup_controller_page_backup_selected": "Kiválasztva: ", "backup_controller_page_backup_sub": "Mentett fotók és videók", @@ -539,6 +565,12 @@ "backup_options_page_title": "Biztonági mentés beállításai", "backup_setting_subtitle": "A háttérben és előtérben mentés beállításainak kezelése", "backward": "Visszafele", + "beta_sync": "Béta Szinkronizálás Állapota", + "beta_sync_subtitle": "Az új szinkronizálási rendszer kezelése", + "biometric_auth_enabled": "Biometrikus azonosítás engedélyezve", + "biometric_locked_out": "Ki vagy zárva a biometrikus azonosításból", + "biometric_no_options": "Nincsen elérhető biometrikus azonosítás", + "biometric_not_available": "Biometrikus azonosítás ezen az eszközön nem elérhető", "birthdate_saved": "Születésnap elmentve", "birthdate_set_description": "A születés napját a rendszer arra használja, hogy kiírja, hogy a fénykép készítésekor a személy hány éves volt.", "blurred_background": "Homályos háttér", @@ -551,7 +583,7 @@ "cache_settings_clear_cache_button": "Gyorsítótár kiürítése", "cache_settings_clear_cache_button_title": "Kiüríti az alkalmazás gyorsítótárát. Ez jelentősen kihat az alkalmazás teljesítményére, amíg a gyorsítótár újra nem épül.", "cache_settings_duplicated_assets_clear_button": "KIÜRÍT", - "cache_settings_duplicated_assets_subtitle": "Fotók és videók, amiket az alkalmazás fekete listára tett", + "cache_settings_duplicated_assets_subtitle": "Fotók és videók, amiket az alkalmazás figyelmen kívül hagyott", "cache_settings_duplicated_assets_title": "Duplikált Elemek ({count})", "cache_settings_statistics_album": "Képtár bélyegképei", "cache_settings_statistics_full": "Teljes méretű képek", @@ -572,6 +604,7 @@ "cannot_undo_this_action": "Ez a művelet nem visszavonható!", "cannot_update_the_description": "A leírás megváltoztatása nem sikerült", "change_date": "Dátum változtatása", + "change_description": "Leírás megváltoztatása", "change_display_order": "Megjelenítési sorrend megváltoztatása", "change_expiration_time": "Lejárati idő megváltoztatása", "change_location": "Helyszín változtatása", @@ -598,6 +631,7 @@ "clear_all_recent_searches": "Legutóbbi keresések törlése", "clear_message": "Üzenet törlése", "clear_value": "Érték törlése", + "client_cert_dialog_msg_confirm": "OK", "client_cert_enter_password": "Jelszó Megadása", "client_cert_import": "Importálás", "client_cert_import_success_msg": "Kliens tanúsítvány importálva", @@ -625,6 +659,10 @@ "confirm_keep_this_delete_others": "Minden más elem a készletben törlésre kerül, kivéve ezt az elemet. Biztosan folytatni szeretnéd?", "confirm_new_pin_code": "Új PIN kód megerősítése", "confirm_password": "Jelszó megerősítése", + "confirm_tag_face": "Szeretnéd ezt az arcot {name}-nak/nek megjelölni?", + "confirm_tag_face_unnamed": "Szeretnéd ezt az arcot megjelölni?", + "connected_device": "Kapcsolt eszköz", + "connected_to": "Kapcsolódva", "contain": "Belül", "context": "Kontextus", "continue": "Folytatás", @@ -633,6 +671,7 @@ "control_bottom_app_bar_delete_from_local": "Törlés az eszközről", "control_bottom_app_bar_edit_location": "Hely Módosítása", "control_bottom_app_bar_edit_time": "Dátum és Idő Módosítása", + "control_bottom_app_bar_share_link": "Link megosztása", "control_bottom_app_bar_share_to": "Megosztás Ide", "control_bottom_app_bar_trash_from_immich": "Lomtárba Helyez", "copied_image_to_clipboard": "Kép a vágólapra másolva.", @@ -664,6 +703,7 @@ "create_tag_description": "Új címke létrehozása. Beágyazott címkék esetén add meg a címke teljes elérési útvonalát, beleértve a perjeleket is.", "create_user": "Felhasználó létrehozása", "created": "Készült", + "created_at": "Létrehozva", "crop": "Kivágás", "curated_object_page_title": "Dolgok", "current_device": "Ez az eszköz", @@ -674,6 +714,7 @@ "daily_title_text_date": "MMM dd (E)", "daily_title_text_date_year": "yyyy MMM dd (E)", "dark": "Sötét", + "dark_theme": "Sötét téma kapcsolása", "date_after": "Dátumtól", "date_and_time": "Dátum és Idő", "date_before": "Dátumig", @@ -689,6 +730,7 @@ "default_locale": "Alapértelmezett Területi Beállítás", "default_locale_description": "Dátumok és számok formázása a böngésződ területi beállítása alapján", "delete": "Törlés", + "delete_action_prompt": "{count} törölve", "delete_album": "Album törlése", "delete_api_key_prompt": "Biztosan törölni szeretnéd ezt az API kulcsot?", "delete_dialog_alert": "Ezek az elemek véglegesen törölve lesznek Immich-ről és az eszközödről is", @@ -702,9 +744,12 @@ "delete_key": "Kulcs törlése", "delete_library": "Képtár Törlése", "delete_link": "Link törlése", + "delete_local_action_prompt": "{count} törölve az eszközről", "delete_local_dialog_ok_backed_up_only": "Csak a Biztonsági Mentés Törlése", "delete_local_dialog_ok_force": "Törlés Mindenképp", "delete_others": "Többi törlése", + "delete_permanently": "Törlés véglegesen", + "delete_permanently_action_prompt": "{count} törölve véglegesen", "delete_shared_link": "Megosztott link törlése", "delete_shared_link_dialog_title": "Megosztott Link Törlése", "delete_tag": "Címke törlése", @@ -719,7 +764,9 @@ "direction": "Irány", "disabled": "Letiltott", "disallow_edits": "Módosítások letiltása", + "discord": "Discord", "discover": "Felfedez", + "discovered_devices": "Felfedezett eszközök", "dismiss_all_errors": "Minden hiba elvetése", "dismiss_error": "Hiba elvetése", "display_options": "Megjelenítési beállítások", @@ -730,6 +777,7 @@ "documentation": "Dokumentáció", "done": "Kész", "download": "Letöltés", + "download_action_prompt": "{count} elem letöltése", "download_canceled": "Letöltés megszakítva", "download_complete": "Letöltés kész", "download_enqueue": "Letöltés sorba állítva", @@ -744,8 +792,8 @@ "download_settings_description": "Elemek letöltésével kapcsolatos beállítások kezelése", "download_started": "Letöltés megkezdve", "download_sucess": "Sikeres letöltés", - "download_sucess_android": "Média letöltve a DCIM/Immich mappába\n", - "download_waiting_to_retry": "Várakozás", + "download_sucess_android": "Média letöltve a DCIM/Immich mappába", + "download_waiting_to_retry": "Várás az újrapróbálkozásra", "downloading": "Letöltés", "downloading_asset_filename": "{filename} elem letöltése", "downloading_media": "Média letöltése", @@ -758,6 +806,8 @@ "edit_avatar": "Profilkép módosítása", "edit_date": "Dátum módosítása", "edit_date_and_time": "Dátum és idő módosítása", + "edit_description": "Leírás szerkesztése", + "edit_description_prompt": "Kérlek válassz egy új leírást:", "edit_exclusion_pattern": "Kizárási minta (pattern) módosítása", "edit_faces": "Arcok módosítása", "edit_import_path": "Importálási útvonal módosítása", @@ -765,6 +815,7 @@ "edit_key": "Kulcs módosítása", "edit_link": "Link módosítása", "edit_location": "Hely módosítása", + "edit_location_action_prompt": "{count} hely változtatva", "edit_location_dialog_title": "Hely", "edit_name": "Név módosítása", "edit_people": "Személyek módosítása", @@ -777,18 +828,26 @@ "editor_close_without_save_title": "Szerkesztő bezárása?", "editor_crop_tool_h2_aspect_ratios": "Oldalarányok", "editor_crop_tool_h2_rotation": "Forgatás", + "email": "E-mail", + "email_notifications": "E-mail értesítések", + "empty_folder": "Ez a mappa üres", "empty_trash": "Lomtár ürítése", "empty_trash_confirmation": "Biztosan kiüríted a lomtárat? Ez az Immich lomtárában lévő összes elemet véglegesen törli.\nEz a művelet nem visszavonható!", "enable": "Engedélyezés", + "enable_backup": "Biztonsági mentés bekapcsolása", + "enable_biometric_auth_description": "Add meg a jelszavad a biometrikus azonosítás engedélyezéséhez", "enabled": "Engedélyezve", "end_date": "Vég dátum", "enqueued": "Sorba állítva", - "enter_wifi_name": "Add meg a WiFi hálózat nevét", + "enter_wifi_name": "Add meg a Wi-Fi hálózat nevét", + "enter_your_pin_code": "Add meg a jelszavad", + "enter_your_pin_code_subtitle": "Add meg a PIN kódodat a zárolt mappa megnyitásához", "error": "Hiba", "error_change_sort_album": "Album sorbarendezésének megváltoztatása sikertelen", "error_delete_face": "Hiba az arc törlése során", "error_loading_image": "Hiba a kép betöltése közben", "error_saving_image": "Hiba: {error}", + "error_tag_face_bounding_box": "Hiba az arc megjelölése közben - nem elérhetőek a határoló koordináták", "error_title": "Hiba - valami félresikerült", "errors": { "cannot_navigate_next_asset": "Nem lehet a következő elemhez navigálni", @@ -816,10 +875,12 @@ "failed_to_keep_this_delete_others": "Nem sikerült megtartani ezt az elemet, és a többi elemet törölni", "failed_to_load_asset": "Elem betöltése sikertelen", "failed_to_load_assets": "Elemek betöltése sikertelen", + "failed_to_load_notifications": "Értesítések betöltése sikertelen", "failed_to_load_people": "Személyek betöltése sikertelen", "failed_to_remove_product_key": "Termékkulcs eltávolítása sikertelen", "failed_to_stack_assets": "Elemek csoportosítása sikertelen", "failed_to_unstack_assets": "Csoportosított elemek szétszedése sikertelen", + "failed_to_update_notification_status": "Értesítés státusz frissítése sikertelen", "import_path_already_exists": "Ez az importálási útvonal már létezik.", "incorrect_email_or_password": "Helytelen email vagy jelszó", "paths_validation_failed": "A(z) {paths, plural, one {# elérési útvonal} other {# elérési útvonal}} érvényesítése sikertelen", @@ -836,6 +897,7 @@ "unable_to_archive_unarchive": "Az elem {archived, select, true {archiválása} other {kivétele az archívumból}} sikertelen", "unable_to_change_album_user_role": "Az album felhasználói jogkörének megváltoztatása sikertelen", "unable_to_change_date": "Dátum megváltoztatása sikertelen", + "unable_to_change_description": "Leírás módosítása sikertelen", "unable_to_change_favorite": "Az elem kedvenc állapotának megváltoztatása sikertelen", "unable_to_change_location": "Hely megváltoztatása sikertelen", "unable_to_change_password": "Jelszó megváltoztatása sikertelen", @@ -879,6 +941,7 @@ "unable_to_remove_partner": "Partner eltávolítása sikertelen", "unable_to_remove_reaction": "Reakció eltávolítása sikertelen", "unable_to_reset_password": "Jelszó visszaállítása sikertelen", + "unable_to_reset_pin_code": "PIN kód visszaállítása sikertelen", "unable_to_resolve_duplicate": "Duplikátum feloldása sikertelen", "unable_to_restore_assets": "Elemek visszaállítása sikertelen", "unable_to_restore_trash": "Az összes elem visszaállítása sikertelen", @@ -906,11 +969,15 @@ "unable_to_update_user": "Felhasználó módosítása sikertelen", "unable_to_upload_file": "Fájlfeltöltés sikertelen" }, + "exif": "Exif", "exif_bottom_sheet_description": "Leírás Hozzáadása...", "exif_bottom_sheet_details": "RÉSZLETEK", "exif_bottom_sheet_location": "HELY", "exif_bottom_sheet_people": "EMBEREK", "exif_bottom_sheet_person_add_person": "Elnevez", + "exif_bottom_sheet_person_age_months": "{months} hónap idős", + "exif_bottom_sheet_person_age_year_months": "1 év, {months} hónap idős", + "exif_bottom_sheet_person_age_years": "Életkor: {years}", "exit_slideshow": "Kilépés a Diavetítésből", "expand_all": "Összes kinyitása", "experimental_settings_new_asset_list_subtitle": "Fejlesztés alatt", @@ -924,14 +991,18 @@ "explorer": "Böngésző", "export": "Exportálás", "export_as_json": "Exportálás JSON formátumban", + "export_database": "Adatbázis Exportálása", + "export_database_description": "Az SQLite adatbázis exportálása", "extension": "Kiterjesztés", "external": "Külső Képtár", "external_libraries": "Külső Képtárak", "external_network": "Külső hálózat", - "external_network_sheet_info": "Ha nem vagy a megadott WiFi hálózathoz csatlakozva, akkor az alkalmazás az alábbi URL címeken fogja elérni a szervert, fentről lefelé haladva", + "external_network_sheet_info": "Ha nem vagy a megadott Wi-Fi hálózathoz csatlakozva, akkor az alkalmazás az alábbi URL címeken fogja elérni a szervert, fentről lefelé haladva", "face_unassigned": "Nincs hozzárendelve", "failed": "Sikertelen", + "failed_to_authenticate": "Autentikáció sikertelen", "failed_to_load_assets": "Nem sikerült betölteni az elemeket", + "failed_to_load_folder": "Mappa betöltése sikertelen", "favorite": "Kedvenc", "favorite_or_unfavorite_photo": "Fotó kedvencnek jelölése vagy annak visszavonása", "favorites": "Kedvencek", @@ -945,14 +1016,19 @@ "filetype": "Fájltípus", "filter": "Szűrő", "filter_people": "Személyek szűrése", + "filter_places": "Helyszínek szűrése", "find_them_fast": "Név alapján kereséssel gyorsan megtalálhatóak", "fix_incorrect_match": "Hibás találat javítása", + "folder": "Mappa", + "folder_not_found": "Mappa nem található", "folders": "Mappák", "folders_feature_description": "A fájlrendszerben lévő fényképek és videók mappanézetben való böngészése", "forward": "Előre", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "Ez a funkció a Google-től tölti be a működéséhez szükséges külső adatokat.", "general": "Általános", "get_help": "Segítségkérés", - "get_wifiname_error": "Nem sikerült lekérni a Wi-Fi nevét. Győződj meg róla, hogy megadtad a szükséges engedélyeket és csatlakoztál egy Wi-Fi hálózathoz.", + "get_wifiname_error": "Nem sikerült lekérni a Wi-Fi nevét. Győződj meg róla, hogy megadtad a szükséges engedélyeket és csatlakoztál egy Wi-Fi hálózathoz", "getting_started": "Kezdő Lépések", "go_back": "Visszalépés", "go_to_folder": "Ugrás a mappához", @@ -981,9 +1057,9 @@ "hide_person": "Személy elrejtése", "hide_unnamed_people": "Név nélküli személyek elrejtése", "home_page_add_to_album_conflicts": "{added} elem hozzáadva a(z) \"{album}\" albumhoz. {failed} elem már eleve az albumban volt.", - "home_page_add_to_album_err_local": "Helyi elemeket még nem lehet albumba tenni. Kihagyjuk.", + "home_page_add_to_album_err_local": "Helyi elemeket még nem lehet albumba tenni, ki lesznek hagyva", "home_page_add_to_album_success": "{added} elem hozzáadva a(z) \"{album}\" albumhoz.", - "home_page_album_err_partner": "Még nem lehet a partner elemeit albumokhoz adni, úghogy kihagyjuk.", + "home_page_album_err_partner": "Még nem lehet a partner elemeit albumokhoz adni, ki lesznek hagyva", "home_page_archive_err_local": "Helyi elemek archiválása még nem támogatott, úgyhogy kihagyjuk", "home_page_archive_err_partner": "Partner elemeit nem lehet archiválni, úgyhogy kihagyjuk", "home_page_building_timeline": "Idővonal összeállítása", @@ -991,11 +1067,14 @@ "home_page_delete_remote_err_local": "Helyi elemek vannak távoli törlésre kiválasztva, úgyhogy ezeket kihagyjuk", "home_page_favorite_err_local": "Helyi elemeket még nem lehet a kedvencek közé tenni, úgyhogy ezeket kihagyjuk", "home_page_favorite_err_partner": "Partner elemeit még nem lehet a kedvencek közé tenni, úgyhogy ezeket kihagyjuk", - "home_page_first_time_notice": "Ha most használod először az alkalmazást, akkor ahhoz, hogy megjelenjenek a fotók és a videók az idővonaladon, állítsd be, hogy melyik albumaidról készüljön biztonsági mentés.", + "home_page_first_time_notice": "Ha most használod először az alkalmazást, a fotók és videók megjelenítéséhez az idővonaladon, állítsd be, hogy melyik albumaidról készüljön biztonsági mentés", + "home_page_locked_error_local": "A Helyi elemek nem mozgathatóak a zárolt mappába, ki lesznek hagyva", + "home_page_locked_error_partner": "Partner elemek nem mozgathatóak a zárolt mappába, átugorva", "home_page_share_err_local": "Helyi elemekről nem lehet megosztott linket készíteni, úgyhogy kihagyjuk", "home_page_upload_err_limit": "Csak 30 elemet tudsz egyszerre feltölteni, úgyhogy kihagyjuk", "host": "Kiszolgáló", "hour": "Óra", + "id": "Azonosító", "ignore_icloud_photos": "iCloud fotók figyelmen kívül hagyása", "ignore_icloud_photos_description": "Az iCloud-ban tárolt fotók nem lesznek feltöltve az Immich szerverre", "image": "Kép", @@ -1035,6 +1114,11 @@ "invalid_date_format": "Érvénytelen dátumformátum", "invite_people": "Személyek Meghívása", "invite_to_album": "Meghívás az albumba", + "ios_debug_info_last_sync_at": "Utoljára szinkronizálva {dateTime}", + "ios_debug_info_no_processes_queued": "Nincs a sorban háttérfolyamat jelenleg", + "ios_debug_info_no_sync_yet": "Még nem futott szinkronizáló háttérfolyamat", + "ios_debug_info_processes_queued": "{count, plural, one {{count} háttérfolyamat előkészítve} other {{count} háttérfolyamat előkészítve}}", + "ios_debug_info_processing_ran_at": "A feldolgozás ekkor futott: {dateTime}", "items_count": "{count, plural, other {# elem}}", "jobs": "Feladatok", "keep": "Megtart", @@ -1043,6 +1127,8 @@ "kept_this_deleted_others": "Ez az elem és a töröltek meg lettek hagyva {count, plural, one {# asset} other {# assets}}", "keyboard_shortcuts": "Billentyűparancsok", "language": "Nyelv", + "language_no_results_subtitle": "Próbáld módosítani a szavaidat a keresésnél", + "language_search_hint": "Nyelvek keresése...", "language_setting_description": "Válaszd ki preferált nyelvet", "last_seen": "Utoljára láttuk", "latest_version": "Legfrissebb Verzió", @@ -1059,26 +1145,31 @@ "library_page_sort_created": "Létrehozás ideje", "library_page_sort_last_modified": "Utolsó módosítás ideje", "library_page_sort_title": "Album címe", + "licenses": "Licencek", "light": "Világos", "like_deleted": "Reakció törölve", "link_motion_video": "Motion videó hozzárendelése", - "link_options": "Link beállítások", "link_to_oauth": "Csatlakoztatás OAuth-hoz", "linked_oauth_account": "Csatlakoztatott OAuth felhasználó", "list": "Lista", "loading": "Betöltés", "loading_search_results_failed": "Keresési eredmények betöltése sikertelen", + "local": "Helyi", + "local_assets": "Helyi Elemek", "local_network": "Helyi hálózat", "local_network_sheet_info": "Az alkalmazés ezen az URL címen fogja elérni a szervert, ha a megadott WiFi hálózathoz van csatlankozva", "location_permission": "Helymeghatározási engedély", - "location_permission_content": "Hálózatok automatikus váltásához az Immich-nek *mindenképpen* hozzá kell férnie a pontos helyzethez, hogy az alkalmazás le tudja kérni a Wi-Fi hálózat nevét", + "location_permission_content": "A Hálózatok automatikus váltásához az Immich-nek szüksége van a pontos helymeghatározásra, hogy az alkalmazás le tudja kérni a Wi-Fi hálózat nevét", "location_picker_choose_on_map": "Válassz a térképen", "location_picker_latitude_error": "Érvényes szélességi kört írj be", "location_picker_latitude_hint": "Ide írd a szélességi kört", "location_picker_longitude_error": "Érvényes hosszúsági kört írj be", "location_picker_longitude_hint": "Ide írd a hosszúsági kört", + "lock": "Zárolás", + "locked_folder": "Zárolt mappa", "log_out": "Kijelentkezés", "log_out_all_devices": "Kijelentkezés Minden Eszközön", + "logged_in_as": "Belépve: {user} néven", "logged_out_all_devices": "Minden eszköz kijelentkeztetve", "logged_out_device": "Eszköz kijelentkeztetve", "login": "Bejelentkezés", @@ -1093,7 +1184,7 @@ "login_form_err_invalid_url": "Érvénytelen cím", "login_form_err_leading_whitespace": "Az első karakter szóköz", "login_form_err_trailing_whitespace": "Az utolsó karakter szóköz", - "login_form_failed_get_oauth_server_config": "Nem sikerült az OAuth bejelentkezés. Ellenőrizd a szerver címét.", + "login_form_failed_get_oauth_server_config": "Nem sikerült az OAuth bejelentkezés. Ellenőrizd a szerver URL-t", "login_form_failed_get_oauth_server_disable": "OAuth bejelentkezés nem elérhető ezen a szerveren", "login_form_failed_login": "Hiba a bejelentkezés közben, ellenőrizd a szerver címét, az emailt és a jelszót", "login_form_handshake_exception": "SSL Kézfogási Hiba törént. Engedélyezd az önaláírt tanúsítvényokat a beállításokban, hogy ha önaláírt tanúsítványt használsz.", @@ -1121,8 +1212,7 @@ "manage_your_devices": "Bejelentkezett eszközök kezelése", "manage_your_oauth_connection": "OAuth kapcsolódás kezelése", "map": "Térkép", - "map_assets_in_bound": "{count} fotó", - "map_assets_in_bounds": "{count} fotó", + "map_assets_in_bounds": "{count, plural, one {# fotó} other {# fotó}}", "map_cannot_get_user_location": "A helymeghatározás nem sikerült", "map_location_dialog_yes": "Igen", "map_location_picker_page_use_location": "Kiválasztott hely használata", @@ -1145,6 +1235,9 @@ "map_settings_only_show_favorites": "Csak Kedvencek Mutatása", "map_settings_theme_settings": "Térkép Témája", "map_zoom_to_see_photos": "Kicsinyítsd, hogy láss fényképeket", + "mark_all_as_read": "Összes megjelölése olvasottként", + "mark_as_read": "Megjelölés olvasottként", + "marked_all_as_read": "Összes megjelölve olvasottként", "matches": "Azonosak", "media_type": "Médiatípus", "memories": "Emlékek", @@ -1169,6 +1262,12 @@ "month": "Hónap", "monthly_title_text_date_format": "y MMMM", "more": "Továbbiak", + "move": "Áthelyezés", + "move_off_locked_folder": "Átmozgatás a zárolt mappából", + "move_to_locked_folder": "Áthelyezés a zárolt mappába", + "move_to_locked_folder_confirmation": "Ezek a képek és videók az összes albumból kikerülnek, és csak a zárolt mappában lesznek elérhetőek", + "moved_to_archive": "{count, plural, one {# Elem} other {# Elemek}} archiválva", + "moved_to_library": "{count, plural, one {# Elem} other {# Elemek}} másik könyvtárba költöztetve", "moved_to_trash": "Áthelyezve a lomtárba", "multiselect_grid_edit_date_time_err_read_only": "Csak-olvasható elem(ek) dátuma nem módosítható, ezért kihagyjuk", "multiselect_grid_edit_gps_err_read_only": "Csak-olvasható elem(ek) helye nem módosítható, ezért kihagyjuk", @@ -1184,6 +1283,7 @@ "new_password": "Új jelszó", "new_person": "Új személy", "new_pin_code": "Új PIN kód", + "new_pin_code_subtitle": "Ez az első alkalom hogy megnyitod a zárolt mappát. Hozz létre egy jelszót a mappa biztonságos eléréséhez", "new_user_created": "Új felhasználó létrehozva", "new_version_available": "ÚJ VERZIÓ ÉRHETŐ EL", "newest_first": "Legújabb először", @@ -1201,7 +1301,9 @@ "no_explore_results_message": "Tölts fel több képet, hogy böngészhesd a gyűjteményed.", "no_favorites_message": "Add hozzá a kedvencekhez, hogy gyorsan megtaláld a legjobb képeidet és videóidat", "no_libraries_message": "Hozz létre külső képtárat a fényképeid és videóid megtekintéséhez", + "no_locked_photos_message": "A zárolt mappában elhelyezett fotók és videók rejtettek, és nem jelennek meg a könyvtárad böngészése vagy keresése közben sem.", "no_name": "Nincs Név", + "no_notifications": "Nincsenek értesítések", "no_places": "Nincsenek helyek", "no_results": "Nincs találat", "no_results_description": "Próbálkozz szinonimákkal vagy általánosabb kulcsszavakkal", @@ -1210,6 +1312,7 @@ "not_selected": "Nincs kiválasztva", "note_apply_storage_label_to_previously_uploaded assets": "Megjegyzés: a korábban feltöltött elemek Tárhely Címkézéséhez futtasd a(z)", "notes": "Megjegyzések", + "nothing_here_yet": "Még semmi sincs itt", "notification_permission_dialog_content": "Az értesítések bekapcsolásához a Beállítások menüben válaszd ki az Engedélyezés-t.", "notification_permission_list_tile_content": "Értesítések engedélyezése.", "notification_permission_list_tile_enable_button": "Értesítések Bekapcsolása", @@ -1217,15 +1320,21 @@ "notification_toggle_setting_description": "Email értesítések engedélyezése", "notifications": "Értesítések", "notifications_setting_description": "Értesítések kezelése", + "oauth": "OAuth", "official_immich_resources": "Hivatalos Immich Források", + "offline": "Nem elérhető (offline)", "ok": "Rendben", "oldest_first": "Legrégebbi először", "on_this_device": "Ezen az eszközön", "onboarding": "Első lépések", - "onboarding_privacy_description": "Az alábbi (nem kötelező) funkciók külsős szolgáltatásokon alapulnak és bármikor kikapcsolhatóak az adminisztrációs beállításokban.", + "onboarding_locale_description": "Válaszd ki a preferált nyelved. Ezt később a beállításokban bármikor módosíthatod.", + "onboarding_privacy_description": "Az alábbi (nem kötelező) funkciók külsős szolgáltatásokon alapulnak és bármikor kikapcsolhatóak a beállításokban.", "onboarding_theme_description": "Válassz egy színtémát. Ezt bármikor megváltoztathatod a beállításokban.", + "onboarding_user_welcome_description": "Kezdjünk bele!", "onboarding_welcome_user": "Üdvözöllek {user}", + "online": "Online (elérhető)", "only_favorites": "Csak kedvencek", + "open": "Nyitva", "open_in_map_view": "Megnyitás térkép nézetben", "open_in_openstreetmap": "Megnyitás OpenStreetMap-ben", "open_the_search_filters": "Keresési szűrők megnyitása", @@ -1238,6 +1347,7 @@ "other_variables": "Egyéb változók", "owned": "Tulajdonos", "owner": "Tulajdonos", + "partner": "Partner", "partner_can_access": "{partner} hozzáférhet", "partner_can_access_assets": "Minden fényképed és videód, kivéve az Archiváltak és a Töröltek", "partner_can_access_location": "A helyszín, ahol a fotókat készítették", @@ -1247,7 +1357,7 @@ "partner_page_no_more_users": "Nincs több hozzáadható felhasználó", "partner_page_partner_add_failed": "Partner hozzáadása sikertelen", "partner_page_select_partner": "Partner kiválasztása", - "partner_page_shared_to_title": "Megosztva: ", + "partner_page_shared_to_title": "Megosztva", "partner_page_stop_sharing_content": "{partner} nem fog többé hozzáférni a fotóidhoz.", "partner_sharing": "Partner Megosztás", "partners": "Partnerek", @@ -1277,6 +1387,8 @@ "permanently_delete_assets_prompt": "Biztos, hogy véglegesen törölni {count, plural, one {szeretnéd ezt az elemet} other {szeretnél # elemet}}? Ez el fogja távolítani az {count, plural, one {elemet az albumokból, amikben szerepel} other {elemeket az albumokból, amikben szerepelnek}}.", "permanently_deleted_asset": "Elem véglegesen törölve", "permanently_deleted_assets_count": "{count, plural, other {# elem}} véglegesen törölve", + "permission": "Jogosultság", + "permission_empty": "A jogosultság nem hagyható üresen", "permission_onboarding_back": "Vissza", "permission_onboarding_continue_anyway": "Folytatás mindenképp", "permission_onboarding_get_started": "Vágjunk bele", @@ -1284,7 +1396,7 @@ "permission_onboarding_permission_denied": "Hozzáférés megtagadva. Az Immich használatához engedélyezni kell a fotó és videó hozzáférést a Beállításokban.", "permission_onboarding_permission_granted": "Hozzáférés engedélyezve! Minden készen áll.", "permission_onboarding_permission_limited": "Korlátozott hozzáférés. Ha szeretnéd, hogy az Immich a teljes galéria gyűjteményedet mentse és kezelje, akkor a Beállításokban engedélyezd a fotó és videó jogosultságokat.", - "permission_onboarding_request": "Engedélyezni kell, hogy az Immich hozzáférjen a képeidhez és videóidhoz", + "permission_onboarding_request": "Engedélyezni kell, hogy az Immich hozzáférjen a képeidhez és videóidhoz.", "person": "Személy", "person_birthdate": "Született: {date}", "person_hidden": "{name}{hidden, select, true { (rejtett)} other {}}", @@ -1304,19 +1416,25 @@ "play_memories": "Emlékek lejátszása", "play_motion_photo": "Mozgókép lejátszása", "play_or_pause_video": "Videó elindítása vagy megállítása", + "port": "Port", "preferences_settings_subtitle": "Alkalmazásbeállítások kezelése", "preferences_settings_title": "Beállítások", "preset": "Sablon", "preview": "Előnézet", "previous": "Előző", "previous_memory": "Előző emlék", - "previous_or_next_photo": "Előző vagy következő fotó", + "previous_or_next_day": "Nap előre/hátra", + "previous_or_next_month": "Hónap előre/hátra", + "previous_or_next_photo": "Fotó előre/hátra", + "previous_or_next_year": "Év előre/hátra", "primary": "Elsődleges", "privacy": "Magánszféra", + "profile": "Profil", "profile_drawer_app_logs": "Naplók", "profile_drawer_client_out_of_date_major": "A mobilalkalmazás elavult. Kérjük, frissítsd a legfrisebb főverzióra.", "profile_drawer_client_out_of_date_minor": "A mobilalkalmazás elavult. Kérjük, frissítsd a legfrisebb alverzióra.", "profile_drawer_client_server_up_to_date": "A Kliens és a Szerver is naprakész", + "profile_drawer_github": "GitHub", "profile_drawer_server_out_of_date_major": "A szerver elavult. Kérjük, frissítsd a legfrisebb főverzióra.", "profile_drawer_server_out_of_date_minor": "A szerver elavult. Kérjük, frissítsd a legfrisebb alverzióra.", "profile_image_of_user": "{user} profilképe", @@ -1370,6 +1488,8 @@ "recent_searches": "Legutóbbi keresések", "recently_added": "Nemrég hozzáadott", "recently_added_page_title": "Nemrég Hozzáadott", + "recently_taken": "Nemrég készített", + "recently_taken_page_title": "Nemrég készített", "refresh": "Frissítés", "refresh_encoded_videos": "Átkódolt videók frissítése", "refresh_faces": "Arcok frissítése", @@ -1381,6 +1501,8 @@ "refreshing_faces": "Arcok frissítése folyamatban", "refreshing_metadata": "Metaadatok frissítése folyamatban", "regenerating_thumbnails": "Bélyegképek újragenerálása folyamatban", + "remote": "Távoli", + "remote_assets": "Távoli Elemek", "remove": "Eltávolítás", "remove_assets_album_confirmation": "Biztosan el szeretnél távolítani {count, plural, one {# elemet} other {# elemet}} az albumból?", "remove_assets_shared_link_confirmation": "Biztosan el szeretnél távolítani {count, plural, one {# elemet} other {# elemet}} ebből a megosztott linkből?", @@ -1389,9 +1511,12 @@ "remove_deleted_assets": "Törölt Elemek Eltávolítása", "remove_from_album": "Eltávolítás az albumból", "remove_from_favorites": "Eltávolítás a kedvencekből", + "remove_from_locked_folder": "Eltávolítás a zárolt mappából", + "remove_from_locked_folder_confirmation": "Biztosan ki szeretnéd venni ezeket a fotókat és videókat a zárolt mappából? Láthatóak lesznek a könyvtáradban.", "remove_from_shared_link": "Eltávolítás a megosztott linkből", "remove_memory": "Emlék eltávolítása", "remove_photo_from_memory": "Kép eltávolítása az emlékből", + "remove_tag": "Címke eltávolítása", "remove_url": "URL eltávolítása", "remove_user": "Felhasználó eltávolítása", "removed_api_key": "API Kulcs eltávolítva: {name}", @@ -1413,6 +1538,7 @@ "reset_password": "Jelszó visszaállítása", "reset_people_visibility": "Személyek láthatóságának visszaállítása", "reset_pin_code": "PIN kód visszaállítása", + "reset_sqlite": "SQLite Adatbázis Visszaállítása", "reset_to_default": "Visszaállítás alapállapotba", "resolve_duplicates": "Duplikátumok feloldása", "resolved_all_duplicates": "Minden duplikátum feloldása", @@ -1483,9 +1609,9 @@ "search_places": "Helyek keresése", "search_rating": "Keresés értékelés szerint...", "search_result_page_new_search_hint": "Új Keresés", - "search_settings": "Keresési beállítások", + "search_settings": "Beállítások keresése", "search_state": "Megye/Állam keresése...", - "search_suggestion_list_smart_search_hint_1": "Az intelligens keresés alapértelmezetten be van kapcsolva, metaadatokat így kereshetsz: ", + "search_suggestion_list_smart_search_hint_1": "Az intelligens keresés alapértelmezetten be van kapcsolva, metaadatokat így kereshetsz ", "search_suggestion_list_smart_search_hint_2": "m:keresési-kifejezés", "search_tags": "Címkék keresése...", "search_timezone": "Időzóna keresése...", @@ -1517,6 +1643,7 @@ "server_info_box_server_url": "Szerver Címe", "server_offline": "Szerver Nem Elérhető", "server_online": "Szerver Elérhető", + "server_privacy": "Szerver biztonság", "server_stats": "Szerver Statisztikák", "server_version": "Szerver Verzió", "set": "Beállít", @@ -1526,6 +1653,7 @@ "set_date_of_birth": "Születési dátum beállítása", "set_profile_picture": "Profilkép beállítása", "set_slideshow_to_fullscreen": "Diavetítés teljes képernyőre állítása", + "set_stack_primary_asset": "Beállítás elsődleges elemként", "setting_image_viewer_help": "Az Elem Megjelenítő először a kis bélyegképet tölti be, aztán a közepes méretű előnézetet (ha elérhető), végül az eredetit (ha elérhető).", "setting_image_viewer_original_subtitle": "Engedélyezi az eredeti teljes felbontású kép betöltését (nagy!). Kikapcsolva csökkenti az adathasználatot (a neten és az eszköz gyorsítótárán is).", "setting_image_viewer_original_title": "Eredeti kép betöltése", @@ -1556,6 +1684,7 @@ "share_add_photos": "Fotók hozzáadása", "share_assets_selected": "{count} kiválasztva", "share_dialog_preparing": "Előkészítés...", + "share_link": "Link megosztása", "shared": "Megosztva", "shared_album_activities_input_disable": "Hozzászólások kikapcsolva", "shared_album_activity_remove_content": "Törölni szeretnéd ezt a tevékenységet?", @@ -1595,6 +1724,7 @@ "shared_link_expires_second": "{count} másodperc múlva lejár", "shared_link_expires_seconds": "{count} másodperc múlva lejár", "shared_link_individual_shared": "Egyénileg megosztva", + "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Megosztott linkek kezelése", "shared_link_options": "Megosztott link beállításai", "shared_links": "Megosztott linkek", @@ -1656,6 +1786,7 @@ "stack_select_one_photo": "Válassz egy fő képet a csoportból", "stack_selected_photos": "Kiválasztott fényképek csoportosítása", "stacked_assets_count": "{count, plural, other {# elem}} csoportosítva", + "stacktrace": "Hiba leírása", "start": "Elindít", "start_date": "Kezdő dátum", "state": "Megye/Állam", @@ -1666,8 +1797,10 @@ "stop_sharing_photos_with_user": "Fényképeid megosztásának megszüntetése ezzel a felhasználóval", "storage": "Tárhely", "storage_label": "Tárhely címke", + "storage_quota": "Tárhely kvóta", "storage_usage": "{used}/{available} használatban", "submit": "Beküldés", + "success": "Siker", "suggestions": "Javaslatok", "sunrise_on_the_beach": "Napkelte a tengerparton", "support": "Támogatás", @@ -1677,6 +1810,8 @@ "sync": "Szinkronizálás", "sync_albums": "Albumok szinkronizálása", "sync_albums_manual_subtitle": "Összes fotó és videó létrehozása és szinkronizálása a kiválasztott Immich albumokba", + "sync_local": "Helyi Szinkronizálása", + "sync_remote": "Távoli Szinkronizálása", "sync_upload_album_setting_subtitle": "Fotók és videók létrehozása és szinkronizálása a kiválasztott Immich albumba", "tag": "Címke", "tag_assets": "Elemek címkézése", @@ -1693,11 +1828,11 @@ "theme_selection_description": "A böngésző beállításának megfelelően automatikusan használjon világos vagy sötét témát", "theme_setting_asset_list_storage_indicator_title": "Tárhely ikon mutatása az elemeken", "theme_setting_asset_list_tiles_per_row_title": "Elemek száma soronként ({count})", - "theme_setting_colorful_interface_subtitle": "Alapértelmezett szín használata a háttérben lévő felületekhez", + "theme_setting_colorful_interface_subtitle": "Alapértelmezett szín használata a háttérben lévő felületekhez.", "theme_setting_colorful_interface_title": "Színes felhasználói felület", "theme_setting_image_viewer_quality_subtitle": "Részletes képmegjelenítő minőségének beállítása", "theme_setting_image_viewer_quality_title": "Képmegjelenítő minősége", - "theme_setting_primary_color_subtitle": "Válassz egy színt az alapértelmezett műveletekhez és kiemelésekhez", + "theme_setting_primary_color_subtitle": "Válassz egy színt az alapértelmezett műveletekhez és kiemelésekhez.", "theme_setting_primary_color_title": "Alapértelmezett szín", "theme_setting_system_primary_color_title": "Rendszerszínek használata", "theme_setting_system_theme_switch": "Automatikus (követi a rendszer témáját)", @@ -1737,6 +1872,7 @@ "unable_to_setup_pin_code": "Sikertelen PIN kód beállítás", "unarchive": "Archívumból kivesz", "unarchived_count": "{count, plural, other {# elem kivéve az archívumból}}", + "undo": "Visszavonás", "unfavorite": "Kedvenc közül kivesz", "unhide_person": "Nem rejtett személy", "unknown": "Ismeretlen", @@ -1756,6 +1892,7 @@ "unstack": "Csoport Szétszedése", "unstacked_assets_count": "{count, plural, other {# elemből}} álló csoport szétszedve", "up_next": "Következik", + "updated_at": "Frissített", "updated_password": "Jelszó megváltoztatva", "upload": "Feltöltés", "upload_concurrency": "Párhuzamos feltöltés", @@ -1770,14 +1907,17 @@ "upload_success": "Feltöltés sikeres, frissítsd az oldalt az újonnan feltöltött elemek megtekintéséhez.", "upload_to_immich": "Feltöltés Immich-be ({count})", "uploading": "Feltöltés folyamatban", + "url": "URL", "usage": "Használat", "use_current_connection": "Jelenlegi kapcsolat használata", "use_custom_date_range": "Szabadon megadott időintervallum használata", "user": "Felhasználó", + "user_has_been_deleted": "Ez a felhasználó törlésre került.", "user_id": "Felhasználó azonosítója", "user_liked": "{user} felhasználónak {type, select, photo {ez a fénykép} video {ez a videó} asset {ez az elem} other {ez}} tetszik", "user_pin_code_settings": "PIN kód", "user_pin_code_settings_description": "PIN kód kezelése", + "user_privacy": "Felhasználói adatvédelem", "user_purchase_settings": "Megvásárlás", "user_purchase_settings_description": "Vásárlás kezelése", "user_role_set": "{user} felhasználónak {role} jogkör biztosítása", @@ -1822,12 +1962,12 @@ "week": "Hét", "welcome": "Üdvözlünk", "welcome_to_immich": "Üdvözöl az Immich", - "wifi_name": "WiFi Neve", + "wifi_name": "Wi-Fi Neve", "wrong_pin_code": "Hibás PIN kód", "year": "Év", "years_ago": "{years, plural, one {# évvel} other {# évvel}} ezelőtt", "yes": "Igen", "you_dont_have_any_shared_links": "Nincsenek megosztott linkjeid", - "your_wifi_name": "A WiFi hálózatod neve", + "your_wifi_name": "A Wi-Fi hálózatod neve", "zoom_image": "Kép Nagyítása" } diff --git a/i18n/hy.json b/i18n/hy.json index 86cbcd5554..3bb7e1f526 100644 --- a/i18n/hy.json +++ b/i18n/hy.json @@ -28,7 +28,6 @@ "exif_bottom_sheet_person_add_person": "Ավելացնել անուն", "exif_bottom_sheet_person_age_years": "Տարիք {years}", "hi_user": "Բարեւ {name} ({email})", - "map_assets_in_bound": "{count} նկար", "map_assets_in_bounds": "{count} նկարներ", "partner_list_user_photos": "{}-ին նկարները", "photos": "Նկարներ", diff --git a/i18n/id.json b/i18n/id.json index 4d15ef01e9..7386887b5f 100644 --- a/i18n/id.json +++ b/i18n/id.json @@ -34,6 +34,7 @@ "added_to_favorites_count": "Ditambahkan {count, number} ke favorit", "admin": { "add_exclusion_pattern_description": "Tambahkan pola pengecualian. Glob menggunakan *, **, dan ? didukung. Untuk mengabaikan semua berkas dalam direktori apa pun bernama \"Raw\", gunakan \"**/Raw/**\". Untuk mengabaikan semua berkas berakhiran dengan \".tif\", gunakan \"**/*.tif\". Untuk mengabaikan jalur absolut, gunakan \"/jalur/untuk/diabaikan/**\".", + "admin_user": "Pengguna Admin", "asset_offline_description": "Aset pustaka eksternal ini tidak ada di diska dan telah dipindahkan ke tempat sampah. Jika berkasnya dipindah dalam pustaka, periksa lini masa Anda untuk aset baru yang cocok. Untuk memulihkan aset ini, pastikan jalur berkas di bawah dapat diakses oleh Immich dan pindai pustaka.", "authentication_settings": "Pengaturan Autentikasi", "authentication_settings_description": "Kelola kata sandi, OAuth, dan pengaturan autentikasi lainnya", @@ -44,7 +45,7 @@ "backup_database_enable_description": "Aktifkan pencadangan basis data", "backup_keep_last_amount": "Jumlah cadangan untuk disimpan", "backup_settings": "Pengaturan Pencadangan Basis Data", - "backup_settings_description": "Kelola pengaturan pencadangan basis data. Catatan: Tugas ini tidak dipantau dan Anda tidak akan diberi tahu jika ada kesalahan.", + "backup_settings_description": "Kelola pengaturan pencadangan basis data.", "cleared_jobs": "Tugas terselesaikan untuk: {job}", "config_set_by_file": "Konfigurasi saat ini ditetapkan oleh berkas konfigurasi", "confirm_delete_library": "Apakah Anda yakin ingin menghapus pustaka {library}?", @@ -165,12 +166,26 @@ "metadata_settings_description": "Kelola pengaturan metadata", "migration_job": "Migrasi", "migration_job_description": "Migrasikan gambar kecil untuk aset dan wajah ke struktur folder terkini", + "nightly_tasks_cluster_faces_setting_description": "Mulai pengenalan wajah pada semua wajah yang baru saja terdeteksi", + "nightly_tasks_cluster_new_faces_setting": "Kelompokkan semua wajah baru", + "nightly_tasks_database_cleanup_setting": "Tugas pembersihan basis data", + "nightly_tasks_database_cleanup_setting_description": "Membersihkan data lama, kadaluarsa dari database", + "nightly_tasks_generate_memories_setting": "Buat kenang-kenangan", + "nightly_tasks_generate_memories_setting_description": "Buat kenang-kenangan baru dari berbagai aset", + "nightly_tasks_missing_thumbnails_setting": "Membuat thumbnail yang hilang", + "nightly_tasks_missing_thumbnails_setting_description": "Mengantrikan aset tanpa thumbnail untuk pembuatan thumbnail", + "nightly_tasks_settings": "Pengaturan Tugas Malam", + "nightly_tasks_settings_description": "Atur tugas malam", + "nightly_tasks_start_time_setting": "Waktu mulai", + "nightly_tasks_start_time_setting_description": "Waktu saat server mulai menjalankan tugas malam", + "nightly_tasks_sync_quota_usage_setting": "Sinkronisasi penggunaan kuota", + "nightly_tasks_sync_quota_usage_setting_description": "Pembaruan kuota penyimpanan pengguna, berdasarkan penggunaan sekarang", "no_paths_added": "Tidak ada jalur yang ditambahkan", "no_pattern_added": "Tidak ada pola yang ditambahkan", "note_apply_storage_label_previous_assets": "Catatan: Untuk menerapkan Label Penyimpanan untuk aset yang telah diunggah sebelumnya, jalankan", "note_cannot_be_changed_later": "CATATAN: Ini tidak akan dapat diubah lagi!", "notification_email_from_address": "Dari alamat", - "notification_email_from_address_description": "Alamat surel pengirim, misalnya: \"Server Foto Immich \"", + "notification_email_from_address_description": "Alamat surel pengirim, misalnya: \"Server Foto Immich \". Pastikan untuk menggunakan alamat yang diizinkan untuk mengirim email.", "notification_email_host_description": "Hos server surel (mis. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Abaikan eror sertifikat", "notification_email_ignore_certificate_errors_description": "Abaikan eror validasi sertifikat TLS (tidak disarankan)", @@ -195,6 +210,7 @@ "oauth_mobile_redirect_uri": "URI pengalihan ponsel", "oauth_mobile_redirect_uri_override": "Penimpaan URI penerusan ponsel", "oauth_mobile_redirect_uri_override_description": "Aktifkan ketika provider OAuth tidak mengizinkan tautan mobile, seperti ''{callback}''", + "oauth_role_claim_description": "Secara otomatis memberikan akses admin berdasarkan keberadaan klaim ini. Klaim dapat berupa \"user\" atau \"admin\".", "oauth_settings": "OAuth", "oauth_settings_description": "Kelola pengaturan log masuk OAuth", "oauth_settings_more_details": "Untuk detail lanjut tentang fitur ini, lihat docs.", @@ -243,6 +259,7 @@ "storage_template_migration_info": "Templat penyimpanan akan mengubah semua ekstensi ke huruf kecil. Perubahan templat hanya akan diterapkan pada aset baru. Untuk menerapkan templat pada setiap aset yang sebelumnya telah diunggah, jalankan {job}.", "storage_template_migration_job": "Tugas Migrasi Templat Ruang Penyimpanan", "storage_template_more_details": "Untuk detail lebih lanjut tentang fitur ini, pergi ke Templat Penyimpanan dan kekurangannya", + "storage_template_onboarding_description_v2": "Saat diaktifkan, fitur ini akan mengatur file secara otomatis berdasarkan templat yang ditentukan pengguna. Untuk informasi selengkapnya, silakan lihat dokumentasi.", "storage_template_path_length": "Batas panjang jalur: {length, number}{limit, number}", "storage_template_settings": "Templat Penyimpanan", "storage_template_settings_description": "Kelola struktur folder dan nama berkas dari aset yang diunggah", @@ -257,7 +274,7 @@ "template_email_update_album": "Perbarui Templat Album", "template_email_welcome": "Templat surel selamat datang", "template_settings": "Templat Notifikasi", - "template_settings_description": "Kelola templat kustom untuk notifikasi.", + "template_settings_description": "Kelola templat kustom untuk notifikasi", "theme_custom_css_settings": "CSS Kustom", "theme_custom_css_settings_description": "CSS memungkinkan desain Immich untuk diubah.", "theme_settings": "Pengaturan Tema", @@ -355,13 +372,20 @@ "admin_password": "Kata Sandi Admin", "administration": "Administrasi", "advanced": "Tingkat lanjut", + "advanced_settings_beta_timeline_subtitle": "Coba pengalaman aplikasi baru", + "advanced_settings_beta_timeline_title": "Garis waktu Beta", "advanced_settings_enable_alternate_media_filter_subtitle": "Gunakan opsi ini untuk menyaring media saat sinkronisasi berdasarkan kriteria alternatif. Hanya coba ini dengan aplikasi mendeteksi semua album.", "advanced_settings_enable_alternate_media_filter_title": "[EKSPERIMENTAL] Gunakan saringan sinkronisasi album perangkat alternatif", "advanced_settings_log_level_title": "Tingkat log: {level}", "advanced_settings_prefer_remote_subtitle": "Beberapa perangkat tidak dapat memuat gambar kecil dengan cepat. Menyalakan ini akan memuat gambar kecil dari server.", "advanced_settings_prefer_remote_title": "Prioritaskan gambar dari server", + "advanced_settings_proxy_headers_subtitle": "Tentukan header proxy yang harus dikirim Immich dengan setiap permintaan jaringan", + "advanced_settings_self_signed_ssl_subtitle": "Melewati verifikasi sertifikat SSL untuk titik akhir server. Diperlukan untuk sertifikat yang ditandatangani sendiri.", + "advanced_settings_self_signed_ssl_title": "Izinkan sertifikat SSL yang ditandatangani sendiri", "advanced_settings_sync_remote_deletions_subtitle": "Hapus atau pulihkan aset pada perangkat ini secara otomatis ketika tindakan dilakukan di web", "advanced_settings_sync_remote_deletions_title": "Sinkronisasi penghapusan jarak jauh [EKSPERIMENTAL]", + "advanced_settings_tile_subtitle": "Pengaturan pengguna tingkat lanjut", + "advanced_settings_troubleshooting_subtitle": "Aktifkan fitur tambahan untuk pemecahan masalah", "age_months": "Umur {months, plural, one {# bulan} other {# bulan}}", "age_year_months": "Umur 1 tahun, {months, plural, one {# bulan} other {# bulan}}", "age_years": "{years, plural, other {Umur #}}", @@ -446,7 +470,6 @@ "assets": "Aset", "assets_added_count": "{count, plural, one {# aset} other {# aset}} ditambahkan", "assets_added_to_album_count": "Ditambahkan {count, plural, one {# aset} other {# aset}} ke album", - "assets_added_to_name_count": "Ditambahkan {count, plural, one {# aset} other {# aset}} ke {hasName, select, true {{name}} other {album baru}}", "assets_count": "{count, plural, one {# aset} other {# aset}}", "assets_deleted_permanently": "{count} aset dihapus secara permanen", "assets_deleted_permanently_from_server": "{count} aset dihapus secara permanen dari server Immich", @@ -464,6 +487,7 @@ "authorized_devices": "Perangkat Terautentikasi", "back": "Kembali", "back_close_deselect": "Kembali, tutup, atau batalkan pemilihan", + "backup": "Cadangkan", "backup_album_selection_page_albums_device": "Album di perangkat ({count})", "backup_album_selection_page_albums_tap": "Sentuh untuk memilih, sentuh 2x untuk mengecualikan", "backup_album_selection_page_assets_scatter": "Aset dapat tersebar dalam banyak album. Sehingga album dapat dipilih atau dikecualikan saat proses pencadangan.", @@ -1046,7 +1070,6 @@ "light": "Terang", "like_deleted": "Suka dihapus", "link_motion_video": "Tautan video gerak", - "link_options": "Opsi tautan", "link_to_oauth": "Tautkan ke OAuth", "linked_oauth_account": "Akun OAuth tertaut", "list": "Daftar", @@ -1101,7 +1124,6 @@ "manage_your_devices": "Kelola perangkat Anda yang masuk", "manage_your_oauth_connection": "Kelola koneksi OAuth Anda", "map": "Peta", - "map_assets_in_bound": "{count} foto", "map_assets_in_bounds": "{count} foto", "map_cannot_get_user_location": "Tidak dapat memeroleh lokasi pengguna", "map_location_dialog_yes": "Ya", diff --git a/i18n/it.json b/i18n/it.json index 07e19736a7..49ef0941e1 100644 --- a/i18n/it.json +++ b/i18n/it.json @@ -14,6 +14,7 @@ "add_a_location": "Aggiungi una posizione", "add_a_name": "Aggiungi un nome", "add_a_title": "Aggiungi un titolo", + "add_birthday": "Aggiungi un compleanno", "add_endpoint": "Aggiungi endpoint", "add_exclusion_pattern": "Aggiungi un pattern di esclusione", "add_import_path": "Aggiungi un percorso di importazione", @@ -44,6 +45,13 @@ "backup_database": "Crea Dump Database", "backup_database_enable_description": "Abilita i backup del database", "backup_keep_last_amount": "Numero di backup da mantenere", + "backup_onboarding_1_description": "copia offsite nel cloud o in un'altra sede fisica.", + "backup_onboarding_2_description": "copie locali su diversi dispositivi. Ciò include i file principali e un backup di tali file a livello locale.", + "backup_onboarding_3_description": "copie totali dei tuoi dati, compresi i file originali. Ciò include 1 copia offsite e 2 copie locali.", + "backup_onboarding_description": "Per proteggere i tuoi dati, è consigliato adottare una strategia di backup 3-2-1. Per una soluzione di backup completa, è consigliato conservare copie delle foto/video caricati e del database Immich.", + "backup_onboarding_footer": "Per ulteriori informazioni sul backup di Immich, consultare la documentazione.", + "backup_onboarding_parts_title": "Un backup 3-2-1 include:", + "backup_onboarding_title": "Backup", "backup_settings": "Impostazioni Dump database", "backup_settings_description": "Gestisci le impostazioni dei backup.", "cleared_jobs": "Cancellati i processi per: {job}", @@ -87,7 +95,7 @@ "image_settings": "Impostazioni delle immagini", "image_settings_description": "Gestisci qualità e risoluzione delle immagini generate", "image_thumbnail_description": "Miniatura piccola senza metadati, utilizzata durante la visualizzazione di gruppi di foto come la sequenza temporale principale", - "image_thumbnail_quality_description": "Qualità delle miniature da 1 a 100. Un valore più alto è migliore, ma produce file più grandi e può ridurre la reattività dell'app.", + "image_thumbnail_quality_description": "Qualità delle anteprime da 1 a 100. Un valore più alto è migliore ma produce file più grandi e può ridurre la reattività dell'app.", "image_thumbnail_title": "Impostazioni della copertina", "job_concurrency": "Concorrenza {job}", "job_created": "Processo creato", @@ -105,7 +113,7 @@ "library_scanning_enable_description": "Attiva la scansione periodica della libreria", "library_settings": "Libreria Esterna", "library_settings_description": "Gestisci le impostazioni della libreria esterna", - "library_tasks_description": "Scansiona le librerie esterne per i nuovi aggiornamenti", + "library_tasks_description": "Scansiona le librerie esterne per risorse nuove o modificate", "library_watching_enable_description": "Osserva le librerie esterne per cambiamenti", "library_watching_settings": "Osserva librerie (SPERIMENTALE)", "library_watching_settings_description": "Osserva automaticamente i cambiamenti dei file", @@ -121,7 +129,7 @@ "machine_learning_enabled": "Attiva machine learning", "machine_learning_enabled_description": "Se disabilitato, tutte le funzioni di ML saranno disabilitate ignorando le importazioni sottostanti.", "machine_learning_facial_recognition": "Riconoscimento Facciale", - "machine_learning_facial_recognition_description": "Rileva, riconosci, e raggruppa facce nelle immagini", + "machine_learning_facial_recognition_description": "Rileva, riconosci e raggruppa volti nelle immagini", "machine_learning_facial_recognition_model": "Modello di riconoscimento facciale", "machine_learning_facial_recognition_model_description": "I modelli sono mostrati in ordine decrescente in base alla dimensione. I modelli più grandi sono più lenti e utilizzano più memoria, però producono risultati migliori. Nota che devi ri-eseguire il processo di rilevamento facciale per tutte le immagini quando cambi il modello.", "machine_learning_facial_recognition_setting": "Attiva riconoscimento facciale", @@ -156,16 +164,30 @@ "map_settings": "Impostazioni Mappa e Posizione", "map_settings_description": "Gestisci impostazioni mappa", "map_style_description": "URL per un tema della mappa style.json", - "memory_cleanup_job": "pulizia memoria", - "memory_generate_job": "Generazione della memoria", + "memory_cleanup_job": "Pulizia dei vecchi Ricordi", + "memory_generate_job": "Generazione dei Ricordi", "metadata_extraction_job": "Estrazione Metadata", - "metadata_extraction_job_description": "Estrai informazioni dai metadati di ciascun asset, ad esempio coordinate GPS, volti e risoluzione", + "metadata_extraction_job_description": "Estrai informazioni dai metadati di ciascuna risorsa, come coordinate GPS, volti e risoluzione", "metadata_faces_import_setting": "Abilita l'importazione dei volti", "metadata_faces_import_setting_description": "Importa i volti dai dati EXIF dell'immagine e dai file sidecar", "metadata_settings": "Impostazioni Metadati", "metadata_settings_description": "Gestisci le impostazioni dei metadati", "migration_job": "Migrazione", "migration_job_description": "Migra le anteprime per gli asset e volti alla struttura di cartelle più recente", + "nightly_tasks_cluster_faces_setting_description": "Avvia riconoscimento facciale sui volti appena rilevati", + "nightly_tasks_cluster_new_faces_setting": "Raggruppa nuovi volti", + "nightly_tasks_database_cleanup_setting": "Processi di pulizia del database", + "nightly_tasks_database_cleanup_setting_description": "Ripulisci il database da file vecchi e scaduti", + "nightly_tasks_generate_memories_setting": "Genera ricordi", + "nightly_tasks_generate_memories_setting_description": "Genera nuovi ricordi a partire dalle risorse", + "nightly_tasks_missing_thumbnails_setting": "Genera anteprime mancanti", + "nightly_tasks_missing_thumbnails_setting_description": "Metti in coda le risorse senza miniatura per la generazione delle anteprime", + "nightly_tasks_settings": "Impostazioni delle attività notturne", + "nightly_tasks_settings_description": "Gestisci attività notturne", + "nightly_tasks_start_time_setting": "Tempo di avvio", + "nightly_tasks_start_time_setting_description": "Il tempo in cui il server fa partire le attività notturne", + "nightly_tasks_sync_quota_usage_setting": "Sincronizza la quota di utilizzo", + "nightly_tasks_sync_quota_usage_setting_description": "Aggiorna la quota di spazio dell'utente in base all'utilizzo corrente", "no_paths_added": "Nessun percorso aggiunto", "no_pattern_added": "Nessun pattern aggiunto", "note_apply_storage_label_previous_assets": "Nota: Per assegnare l'etichetta storage ad asset precedentemente caricati, esegui", @@ -196,6 +218,8 @@ "oauth_mobile_redirect_uri": "URI reindirizzamento mobile", "oauth_mobile_redirect_uri_override": "Sovrascrivi URI reindirizzamento cellulare", "oauth_mobile_redirect_uri_override_description": "Abilita quando il gestore OAuth non consente un URL come ''{callback}''", + "oauth_role_claim": "Claim del ruolo", + "oauth_role_claim_description": "Concedi automaticamente l'accesso come amministratore in base alla presenza di questo claim. Il claim può essere 'user' o 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Gestisci impostazioni di login OAuth", "oauth_settings_more_details": "Per più dettagli riguardo a questa funzionalità, consulta la documentazione.", @@ -264,8 +288,8 @@ "theme_custom_css_settings_description": "I Cascading Style Sheets (CSS) permettono di personalizzare l'interfaccia di Immich.", "theme_settings": "Impostazioni Tema", "theme_settings_description": "Gestisci la personalizzazione dell'interfaccia web di Immich", - "thumbnail_generation_job": "Generazione Miniature", - "thumbnail_generation_job_description": "Genera miniature grandi, piccole e sfocate per ogni asset, oltre a miniature per ogni persona", + "thumbnail_generation_job": "Genera Anteprime", + "thumbnail_generation_job_description": "Genera anteprime grandi, piccole e sfocate per ogni asset, oltre a miniature per ogni persona", "transcoding_acceleration_api": "API di accelerazione", "transcoding_acceleration_api_description": "L'API che interagirà con il tuo dispositivo per accelerare la transcodifica. Questa impostazione è \"best effort\": ripiegherà sulla transcodifica software in caso di fallimento. VP9 potrebbe funzionare o meno a seconda del tuo hardware.", "transcoding_acceleration_nvenc": "NVENC (richiede GPU NVIDIA)", @@ -357,28 +381,31 @@ "admin_password": "Password Amministratore", "administration": "Amministrazione", "advanced": "Avanzate", + "advanced_settings_beta_timeline_subtitle": "Prova la nuova esperienza dell'app", + "advanced_settings_beta_timeline_title": "Timeline beta", "advanced_settings_enable_alternate_media_filter_subtitle": "Usa questa opzione per filtrare i contenuti multimediali durante la sincronizzazione in base a criteri alternativi. Prova questa opzione solo se riscontri problemi con il rilevamento di tutti gli album da parte dell'app.", "advanced_settings_enable_alternate_media_filter_title": "[SPERIMENTALE] Usa un filtro alternativo per la sincronizzazione degli album del dispositivo", "advanced_settings_log_level_title": "Livello log: {level}", - "advanced_settings_prefer_remote_subtitle": "Alcuni dispositivi sono molto lenti a caricare le anteprime delle immagini dal dispositivo. Attivare questa impostazione per caricare invece le immagini remote.", + "advanced_settings_prefer_remote_subtitle": "Alcuni dispositivi sono molto lenti a caricare le anteprime delle immagini locali. Attivare questa impostazione per caricare invece le immagini remote.", "advanced_settings_prefer_remote_title": "Preferisci immagini remote", "advanced_settings_proxy_headers_subtitle": "Definisci gli header per i proxy che Immich dovrebbe inviare con ogni richiesta di rete", "advanced_settings_proxy_headers_title": "Header Proxy", "advanced_settings_self_signed_ssl_subtitle": "Salta la verifica dei certificati SSL del server. Richiesto con l'uso di certificati self-signed.", "advanced_settings_self_signed_ssl_title": "Consenti certificati SSL self-signed", - "advanced_settings_sync_remote_deletions_subtitle": "Rimuovi o ripristina automaticamente un elemento su questo dispositivo se l'azione è stata fatta via web", + "advanced_settings_sync_remote_deletions_subtitle": "Rimuovi o ripristina automaticamente un elemento su questo dispositivo quando l'azione è stata fatta via web", "advanced_settings_sync_remote_deletions_title": "Sincronizza le cancellazioni remote [SPERIMENTALE]", - "advanced_settings_tile_subtitle": "Impostazioni aggiuntive utenti", + "advanced_settings_tile_subtitle": "Impostazioni avanzate dell'utente", "advanced_settings_troubleshooting_subtitle": "Attiva funzioni addizionali per la risoluzione dei problemi", "advanced_settings_troubleshooting_title": "Risoluzione problemi", "age_months": "Età {months, plural, one {# mese} other {# mesi}}", "age_year_months": "Età 1 anno, {months, plural, one {# mese} other {# mesi}}", "age_years": "{years, plural, one {# anno} other {# anni}}", "album_added": "Album aggiunto", - "album_added_notification_setting_description": "Ricevi una notifica email quando sei aggiunto a un album condiviso", + "album_added_notification_setting_description": "Ricevi una notifica email quando sei aggiunto ad un album condiviso", "album_cover_updated": "Copertina dell'album aggiornata", "album_delete_confirmation": "Sei sicuro di voler cancellare l'album {album}?", "album_delete_confirmation_description": "Se l'album è condiviso gli altri utenti perderanno l'accesso.", + "album_deleted": "Album eliminato", "album_info_card_backup_album_excluded": "ESCLUSI", "album_info_card_backup_album_included": "INCLUSI", "album_info_updated": "Informazioni dell'album aggiornate", @@ -388,38 +415,40 @@ "album_options": "Impostazioni Album", "album_remove_user": "Rimuovi l'utente?", "album_remove_user_confirmation": "Sicuro di voler rimuovere l'utente {user}?", + "album_search_not_found": "Nessun album trovato corrispondente alla tua ricerca", "album_share_no_users": "Sembra che tu abbia condiviso questo album con tutti gli utenti oppure non hai nessun utente con cui condividere.", "album_updated": "Album aggiornato", "album_updated_setting_description": "Ricevi una notifica email quando un album condiviso ha nuovi media", "album_user_left": "{album} abbandonato", "album_user_removed": "Utente {user} rimosso", "album_viewer_appbar_delete_confirm": "Sei sicuro di voler rimuovere questo album dal tuo account?", - "album_viewer_appbar_share_err_delete": "Impossibile eliminare l'album", - "album_viewer_appbar_share_err_leave": "Impossibile lasciare l'album", - "album_viewer_appbar_share_err_remove": "Ci sono problemi nel rimuovere oggetti dall'album", - "album_viewer_appbar_share_err_title": "Impossibile cambiare il titolo dell'album", + "album_viewer_appbar_share_err_delete": "Non è stato possibile eliminare l'album", + "album_viewer_appbar_share_err_leave": "Non è stato possibile lasciare l'album", + "album_viewer_appbar_share_err_remove": "Ci sono problemi nel rimuovere elementi dall'album", + "album_viewer_appbar_share_err_title": "Non è stato possibile cambiare il titolo dell'album", "album_viewer_appbar_share_leave": "Lascia album", "album_viewer_appbar_share_to": "Condividi a", "album_viewer_page_share_add_users": "Aggiungi utenti", "album_with_link_access": "Permetti a chiunque possieda il link di visualizzare le foto e le persone dell'album.", "albums": "Album", "albums_count": "{count, plural, one {{count, number} Album} other {{count, number} Album}}", - "albums_default_sort_order": "Ordinamento album predefinito", - "albums_default_sort_order_description": "Ordine iniziale degli asset alla creazione di nuovi album.", - "albums_feature_description": "Collezione di asset che possono essere condivisi con altri utenti.", + "albums_default_sort_order": "Ordinamento predefinito degli album", + "albums_default_sort_order_description": "Ordine iniziale degli elementi alla creazione di nuovi album.", + "albums_feature_description": "Raggruppamento di elementi che possono essere condivisi con altri utenti.", + "albums_on_device_count": "Album sul dispositivo ({count})", "all": "Tutti", "all_albums": "Tutti gli album", "all_people": "Tutte le persone", "all_videos": "Tutti i video", "allow_dark_mode": "Permetti Tema Scuro", - "allow_edits": "Permetti Modifiche", + "allow_edits": "Permetti modifiche", "allow_public_user_to_download": "Permetti agli utenti pubblici di scaricare", "allow_public_user_to_upload": "Permetti agli utenti pubblici di caricare", "alt_text_qr_code": "Immagine QR", "anti_clockwise": "Senso anti-orario", "api_key": "Chiave API", - "api_key_description": "Il valore verrà mostrato solo una volta. Assicurati di copiarlo prima di chiudere la finestra.", - "api_key_empty": "Il nome della chiave API non può essere vuoto", + "api_key_description": "Questo valore verrà mostrato una sola volta. Assicurati di copiarlo prima di chiudere la finestra.", + "api_key_empty": "Il nome della chiave API non dovrebbe essere vuoto", "api_keys": "Chiavi API", "app_bar_signout_dialog_content": "Sei sicuro di volerti disconnettere?", "app_bar_signout_dialog_ok": "Si", @@ -427,8 +456,9 @@ "app_settings": "Impostazioni Applicazione", "appears_in": "Compare in", "archive": "Archivio", + "archive_action_prompt": "Aggiunti {count} elementi all'Archivio", "archive_or_unarchive_photo": "Archivia o ripristina foto", - "archive_page_no_archived_assets": "Nessuna oggetto archiviato", + "archive_page_no_archived_assets": "Non è stato trovato nessun elemento archiviato", "archive_page_title": "Archivio ({count})", "archive_size": "Dimensioni Archivio", "archive_size_description": "Imposta le dimensioni dell'archivio per i download (in GiB)", @@ -440,42 +470,41 @@ "asset_action_share_err_offline": "Non è possibile recuperare le risorse offline, azione ignorata", "asset_added_to_album": "Aggiunto all'album", "asset_adding_to_album": "Aggiungendo all'album…", - "asset_description_updated": "La descrizione del media è stata aggiornata", + "asset_description_updated": "La descrizione dell'elemento è stata aggiornata", "asset_filename_is_offline": "Il media {filename} è offline", "asset_has_unassigned_faces": "Il media ha dei volti non categorizzati", "asset_hashing": "Hashing in corso …", "asset_list_group_by_sub_title": "Raggruppa per", "asset_list_layout_settings_dynamic_layout_title": "Layout dinamico", "asset_list_layout_settings_group_automatically": "Automatico", - "asset_list_layout_settings_group_by": "Raggruppa le risorse per", + "asset_list_layout_settings_group_by": "Raggruppa gli elementi per", "asset_list_layout_settings_group_by_month_day": "Mese + giorno", "asset_list_layout_sub_title": "Layout", - "asset_list_settings_subtitle": "Impostazion del layout della griglia delle foto", + "asset_list_settings_subtitle": "Impostazioni del layout della griglia delle foto", "asset_list_settings_title": "Griglia foto", - "asset_offline": "Risorsa Offline", - "asset_offline_description": "Questo media non è stato trovato nel disco. Contatta il tuo amministratore di Immich per assistenza.", - "asset_restored_successfully": "Asset ripristinato con successo", + "asset_offline": "Elemento Offline", + "asset_offline_description": "Questo elemento esterno non viene più trovato sul disco. Contatta il tuo amministratore di Immich per assistenza.", + "asset_restored_successfully": "Elemento ripristinato con successo", "asset_skipped": "Saltato", "asset_skipped_in_trash": "Nel cestino", "asset_uploaded": "Caricato", "asset_uploading": "Caricamento…", - "asset_viewer_settings_subtitle": "Gestisci le impostazioni del visualizzatore risorse", + "asset_viewer_settings_subtitle": "Gestisci le impostazioni del visualizzatore della galleria", "asset_viewer_settings_title": "Visualizzazione risorse", "assets": "Risorse", "assets_added_count": "{count, plural, one {# asset aggiunto} other {# asset aggiunti}}", "assets_added_to_album_count": "{count, plural, one {# asset aggiunto} other {# asset aggiunti}} all'album", - "assets_added_to_name_count": "Aggiunti {count, plural, one {# asset} other {# assets}} a {hasName, select, true {{name}} other {new album}}", - "assets_cannot_be_added_to_album_count": "{count, plural, one {L'asset} other {Gli asset}} non possono essere aggiunti all'album", - "assets_count": "{count, plural, other {# asset}}", + "assets_cannot_be_added_to_album_count": "{count, plural, one {L'elemento} other {Gli elementi}} non possono essere aggiunti all'album", + "assets_count": "{count, plural, one {# elemento} other {# elementi}}", "assets_deleted_permanently": "{count} elementi cancellati definitivamente", "assets_deleted_permanently_from_server": "{count} elementi cancellati definitivamente dal server Immich", - "assets_downloaded_failed": "{count, plural, one {Scaricato # file - {error} file non riusciti} other {Scaricati # file - {error} file non riusciti}}", + "assets_downloaded_failed": "{count, plural, one {Scaricato # file - {error} file non riuscito} other {Scaricati # file - {error} file non riusciti}}", "assets_downloaded_successfully": "{count, plural, one {Scaricato # file con successo} other {Scaricati # file con successo}}", - "assets_moved_to_trash_count": "{count, plural, one {# asset spostato} other {# asset spostati}} nel cestino", + "assets_moved_to_trash_count": "{count, plural, one {# elemento spostato} other {# elementi spostati}} nel cestino", "assets_permanently_deleted_count": "{count, plural, one {# asset cancellato} other {# asset cancellati}} definitivamente", "assets_removed_count": "{count, plural, one {# asset rimosso} other {# asset rimossi}}", "assets_removed_permanently_from_device": "{count} elementi cancellati definitivamente dal tuo dispositivo", - "assets_restore_confirmation": "Sei sicuro di voler ripristinare tutti gli asset cancellati? Non puoi annullare questa azione! Tieni presente che eventuali risorse offline NON possono essere ripristinate in questo modo.", + "assets_restore_confirmation": "Sei sicuro di voler ripristinare tutti gli elementi cancellati? Non puoi annullare questa azione! Tieni presente che eventuali risorse offline NON possono essere ripristinate in questo modo.", "assets_restored_count": "{count, plural, one {# asset ripristinato} other {# asset ripristinati}}", "assets_restored_successfully": "{count} elementi ripristinati", "assets_trashed": "{count} elementi cestinati", @@ -490,6 +519,7 @@ "back_close_deselect": "Indietro, chiudi o deseleziona", "background_location_permission": "Permesso di localizzazione in background", "background_location_permission_content": "Per fare in modo che sia possibile cambiare rete quando è in esecuzione in background, Immich deve *sempre* avere accesso alla tua posizione precisa in modo da poter leggere il nome della rete Wi-Fi", + "backup": "Backup", "backup_album_selection_page_albums_device": "Album sul dispositivo ({count})", "backup_album_selection_page_albums_tap": "Tap per includere, doppio tap per escludere", "backup_album_selection_page_assets_scatter": "Visto che le risorse possono trovarsi in più album, questi possono essere inclusi o esclusi dal backup.", @@ -497,7 +527,7 @@ "backup_album_selection_page_selection_info": "Informazioni sulla selezione", "backup_album_selection_page_total_assets": "Numero totale delle risorse", "backup_all": "Tutti", - "backup_background_service_backup_failed_message": "Impossibile caricare i contenuti. Riprovo…", + "backup_background_service_backup_failed_message": "È stato impossibile fare il backup dei contenuti. Riprovo…", "backup_background_service_connection_failed_message": "Impossibile connettersi al server. Riprovo…", "backup_background_service_current_upload_notification": "Caricamento di {filename} in corso", "backup_background_service_default_notification": "Ricerca di nuovi contenuti…", @@ -506,7 +536,7 @@ "backup_background_service_upload_failure_notification": "Impossibile caricare {filename}", "backup_controller_page_albums": "Backup Album", "backup_controller_page_background_app_refresh_disabled_content": "Attiva l'aggiornamento dell'app in background in Impostazioni > Generale > Aggiorna app in background per utilizzare backup in background.", - "backup_controller_page_background_app_refresh_disabled_title": "Backup in background è disattivo", + "backup_controller_page_background_app_refresh_disabled_title": "Aggiornamento dell'app in background disattivo", "backup_controller_page_background_app_refresh_enable_button_text": "Vai alle impostazioni", "backup_controller_page_background_battery_info_link": "Mostrami come", "backup_controller_page_background_battery_info_message": "Per una migliore esperienza di backup, disabilita le ottimizzazioni della batteria per l'app Immich.\n\nDal momento che è una funzionalità specifica del dispositivo, per favore consulta il manuale del produttore.", @@ -515,12 +545,12 @@ "backup_controller_page_background_charging": "Solo durante la ricarica", "backup_controller_page_background_configure_error": "Impossibile configurare i servizi in background", "backup_controller_page_background_delay": "Ritarda il backup di nuovi elementi: {duration}", - "backup_controller_page_background_description": "Abilita i servizi in background per fare il backup di tutti i nuovi contenuti senza la necessità di aprire l'app", - "backup_controller_page_background_is_off": "Backup automatico disattivato", - "backup_controller_page_background_is_on": "Backup automatico attivo", + "backup_controller_page_background_description": "Abilita i servizi in background per fare il backup di nuovi contenuti senza la necessità di aprire l'app", + "backup_controller_page_background_is_off": "Backup automatico in background disattivato", + "backup_controller_page_background_is_on": "Backup automatico in background attivo", "backup_controller_page_background_turn_off": "Disabilita servizi in background", "backup_controller_page_background_turn_on": "Abilita servizi in background", - "backup_controller_page_background_wifi": "Solo Wi-Fi", + "backup_controller_page_background_wifi": "Solo con Wi-Fi", "backup_controller_page_backup": "Backup", "backup_controller_page_backup_selected": "Selezionati: ", "backup_controller_page_backup_sub": "Foto e video caricati", @@ -553,6 +583,8 @@ "backup_options_page_title": "Opzioni di Backup", "backup_setting_subtitle": "Gestisci le impostazioni di upload in primo piano e in background", "backward": "Indietro", + "beta_sync": "Status sincronizzazione beta", + "beta_sync_subtitle": "Gestisci il nuovo sistema di sincronizzazione", "biometric_auth_enabled": "Autenticazione biometrica attivata", "biometric_locked_out": "Sei stato bloccato dall'autenticazione biometrica", "biometric_no_options": "Nessuna opzione biometrica disponibile", @@ -587,6 +619,7 @@ "cancel": "Annulla", "cancel_search": "Annulla ricerca", "canceled": "Annullato", + "canceling": "Annullamento", "cannot_merge_people": "Impossibile unire le persone", "cannot_undo_this_action": "Non puoi annullare questa azione!", "cannot_update_the_description": "Impossibile aggiornare la descrizione", @@ -700,10 +733,11 @@ "current_server_address": "Indirizzo del server in uso", "custom_locale": "Localizzazione personalizzata", "custom_locale_description": "Formatta data e numeri in base alla lingua e al paese", + "custom_url": "URL personalizzato", "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Scuro", - "darkTheme": "Attiva/Disattiva tema scuro", + "dark_theme": "Imposta tema scuro", "date_after": "Data dopo", "date_and_time": "Data e ora", "date_before": "Data prima", @@ -719,6 +753,8 @@ "default_locale": "Localizzazione preimpostata", "default_locale_description": "Formatta la data e i numeri in base alle impostazioni del tuo browser", "delete": "Elimina", + "delete_action_confirmation_message": "Vuoi davvero eliminare questo asset? Questa azione sposterà l'asset nel cestino del server e ti chiederà se desideri eliminarla localmente", + "delete_action_prompt": "{count} elementi eliminati", "delete_album": "Elimina album", "delete_api_key_prompt": "Sei sicuro di voler eliminare questa chiave API?", "delete_dialog_alert": "Questi oggetti saranno eliminati definitivamente da Immich e dal tuo device", @@ -732,9 +768,12 @@ "delete_key": "Elimina chiave", "delete_library": "Elimina libreria", "delete_link": "Elimina link", + "delete_local_action_prompt": "{count} elementi rimossi in locale", "delete_local_dialog_ok_backed_up_only": "Elimina solo con backup", "delete_local_dialog_ok_force": "Elimina comunque", "delete_others": "Elimina gli altri", + "delete_permanently": "Elimina definitivamente", + "delete_permanently_action_prompt": "{count} eliminati definitivamente", "delete_shared_link": "Elimina link condiviso", "delete_shared_link_dialog_title": "Elimina link condiviso", "delete_tag": "Elimina tag", @@ -745,6 +784,7 @@ "description": "Descrizione", "description_input_hint_text": "Aggiungi descrizione...", "description_input_submit_error": "Errore modificare descrizione, controlli I log per maggiori dettagli", + "deselect_all": "Deseleziona Tutto", "details": "Dettagli", "direction": "Direzione", "disabled": "Disabilitato", @@ -762,6 +802,7 @@ "documentation": "Documentazione", "done": "Fatto", "download": "Scarica", + "download_action_prompt": "Scaricando {count} elementi", "download_canceled": "Download annullato", "download_complete": "Download completato", "download_enqueue": "Download in coda", @@ -788,6 +829,7 @@ "edit": "Modifica", "edit_album": "Modifica album", "edit_avatar": "Modifica avatar", + "edit_birthday": "Modifica Compleanno", "edit_date": "Modifica data", "edit_date_and_time": "Modifica data e ora", "edit_description": "Modifica la descrizione", @@ -799,6 +841,7 @@ "edit_key": "Modifica chiave", "edit_link": "Modifica link", "edit_location": "Modifica posizione", + "edit_location_action_prompt": "{count} luoghi modificati", "edit_location_dialog_title": "Posizione", "edit_name": "Modifica nome", "edit_people": "Modifica persone", @@ -817,6 +860,7 @@ "empty_trash": "Svuota cestino", "empty_trash_confirmation": "Sei sicuro di volere svuotare il cestino? Questo rimuoverà tutte le risorse nel cestino in modo permanente da Immich.\nNon puoi annullare questa azione!", "enable": "Abilita", + "enable_backup": "Abilita Backup", "enable_biometric_auth_description": "Inserire il codice PIN per abilitare l'autenticazione biometrica", "enabled": "Abilitato", "end_date": "Data Fine", @@ -953,6 +997,7 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "Aggiungi una descrizione...", + "exif_bottom_sheet_description_error": "Errore durante l'aggiornamento della descrizione", "exif_bottom_sheet_details": "DETTAGLI", "exif_bottom_sheet_location": "POSIZIONE", "exif_bottom_sheet_people": "PERSONE", @@ -973,6 +1018,8 @@ "explorer": "Esplora", "export": "Esporta", "export_as_json": "Esporta come JSON", + "export_database": "Esporta database", + "export_database_description": "Esporta il database SQLite", "extension": "Estensione", "external": "Esterno", "external_libraries": "Librerie esterne", @@ -984,6 +1031,7 @@ "failed_to_load_assets": "Impossibile caricare gli asset", "failed_to_load_folder": "Impossibile caricare la cartella", "favorite": "Preferito", + "favorite_action_prompt": "{count} elementi aggiunti ai preferiti", "favorite_or_unfavorite_photo": "Aggiungi o rimuovi foto da preferiti", "favorites": "Preferiti", "favorites_page_no_favorites": "Nessun preferito", @@ -1023,6 +1071,9 @@ "haptic_feedback_switch": "Abilita feedback aptico", "haptic_feedback_title": "Feedback aptico", "has_quota": "Ha limite", + "hash_asset": "Risorsa hash", + "hashed_assets": "Risorse hash", + "hashing": "Hashing", "header_settings_add_header_tip": "Aggiungi Header", "header_settings_field_validator_msg": "Il valore non può essere vuoto", "header_settings_header_name_input": "Nome header", @@ -1055,6 +1106,7 @@ "host": "Host", "hour": "Ora", "id": "ID", + "idle": "Inattivo", "ignore_icloud_photos": "Ignora foto iCloud", "ignore_icloud_photos_description": "Le foto che sono memorizzate su iCloud non verranno caricate sul server Immich", "image": "Immagine", @@ -1112,6 +1164,7 @@ "language_no_results_title": "Linguaggi non trovati", "language_search_hint": "Cerca linguaggi...", "language_setting_description": "Seleziona la tua lingua predefinita", + "large_files": "File pesanti", "last_seen": "Ultimo accesso", "latest_version": "Ultima Versione", "latitude": "Latitudine", @@ -1127,16 +1180,18 @@ "library_page_sort_created": "Data di creazione", "library_page_sort_last_modified": "Ultima modifica", "library_page_sort_title": "Titolo album", + "licenses": "Licenze", "light": "Chiaro", "like_deleted": "Mi piace rimosso", "link_motion_video": "Collega video in movimento", - "link_options": "Impostazioni Collegamento", "link_to_oauth": "Collegamento a OAuth", "linked_oauth_account": "Account OAuth collegato", "list": "Lista", "loading": "Caricamento", "loading_search_results_failed": "Impossibile caricare i risultati della ricerca", + "local": "Locale", "local_asset_cast_failed": "Impossibile trasmettere una risorsa che non è caricata sul server", + "local_assets": "Risorsa locale", "local_network": "Rete locale", "local_network_sheet_info": "L'app si collegherà al server tramite questo URL quando è in uso la rete Wi-Fi specificata", "location_permission": "Permesso di localizzazione", @@ -1193,8 +1248,7 @@ "manage_your_devices": "Gestisci i tuoi dispositivi collegati", "manage_your_oauth_connection": "Gestisci la tua connessione OAuth", "map": "Mappa", - "map_assets_in_bound": "{count} foto", - "map_assets_in_bounds": "{count} foto", + "map_assets_in_bounds": "{count, plural, one {# foto} other {# foto}}", "map_cannot_get_user_location": "Non è possibile ottenere la posizione dell'utente", "map_location_dialog_yes": "Si", "map_location_picker_page_use_location": "Usa questa posizione", @@ -1246,6 +1300,7 @@ "more": "Di più", "move": "Sposta", "move_off_locked_folder": "Sposta al di fuori della cartella privata", + "move_to_lock_folder_action_prompt": "{count} elementi aggiunti alla cartella sicura", "move_to_locked_folder": "Sposta nella cartella privata", "move_to_locked_folder_confirmation": "Queste foto e video verranno rimossi da tutti gli album, e saranno visibili solo dalla cartella privata", "moved_to_archive": "Spostati {count, plural, one {# asset} other {# assets}} nell'archivio", @@ -1292,6 +1347,7 @@ "no_results": "Nessun risultato", "no_results_description": "Prova ad usare un sinonimo oppure una parola chiave più generica", "no_shared_albums_message": "Crea un album per condividere foto e video con le persone nella tua rete", + "no_uploads_in_progress": "Nessun upload in corso", "not_in_any_album": "In nessun album", "not_selected": "Non selezionato", "note_apply_storage_label_to_previously_uploaded assets": "Nota: Per aggiungere l'etichetta dell'archiviazione agli asset caricati in precedenza, esegui", @@ -1329,6 +1385,7 @@ "original": "originale", "other": "Altro", "other_devices": "Altri dispositivi", + "other_entities": "Altre entità", "other_variables": "Altre variabili", "owned": "Posseduti", "owner": "Proprietario", @@ -1460,6 +1517,7 @@ "purchase_server_description_2": "Stato di Contributore", "purchase_server_title": "Server", "purchase_settings_server_activated": "La chiave del prodotto del server è gestita dall'amministratore", + "queue_status": "Messi in coda {count}/{total}", "rating": "Valutazione a stelle", "rating_clear": "Crea valutazione", "rating_count": "{count, plural, one {# stella} other {# stelle}}", @@ -1479,15 +1537,17 @@ "recently_taken_page_title": "Scattate di Recente", "refresh": "Aggiorna", "refresh_encoded_videos": "Ricarica video codificati", - "refresh_faces": "Aggiorna facce", + "refresh_faces": "Aggiorna volti", "refresh_metadata": "Ricarica metadati", "refresh_thumbnails": "Ricarica anteprime", "refreshed": "Aggiornato", "refreshes_every_file": "Rilegge tutti i file esistenti e nuovi", "refreshing_encoded_video": "Ricaricando il video codificato", - "refreshing_faces": "Aggiorna Facce", + "refreshing_faces": "Aggiornando volti", "refreshing_metadata": "Ricaricando i metadati", "regenerating_thumbnails": "Rigenerando le anteprime", + "remote": "Remoto", + "remote_assets": "Risorse remote", "remove": "Rimuovi", "remove_assets_album_confirmation": "Sei sicuro di voler rimuovere {count, plural, one {# asset} other {# asset}} dall'album?", "remove_assets_shared_link_confirmation": "Sei sicuro di voler rimuovere {count, plural, one {# asset} other {# asset}} da questo link condiviso?", @@ -1495,7 +1555,9 @@ "remove_custom_date_range": "Rimuovi intervallo data personalizzato", "remove_deleted_assets": "Rimuovi file offline", "remove_from_album": "Rimuovere dall'album", + "remove_from_album_action_prompt": "{count} elementi rimossi dall'album", "remove_from_favorites": "Rimuovi dai preferiti", + "remove_from_lock_folder_action_prompt": "{count} elementi rimossi dalla cartella sicura", "remove_from_locked_folder": "Rimuovi dalla cartella privata", "remove_from_locked_folder_confirmation": "Sei sicuro di voler spostare queste foto e questi video dalla cartella privata? Diventeranno visibili nella vostra libreria.", "remove_from_shared_link": "Rimuovi dal link condiviso", @@ -1523,19 +1585,25 @@ "reset_password": "Ripristina password", "reset_people_visibility": "Ripristina visibilità persone", "reset_pin_code": "Resetta il codice PIN", + "reset_sqlite": "Resetta Database SQLite", + "reset_sqlite_confirmation": "Vuoi davvero reimpostare il database SQLite? Dovrai disconnetterti e riconnetterti per risincronizzare i dati", + "reset_sqlite_success": "Database SQLite reimpostato correttamente", "reset_to_default": "Ripristina i valori predefiniti", "resolve_duplicates": "Risolvi duplicati", "resolved_all_duplicates": "Tutti i duplicati sono stati risolti", "restore": "Ripristina", "restore_all": "Ripristina tutto", + "restore_trash_action_prompt": "{count} ripristinati dal cestino", "restore_user": "Ripristina utente", "restored_asset": "Asset ripristinato", "resume": "Riprendi", "retry_upload": "Riprova caricamento", "review_duplicates": "Esamina duplicati", + "review_large_files": "Revisiona file pesanti", "role": "Ruolo", "role_editor": "Editor", "role_viewer": "Visualizzatore", + "running": "In esecuzione", "save": "Salva", "save_to_gallery": "Salva in galleria", "saved_api_key": "Chiave API salvata", @@ -1667,6 +1735,7 @@ "settings_saved": "Impostazioni salvate", "setup_pin_code": "Configura un codice PIN", "share": "Condivisione", + "share_action_prompt": "Condivisi {count} elementi", "share_add_photos": "Aggiungi foto", "share_assets_selected": "{count} selezionati", "share_dialog_preparing": "Preparo…", @@ -1688,6 +1757,7 @@ "shared_link_clipboard_copied_massage": "Copiato negli appunti", "shared_link_clipboard_text": "Link: {link}\nPassword: {password}", "shared_link_create_error": "Si è verificato un errore durante la creazione del link condiviso", + "shared_link_custom_url_description": "Accedi a questo link con un URL personalizzato", "shared_link_edit_description_hint": "Inserisci la descrizione della condivisione", "shared_link_edit_expire_after_option_day": "1 giorno", "shared_link_edit_expire_after_option_days": "{count} giorni", @@ -1713,6 +1783,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Gestisci link condivisi", "shared_link_options": "Opzioni link condiviso", + "shared_link_password_description": "Imposta una password per questo link", "shared_links": "Link condivisi", "shared_links_description": "Condividi foto e video con un link", "shared_photos_and_videos_count": "{assetCount, plural, other {# foto & video condivisi.}}", @@ -1768,6 +1839,7 @@ "sort_title": "Titolo", "source": "Fonte", "stack": "Raggruppa", + "stack_action_prompt": "{count} elementi raggruppati", "stack_duplicates": "Raggruppa i duplicati", "stack_select_one_photo": "Seleziona una foto principale per il gruppo", "stack_selected_photos": "Impila foto selezionate", @@ -1787,6 +1859,7 @@ "storage_quota": "Limite Archiviazione", "storage_usage": "{used} di {available} utilizzati", "submit": "Invia", + "success": "Successo", "suggestions": "Suggerimenti", "sunrise_on_the_beach": "Tramonto sulla spiaggia", "support": "Supporto", @@ -1796,6 +1869,8 @@ "sync": "Sincronizza", "sync_albums": "Sincronizza album", "sync_albums_manual_subtitle": "Sincronizza tutti i video e le foto caricate sull'album di backup selezionato", + "sync_local": "Sincronizza gli elementi locali", + "sync_remote": "Sincronizza gli elementi remoti", "sync_upload_album_setting_subtitle": "Crea e carica le tue foto e video sull'album selezionato in Immich", "tag": "Tag", "tag_assets": "Tagga risorse", @@ -1806,6 +1881,7 @@ "tag_updated": "Tag {tag} aggiornata", "tagged_assets": "{count, plural, one {# asset etichettato} other {# asset etichettati}}", "tags": "Tag", + "tap_to_run_job": "Tocca per eseguire l'attività", "template": "Modello", "theme": "Tema", "theme_selection": "Selezione tema", @@ -1838,6 +1914,7 @@ "total": "Totale", "total_usage": "Utilizzo totale", "trash": "Cestino", + "trash_action_prompt": "{count} elementi spostati nel cestino", "trash_all": "Cestina Tutto", "trash_count": "Cancella {count, number}", "trash_delete_asset": "Cestina/Cancella Asset", @@ -1855,9 +1932,11 @@ "unable_to_change_pin_code": "Impossibile cambiare il codice PIN", "unable_to_setup_pin_code": "Impossibile configurare il codice PIN", "unarchive": "Annulla l'archiviazione", + "unarchive_action_prompt": "{count} elementi rimossi dall'Archivio", "unarchived_count": "{count, plural, other {Non archiviati #}}", "undo": "Annulla", "unfavorite": "Rimuovi preferito", + "unfavorite_action_prompt": "{count} rimossi dai Favoriti", "unhide_person": "Mostra persona", "unknown": "Sconosciuto", "unknown_country": "Paese sconosciuto", @@ -1875,15 +1954,20 @@ "unselect_all_duplicates": "Deseleziona tutti i duplicati", "unselect_all_in": "Deseleziona tutto in {group}", "unstack": "Rimuovi dal gruppo", + "unstack_action_prompt": "{count} rimossi", "unstacked_assets_count": "{count, plural, one {Separato # asset} other {Separati # asset}}", + "untagged": "Senza tag", "up_next": "Prossimo", "updated_at": "Aggiornato il", "updated_password": "Password aggiornata", "upload": "Carica", + "upload_action_prompt": "{count} accodati per l'upload", "upload_concurrency": "Caricamenti contemporanei", + "upload_details": "Dettagli di caricamento", "upload_dialog_info": "Vuoi fare il backup sul server delle risorse selezionate?", "upload_dialog_title": "Carica file", "upload_errors": "Caricamento completato con {count, plural, one {# errore} other {# errori}}, ricarica la pagina per vedere gli asset caricati.", + "upload_finished": "Upload terminato", "upload_progress": "Rimanenti {remaining, number} - Processati {processed, number}/{total, number}", "upload_skipped_duplicates": "{count, plural, one {Ignorato # asset duplicato} other {Ignorati # asset duplicati}}", "upload_status_duplicates": "Duplicati", @@ -1892,6 +1976,7 @@ "upload_success": "Caricamento completato con successo, aggiorna la pagina per vedere i nuovi asset caricati.", "upload_to_immich": "Carica su Immich ({count})", "uploading": "Caricamento", + "uploading_media": "Caricando i media", "url": "URL", "usage": "Utilizzo", "use_biometric": "Usa biometrica", @@ -1912,6 +1997,7 @@ "user_usage_stats_description": "Consulta le statistiche d'uso dell'account", "username": "Nome utente", "users": "Utenti", + "users_added_to_album_count": "Aggiunti {count, plural, one {# utente} other {# utenti}} all'album", "utilities": "Utilità", "validate": "Validazione", "validate_endpoint_error": "Inserisci un URL valido", @@ -1930,6 +2016,7 @@ "view_album": "Visualizza Album", "view_all": "Vedi tutto", "view_all_users": "Visualizza tutti gli utenti", + "view_details": "Visualizza Dettagli", "view_in_timeline": "Visualizza in timeline", "view_link": "Visualizza link", "view_links": "Visualizza i link", diff --git a/i18n/ja.json b/i18n/ja.json index 1649f978d8..fd4734febf 100644 --- a/i18n/ja.json +++ b/i18n/ja.json @@ -427,6 +427,7 @@ "app_settings": "アプリ設定", "appears_in": "これらに含まれます", "archive": "アーカイブ", + "archive_action_prompt": "アーカイブに{count}項目追加しました", "archive_or_unarchive_photo": "写真をアーカイブまたはアーカイブ解除", "archive_page_no_archived_assets": "アーカイブした写真またはビデオがありません", "archive_page_title": "アーカイブ ({count})", @@ -464,7 +465,6 @@ "assets": "アセット", "assets_added_count": "{count, plural, one {#個} other {#個}}のアセットを追加しました", "assets_added_to_album_count": "{count, plural, one {#個} other {#個}}のアセットをアルバムに追加しました", - "assets_added_to_name_count": "{count, plural, one {#個} other {#個}}のアセットを{hasName, select, true {{name}} other {新しいアルバム}}に追加しました", "assets_cannot_be_added_to_album_count": "{count, plural, one {アセット} other {アセット}} はアルバムに追加できません", "assets_count": "{count, plural, one {#個} other {#個}}のアセット", "assets_deleted_permanently": "{count}項目を完全に削除しました", @@ -490,9 +490,10 @@ "back_close_deselect": "戻る、閉じる、選択解除", "background_location_permission": "バックグラウンド位置情報アクセス", "background_location_permission_content": "正常にWi-Fiの名前(SSID)を獲得するにはアプリが常に詳細な位置情報にアクセスできる必要があります", + "backup": "バックアップ", "backup_album_selection_page_albums_device": "デバイス上のアルバム({count})", "backup_album_selection_page_albums_tap": "タップで選択、ダブルタップで除外", - "backup_album_selection_page_assets_scatter": "アルバムを選択・除外してバックアップする写真を選ぶ (同じ写真が複数のアルバムに登録されていることがあるため)", + "backup_album_selection_page_assets_scatter": "アルバムを選択・除外してバックアップする写真を選ぶ。 (同じ写真が複数のアルバムに登録されていることがあるため)", "backup_album_selection_page_select_albums": "アルバムを選択", "backup_album_selection_page_selection_info": "選択・除外中のアルバム", "backup_album_selection_page_total_assets": "選択されたアルバムの写真と動画の数", @@ -703,7 +704,7 @@ "daily_title_text_date": "MM DD, EE", "daily_title_text_date_year": "yyyy MM DD, EE", "dark": "ダークモード", - "darkTheme": "ダークモードを切り替え", + "dark_theme": "ダークモード切り替え", "date_after": "この日以降", "date_and_time": "日付と時間", "date_before": "この日以前", @@ -719,6 +720,7 @@ "default_locale": "デフォルトのロケール", "default_locale_description": "ブラウザのロケールに基づいて日付と数値をフォーマットします", "delete": "削除", + "delete_action_prompt": "{count}項目を完全に削除しました", "delete_album": "アルバムを削除", "delete_api_key_prompt": "本当にこのAPI キーを削除しますか?", "delete_dialog_alert": "サーバーとデバイスの両方から完全に削除されます", @@ -799,6 +801,7 @@ "edit_key": "キーを編集", "edit_link": "リンクを編集する", "edit_location": "位置情報を編集", + "edit_location_action_prompt": "{count}項目の位置情報を変更しました", "edit_location_dialog_title": "位置情報", "edit_name": "名前を変更", "edit_people": "人物を編集", @@ -984,6 +987,7 @@ "failed_to_load_assets": "アセットのロードに失敗しました", "failed_to_load_folder": "フォルダーの読み込みに失敗", "favorite": "お気に入り", + "favorite_action_prompt": "{count}項目をお気に入りに追加しました", "favorite_or_unfavorite_photo": "写真をお気にいりに登録または解除", "favorites": "お気に入り", "favorites_page_no_favorites": "お気に入り登録された項目がありません", @@ -1130,7 +1134,6 @@ "light": "ライトモード", "like_deleted": "いいねが削除されました", "link_motion_video": "モーションビデオのリンク", - "link_options": "リンクのオプション", "link_to_oauth": "OAuthへリンクする", "linked_oauth_account": "リンクされたOAuthアカウント", "list": "リスト", @@ -1193,7 +1196,6 @@ "manage_your_devices": "ログインデバイスを管理します", "manage_your_oauth_connection": "OAuth接続を管理します", "map": "地図", - "map_assets_in_bound": "{count}枚", "map_assets_in_bounds": "{count}枚", "map_cannot_get_user_location": "位置情報がゲットできません", "map_location_dialog_yes": "はい", @@ -1246,6 +1248,7 @@ "more": "もっと表示", "move": "移動", "move_off_locked_folder": "鍵付きフォルダーから出す", + "move_to_lock_folder_action_prompt": "{count}項目を鍵付きフォルダーに追加しました", "move_to_locked_folder": "鍵付きフォルダーへ移動", "move_to_locked_folder_confirmation": "これらの写真や動画はすべてのアルバムから外され、鍵付きフォルダー内でのみ閲覧可能になります", "moved_to_archive": "{count, plural, one {#} other {#}}項目をアーカイブしました", @@ -1496,6 +1499,7 @@ "remove_deleted_assets": "オフラインのアセットを削除", "remove_from_album": "アルバムから削除", "remove_from_favorites": "お気に入り解除", + "remove_from_lock_folder_action_prompt": "{count}項目を鍵付きフォルダーから出しました", "remove_from_locked_folder": "鍵付きフォルダーから取り除く", "remove_from_locked_folder_confirmation": "選択した写真・動画を鍵付きフォルダーの外に出してよろしいですか?ライブラリに再び表示されるようになります", "remove_from_shared_link": "共有リンクから削除", @@ -1838,6 +1842,7 @@ "total": "合計", "total_usage": "総使用量", "trash": "ゴミ箱", + "trash_action_prompt": "{count}項目をゴミ箱に移動しました", "trash_all": "全て削除", "trash_count": "{count, number}枚ゴミ箱へ移動", "trash_delete_asset": "アセットをゴミ箱へ移動/削除", diff --git a/i18n/ko.json b/i18n/ko.json index 01b29e6776..ba6e3549fe 100644 --- a/i18n/ko.json +++ b/i18n/ko.json @@ -22,6 +22,7 @@ "add_partner": "파트너 추가", "add_path": "경로 추가", "add_photos": "사진 추가", + "add_tag": "태그 추가하기", "add_to": "앨범에 추가…", "add_to_album": "앨범에 추가", "add_to_album_bottom_sheet_added": "{album}에 추가되었습니다.", @@ -33,6 +34,7 @@ "added_to_favorites_count": "즐겨찾기에 {count, number}개 추가됨", "admin": { "add_exclusion_pattern_description": "규칙에 *, ** 및 ? 를 사용할 수 있습니다. 이름이 \"Raw\"인 디렉터리의 모든 파일을 제외하려면 \"**/Raw/**\"를, \".tif\"로 끝나는 모든 파일을 제외하려면 \"**/*.tif\"를 사용하고, 절대 경로의 경우 \"/path/to/ignore/**\"와 같은 방식으로 사용합니다.", + "admin_user": "관리자", "asset_offline_description": "외부 라이브러리에 포함된 이 항목을 디스크에서 더이상 찾을 수 없어 휴지통으로 이동되었습니다. 파일이 라이브러리 내에서 이동된 경우 타임라인에서 새로 연결된 항목을 확인하세요. 항목을 복원하려면 아래의 파일 경로에 Immich가 접근할 수 있는지 확인하고 라이브러리 스캔을 진행하세요.", "authentication_settings": "인증 설정", "authentication_settings_description": "비밀번호, OAuth 및 기타 인증 설정 관리", @@ -43,7 +45,7 @@ "backup_database_enable_description": "데이터베이스 덤프 활성화", "backup_keep_last_amount": "보관할 이전 덤프의 수", "backup_settings": "데이터베이스 덤프 설정", - "backup_settings_description": "데이터베이스 덤프 설정을 관리합니다. 참고: 이 작업은 진행 및 실패 여부를 확인할 수 없습니다.", + "backup_settings_description": "데이터베이스 덤프 설정을 관리합니다.", "cleared_jobs": "작업 중단: {job}", "config_set_by_file": "현재 구성은 설정 파일을 통해 지정되어 있습니다.", "confirm_delete_library": "{library} 라이브러리를 삭제하시겠습니까?", @@ -164,12 +166,26 @@ "metadata_settings_description": "메타데이터 설정 관리", "migration_job": "마이그레이션", "migration_job_description": "각 항목의 섬네일 및 인물의 얼굴을 최신 폴더 구조로 마이그레이션", + "nightly_tasks_cluster_faces_setting_description": "새로 감지된 얼굴에 대하여 얼굴 인식을 실행", + "nightly_tasks_cluster_new_faces_setting": "새 얼굴 묶기", + "nightly_tasks_database_cleanup_setting": "데이터베이스 정리 작업", + "nightly_tasks_database_cleanup_setting_description": "데이터베이스에서 오래되거나 만료된 데이터 정리하기", + "nightly_tasks_generate_memories_setting": "메모리 생성하기", + "nightly_tasks_generate_memories_setting_description": "항목에서 새로운 메모리 만들기", + "nightly_tasks_missing_thumbnails_setting": "누락된 섬네일 생성하기", + "nightly_tasks_missing_thumbnails_setting_description": "섬네일이 없는 항목에 대해 섬네일 생성 대기열에 추가하기", + "nightly_tasks_settings": "야간 작업 설정", + "nightly_tasks_settings_description": "야간 작업 관리", + "nightly_tasks_start_time_setting": "시작 시간", + "nightly_tasks_start_time_setting_description": "서버가 야간 작업을 시작할 시간", + "nightly_tasks_sync_quota_usage_setting": "사용량 동기화", + "nightly_tasks_sync_quota_usage_setting_description": "현재 사용량에 따라 사용자의 사용량 갱신하기", "no_paths_added": "추가된 경로 없음", "no_pattern_added": "추가된 규칙 없음", "note_apply_storage_label_previous_assets": "참고: 이전에 업로드한 항목에도 스토리지 레이블을 적용하려면 다음을 실행합니다,", "note_cannot_be_changed_later": "주의: 추후 변경할 수 없습니다!", "notification_email_from_address": "보낸 사람 이메일", - "notification_email_from_address_description": "보낸 사람의 이메일 주소, 예: \"Immich Photo Server \"", + "notification_email_from_address_description": "보낸 사람의 이메일 주소, 예: \"Immich Photo Server \". 이메일을 보낼 수 있도록 허가 받은 주소만을 사용하세요.", "notification_email_host_description": "이메일 서버의 호스트 (예: smtp.immich.app)", "notification_email_ignore_certificate_errors": "인증서 오류 무시", "notification_email_ignore_certificate_errors_description": "TLS 인증서 유효성 검사 오류 무시 (권장되지 않음)", @@ -194,6 +210,7 @@ "oauth_mobile_redirect_uri": "모바일 리다이렉트 URI", "oauth_mobile_redirect_uri_override": "모바일 리다이렉트 URI 재정의", "oauth_mobile_redirect_uri_override_description": "OAuth 공급자가 ''{callback}''과 같은 모바일 URI를 제공하지 않는 경우 활성화하세요.", + "oauth_role_claim": "역할 수령", "oauth_settings": "OAuth", "oauth_settings_description": "OAuth 로그인 설정 관리", "oauth_settings_more_details": "이 기능에 대한 자세한 내용은 문서를 참조하세요.", @@ -202,7 +219,7 @@ "oauth_storage_quota_claim": "스토리지 할당량 선택", "oauth_storage_quota_claim_description": "스토리지 할당량을 사용자가 입력한 값으로 자동 설정합니다.", "oauth_storage_quota_default": "스토리지 할당량 기본값 (GiB)", - "oauth_storage_quota_default_description": "입력하지 않은 경우 사용할 GiB 단위의 기본 할당량 (무제한 할당량의 경우 0 입력)", + "oauth_storage_quota_default_description": "입력하지 않은 경우 사용할 GiB 단위의 기본 할당량", "oauth_timeout": "요청 타임아웃", "oauth_timeout_description": "요청 타임아웃 (밀리초 단위)", "password_enable_description": "이메일과 비밀번호로 로그인", @@ -242,6 +259,7 @@ "storage_template_migration_info": "스토리지 템플릿은 모든 확장자를 소문자로 변환하며, 변경 사항은 새로 업로드한 항목에만 적용됩니다. 기존에 업로드된 항목에 적용하려면 {job}을 실행하세요.", "storage_template_migration_job": "스토리지 템플릿 마이그레이션 작업", "storage_template_more_details": "이 기능에 대한 자세한 내용은 스토리지 템플릿설명을 참조하세요.", + "storage_template_onboarding_description_v2": "활성화 시, 이 기능은 사용자 지정 템플릿에 따라 파일을 자동 분류합니다. 자세한 정보는 이 문서를 확인하세요.", "storage_template_path_length": "대략적인 경로 길이 제한: {length, number}/{limit, number}", "storage_template_settings": "스토리지 템플릿", "storage_template_settings_description": "업로드된 항목의 폴더 구조 및 파일 이름 관리", @@ -288,7 +306,7 @@ "transcoding_encoding_options": "인코딩 옵션", "transcoding_encoding_options_description": "인코딩된 동영상의 코덱, 해상도, 품질 및 기타 옵션 설정", "transcoding_hardware_acceleration": "하드웨어 가속", - "transcoding_hardware_acceleration_description": "실험적인 기능입니다. 속도가 향상되지만 동일 비트레이트에서 품질이 상대적으로 낮을 수 있습니다.", + "transcoding_hardware_acceleration_description": "실험적인 기능입니다. 트랜스코딩이 빨라지지만 동일 비트레이트에서 품질이 상대적으로 낮을 수 있습니다.", "transcoding_hardware_decoding": "하드웨어 디코딩", "transcoding_hardware_decoding_setting_description": "인코딩 가속을 위해 엔드 투 엔드 가속을 사용합니다. 모든 동영상에서 작동하지 않을 수 있습니다.", "transcoding_max_b_frames": "최대 B 프레임", @@ -354,10 +372,12 @@ "admin_password": "관리자 비밀번호", "administration": "관리", "advanced": "고급", + "advanced_settings_beta_timeline_subtitle": "새로운 앱 경험 사용하기", + "advanced_settings_beta_timeline_title": "베타 타임라인", "advanced_settings_enable_alternate_media_filter_subtitle": "이 옵션을 사용하면 동기화 중 미디어를 대체 기준으로 필터링할 수 있습니다. 앱이 모든 앨범을 제대로 감지하지 못할 때만 사용하세요.", "advanced_settings_enable_alternate_media_filter_title": "대체 기기 앨범 동기화 필터 사용 (실험적)", "advanced_settings_log_level_title": "로그 레벨: {level}", - "advanced_settings_prefer_remote_subtitle": "일부 기기의 경우 기기 내의 섬네일을 로드하는 속도가 매우 느립니다. 서버 이미지를 대신 로드하려면 이 설정을 활성화하세요.", + "advanced_settings_prefer_remote_subtitle": "일부 기기의 경우 로컬 항목에서 섬네일을 로드하는 속도가 매우 느립니다. 서버 이미지를 대신 로드하려면 이 설정을 활성화하세요.", "advanced_settings_prefer_remote_title": "서버 이미지 선호", "advanced_settings_proxy_headers_subtitle": "네트워크 요청을 보낼 때 Immich가 사용할 프록시 헤더를 정의합니다.", "advanced_settings_proxy_headers_title": "프록시 헤더", @@ -401,6 +421,9 @@ "album_with_link_access": "링크가 있는 경우 누구나 이 앨범의 사진과 인물을 볼 수 있습니다.", "albums": "앨범", "albums_count": "앨범 {count, plural, one {{count, number}개} other {{count, number}개}}", + "albums_default_sort_order": "기본 앨범 정렬 순서", + "albums_default_sort_order_description": "새 앨범을 생성할 때 기본적으로 항목을 정렬할 순서.", + "albums_feature_description": "다른 사용자와 공유할 수 있는 항목 모음.", "all": "모두", "all_albums": "모든 앨범", "all_people": "모든 인물", @@ -421,6 +444,7 @@ "app_settings": "앱 설정", "appears_in": "다음 앨범에 포함됨", "archive": "보관함", + "archive_action_prompt": "보관함에 {count}개가 추가되었습니다.", "archive_or_unarchive_photo": "보관 처리 또는 해제", "archive_page_no_archived_assets": "보관된 항목 없음", "archive_page_title": "보관함 ({count})", @@ -458,10 +482,12 @@ "assets": "항목", "assets_added_count": "{count, plural, one {#개} other {#개}} 항목 추가됨", "assets_added_to_album_count": "앨범에 항목 {count, plural, one {#개} other {#개}} 추가됨", - "assets_added_to_name_count": "{hasName, select, true {{name}} other {새 앨범}}에 항목 {count, plural, one {#개} other {#개}} 추가됨", + "assets_cannot_be_added_to_album_count": "{count, plural, one {항목} other {항목}]이 앨범에 추가될 수 없습니다.", "assets_count": "{count, plural, one {#개} other {#개}} 항목", "assets_deleted_permanently": "{count}개 항목이 영구적으로 삭제됨", "assets_deleted_permanently_from_server": "서버에서 항목 {count}개가 영구적으로 삭제됨", + "assets_downloaded_failed": "{count, plural, one {파일 #개 다운로드 완료 - {error}개 실패} other {파일 #개 다운로드 완료 - {error}개 실패}}", + "assets_downloaded_successfully": "{count, plural, one {#개 파일 다운로드 완료} other {#개 파일 다운로드 완료}}", "assets_moved_to_trash_count": "휴지통으로 항목 {count, plural, one {#개} other {#개}} 이동됨", "assets_permanently_deleted_count": "항목 {count, plural, one {#개} other {#개}}가 영구적으로 삭제됨", "assets_removed_count": "항목 {count, plural, one {#개} other {#개}}를 제거했습니다.", @@ -476,10 +502,12 @@ "authorized_devices": "인증된 기기", "automatic_endpoint_switching_subtitle": "지정된 Wi-Fi가 사용 가능한 경우 내부망을 통해 연결하고, 그렇지 않으면 다른 연결 방식을 사용합니다.", "automatic_endpoint_switching_title": "자동 URL 전환", + "autoplay_slideshow": "슬라이드 쇼 자동 재생", "back": "뒤로", "back_close_deselect": "뒤로, 닫기, 선택 취소", "background_location_permission": "백그라운드 위치 권한", "background_location_permission_content": "백그라운드에서 네트워크를 전환하려면, Immich가 Wi-Fi 네트워크 이름을 확인할 수 있도록 '정확한 위치' 권한을 항상 허용해야 합니다.", + "backup": "백업", "backup_album_selection_page_albums_device": "기기의 앨범 ({count})", "backup_album_selection_page_albums_tap": "한 번 탭하면 포함되고, 두 번 탭하면 제외됩니다.", "backup_album_selection_page_assets_scatter": "각 항목은 여러 앨범에 포함될 수 있으며, 백업 진행 중에도 대상 앨범을 포함하거나 제외할 수 있습니다.", @@ -1103,7 +1131,6 @@ "light": "라이트", "like_deleted": "좋아요가 삭제되었습니다.", "link_motion_video": "모션 비디오 링크", - "link_options": "링크 옵션", "link_to_oauth": "OAuth에 연결", "linked_oauth_account": "OAuth 계정이 연결되었습니다.", "list": "목록", @@ -1164,7 +1191,6 @@ "manage_your_devices": "로그인된 기기 관리", "manage_your_oauth_connection": "OAuth 연결 관리", "map": "지도", - "map_assets_in_bound": "사진 {count}개", "map_assets_in_bounds": "사진 {count}개", "map_cannot_get_user_location": "사용자의 위치를 가져올 수 없습니다.", "map_location_dialog_yes": "예", diff --git a/i18n/lt.json b/i18n/lt.json index d5e6ff20ed..5f58f3ffdc 100644 --- a/i18n/lt.json +++ b/i18n/lt.json @@ -14,6 +14,7 @@ "add_a_location": "Pridėti vietovę", "add_a_name": "Pridėti vardą", "add_a_title": "Pridėti pavadinimą", + "add_endpoint": "Pridėti galutinį tašką", "add_exclusion_pattern": "Pridėti išimčių šabloną", "add_import_path": "Pridėti importavimo kelią", "add_location": "Pridėti vietovę", @@ -32,6 +33,8 @@ "added_to_favorites": "Pridėta prie mėgstamiausių", "added_to_favorites_count": "{count, plural, one {# pridėtas} few {# pridėti} other {# pridėta}} prie mėgstamiausių", "admin": { + "add_exclusion_pattern_description": "Pridėti išimčių taisyklęs. Plaikomi simboliai *,**, ir ?. Ignoruoti bet kokius failus bet kuriame aplanke užvadintame \"Raw\", naudokite \"**/RAW/**\". Ignoravimui failų su plėtiniu \".tif\", naudokite \"**/*.tiff\". Aplanko kelio nustatymams, naudokite \"/aplanko/kelias/ignoruoti/**\"", + "admin_user": "Administratorius", "asset_offline_description": "Šis išorinės bibliotekos elementas nebepasiekiamas diske ir buvo perkeltas į šiukšliadėžę. Jei failas buvo perkeltas toje pačioje bibliotekoje, laiko skalėje rasite naują atitinkamą elementą. Jei norite šį elementą atkurti, įsitikinkite, kad Immich gali pasiekti failą žemiau nurodytu adresu, ir suvykdykite bibliotekos skenavimą.", "authentication_settings": "Autentifikavimo nustatymai", "authentication_settings_description": "Tvarkyti slaptažodžių, OAuth ir kitus autentifikavimo nustatymus", @@ -42,8 +45,8 @@ "backup_database_enable_description": "Įgalinti duomenų bazės išklotinės", "backup_keep_last_amount": "Išsaugomų ankstesnių duomenų bazės išklotinių skaičius", "backup_settings": "Duomenų bazės išklotinių nustatymai", - "backup_settings_description": "Tvarkyti duomenų bazės išklotinės nustatymus. Pastaba: Šie darbai nėra stebimi ir jums nebus pranešta apie nesėkmę.", - "cleared_jobs": "Išvalyti darbai: {job}", + "backup_settings_description": "Tvarkyti duomenų bazės išklotinės nustatymus. Pastaba: šie darbai nėra stebimi ir jums nebus pranešta apie nesėkmę.", + "cleared_jobs": "Išvalytos užduotys užduočiai: {job}", "config_set_by_file": "Konfigūracija nustatyta pagal konfigūracinį failą", "confirm_delete_library": "Ar tikrai norite ištrinti {library} biblioteką?", "confirm_delete_library_assets": "Ar tikrai norite ištrinti šią biblioteką? Šis veiksmas ištrins {count, plural, one {# contained asset} other {all # contained assets}} iš Immich ir negali būti grąžintas. Failai liks diske.", @@ -51,7 +54,7 @@ "confirm_reprocess_all_faces": "Ar tikrai norite iš naujo apdoroti visus veidus? Tai taip pat ištrins įvardytus asmenis.", "confirm_user_password_reset": "Ar tikrai norite iš naujo nustatyti {user} slaptažodį?", "confirm_user_pin_code_reset": "Ar tikrai norite iš naujo nustatyti {user} PIN kodą?", - "create_job": "Sukurti darbą", + "create_job": "Sukurti užduotį", "cron_expression": "Cron išraiška", "cron_expression_description": "Nustatyti skenavimo intervalą naudojant cron formatą. Norėdami gauti daugiau informacijos žiūrėkite Crontab Guru", "cron_expression_presets": "Išankstiniai Cron nustatymai", @@ -62,15 +65,19 @@ "face_detection": "Veidų aptikimas", "face_detection_description": "Veidų aptikimas bibliotekos elementuose naudojant mašininį mokymąsi. Vaizdo įrašų atveju naudojama tik miniatiūra. \"Atnaujinti\" iš naujo nuskaito visus bibliotekos elementus. \"Atstatyti\" ne tik atnaujina, bet ir išvalo visus esamus veidų duomenis. \"Trūkstami\" nuskaito tik dar nenuskaitytus bibliotekos elementus. Veidų aptikimo darbui pasibaigus, aptikti veidai patenka į veidų atpažinimo darbų eilę, kur jie priskiriami jau esamiems ar naujai atpažintiems žmonėms.", "facial_recognition_job_description": "Aptiktų veidų atpažinimas ir priskyrimas žmonėms. Šis darbas vykdomas pasibaigus \"veidų aptikimo\" darbui. \"Atstatyti\" (per)grupuoja visus aptiktus veidus. \"Trūkstami\" apdoroja jokiam žmogui dar nepriskirtus aptiktus veidus.", - "failed_job_command": "Darbo {job} komanda {command} nepavyko", + "failed_job_command": "Užduoties {job} komanda {command} nepavyko", "force_delete_user_warning": "ĮSPĖJIMAS: Šis veiksmas iš karto pašalins naudotoją ir visą jo informaciją. Šis žingsnis nesugrąžinamas ir failų nebus galima atkurti.", "image_format": "Formatas", "image_format_description": "WebP sukuria mažesnius failus nei JPEG, tačiau lėčiau juos apdoroja.", + "image_fullsize_description": "Pilno dydžio nuotrauka be meta duomenų naudojama priartinus", "image_fullsize_enabled": "Įgalinti pilno dydžio nuotraukų generavimą", + "image_fullsize_enabled_description": "Generuoti viso dydžio vaizdą neinternetui pritaikytiems formatams. Kai įjungta parinktis „Pirmenybė įterptai peržiūrai“, įterptosios peržiūros naudojamos tiesiogiai be konvertavimo. Tai neturi įtakos internetui pritaikytiems formatams, pvz., JPEG", "image_fullsize_quality_description": "Pilno dydžio nuotraukų kokybė 1-100. Didesnė yra geresnė, tačiau sukuria didesniu failus.", "image_fullsize_title": "Pilno dydžio nuotraukų Nustatymai", "image_prefer_embedded_preview": "Pageidautinai rodyti įterptą peržiūrą", + "image_prefer_embedded_preview_setting_description": "Naudokite įterptąsias peržiūras RAW nuotraukose kaip įvestį vaizdų apdorojimui ir, jei įmanoma, tai gali suteikti tikslesnes kai kurių vaizdų spalvas, tačiau peržiūros kokybė priklauso nuo fotoaparato, todėl vaizde gali būti daugiau glaudinimo artefaktų.", "image_prefer_wide_gamut": "Teikti pirmenybę plačiai gamai", + "image_prefer_wide_gamut_setting_description": "Miniatiūroms naudokite „Display P3“. Taip geriau išsaugomas vaizdų, turinčių plačias spalvų erdves, ryškumas, tačiau senesniuose įrenginiuose su senesne naršyklės versija vaizdai gali atrodyti kitaip. sRGB vaizdai išsaugomi kaip sRGB, kad būtų išvengta spalvų pasikeitimo.", "image_preview_description": "Vidutinio dydžio vaizdas su išvalytais metaduomenimis, naudojamas kai žiūrimas vienas objektas arba mašininiam mokymuisi", "image_preview_quality_description": "Peržiūros kokybė nuo 1-100. Aukštesnės reikšmės yra geriau, bet sukuriami didesni failai gali sumažinti programos reagavimo laiką. Mažos vertės nustatymas gali paveikti mašininio mokymo kokybę.", "image_preview_title": "Peržiūros nustatymai", @@ -83,11 +90,13 @@ "image_thumbnail_quality_description": "Miniatiūros kokybė nuo 1-100. Aukštesnės reikšmės yra geriau, bet pagaminami didesni failai ir gali būti sulėtintas programos reagavimo greitis.", "image_thumbnail_title": "Miniatiūros nustatymai", "job_concurrency": "{job} lygiagretumas", - "job_created": "Darbas sukurtas", - "job_not_concurrency_safe": "Šis darbas nėra saugus apdoroti lygiagrečiai.", - "job_settings": "Darbų nustatymai", - "job_settings_description": "Keisti darbų lygiagretumą", - "job_status": "Darbų būsenos", + "job_created": "Užduotis sukurta", + "job_not_concurrency_safe": "Ši užduotis nėra saugi apdoroti lygiagrečiai.", + "job_settings": "Užduočių nustatymai", + "job_settings_description": "Keisti užduočių lygiagretumą", + "job_status": "Užduočių būsenos", + "jobs_delayed": "{jobCount, plural, other {# delayed}}", + "jobs_failed": "{jobCount, plural, other {# failed}}", "library_created": "Sukurta biblioteka: {library}", "library_deleted": "Biblioteka ištrinta", "library_import_path_description": "Nurodykite aplanką, kurį norite importuoti. Šiame aplanke, įskaitant poaplankius, bus nuskaityti vaizdai ir vaizdo įrašai.", @@ -104,6 +113,7 @@ "logging_level_description": "Įjungus, kokį žurnalo vedimo lygį naudot.", "logging_settings": "Žurnalo vedimas", "machine_learning_clip_model": "CLIP modelis", + "machine_learning_clip_model_description": "Pavadinimas CLIP modelio įvardintio here. Dėmesio, keičiant modelį jūs privalote iš naujo paleisti 'Išmaniosios Paieškos' užduotį visiems vaizdams.", "machine_learning_duplicate_detection": "Dublikatų aptikimas", "machine_learning_duplicate_detection_enabled": "Įjungti dublikatų aptikimą", "machine_learning_duplicate_detection_enabled_description": "Jei išjungta, visiškai identiški elementai vis tiek bus deduplikuoti.", @@ -113,12 +123,15 @@ "machine_learning_facial_recognition": "Veidų atpažinimas", "machine_learning_facial_recognition_description": "Aptikti, atpažinti ir sugrupuoti veidus nuotraukose", "machine_learning_facial_recognition_model": "Veidų atpažinimo modelis", + "machine_learning_facial_recognition_model_description": "Modeliai išvardinti apimties mažėjančia tvarka. Didieji modeliai yra lėti ir naudoja daugiau atminties, tačiau sukuria geresnius rezultatus. Pastebime kad keičiant modelį jūs turite iš naujo paleisti Veidų Atpažinimo užduotį visiems vaizdams.", "machine_learning_facial_recognition_setting": "Įgalinti veidų atpažinimą", "machine_learning_facial_recognition_setting_description": "Išjungus, vaizdai nebus užšifruoti veidų atpažinimui ir nebus naudojami Žmonių sekcijoje Naršymo puslapyje.", "machine_learning_max_detection_distance": "Maksimalus aptikimo atstumas", "machine_learning_max_detection_distance_description": "Didžiausias atstumas tarp dviejų vaizdų, kad jie būtų laikomi dublikatais, svyruoja nuo 0,001 iki 0,1. Didesnės vertės aptiks daugiau dublikatų, tačiau gali būti klaidingai teigiami.", "machine_learning_max_recognition_distance": "Maksimalus atpažinimo atstumas", + "machine_learning_max_recognition_distance_description": "Didžiausias skirtumas tarp veidų, kad būtų užskaityti kaip vienas ir tas pats asmuo, rėžis nuo 0-2. Mažinant tai gali apsaugoti nuo dviejų žmonių žymėjimo tuo pačiu asmeniu, didinant tai gali apsaugoti nuo to pačio asmens žymėjimo kaip du skirtingus žmones. Pastebime kad yra paprasčiau apjungti kelių žmonių modelius į vieną nei vieną išdalinti į du, taigi kai įmanoma geriau naudoti mažensę ribą.", "machine_learning_min_detection_score": "Minimalus aptikimo balas", + "machine_learning_min_detection_score_description": "Minimalus užtikrintumo balas veido aptikimui nuo 0-1. Mažesnė reikšmė aptiks daugiau veidų tačiau bus ir daugiau klaidingų teigiamų režultatų.", "machine_learning_min_recognized_faces": "Mažiausias atpažintų veidų skaičius", "machine_learning_min_recognized_faces_description": "Mažiausias atpažintų veidų skaičius asmeniui, kurį reikia sukurti. Tai padidinus, veido atpažinimas tampa tikslesnis, bet padidėja tikimybė, kad veidas žmogui nepriskirtas.", "machine_learning_settings": "Mašininio mokymosi nustatymai", @@ -151,14 +164,27 @@ "metadata_faces_import_setting_description": "Importuoti veidus iš vaizdo EXIF duomenų ir papildomų failų", "metadata_settings": "Metaduomenų nustatymai", "metadata_settings_description": "Tvarkyti metaduomenų nustatymus", - "migration_job": "Migracija", + "migration_job": "Tvarkymas", + "migration_job_description": "Pertvarkytį turinio ir veidų miniatiūras pagal naują struktūrą", + "nightly_tasks_cluster_faces_setting_description": "Paleisti veido atpažinimą naujai aptiktiems veidams", + "nightly_tasks_cluster_new_faces_setting": "Sugrupuoti naujus veidus", + "nightly_tasks_database_cleanup_setting": "Duomenų bazės valymo darbai", + "nightly_tasks_database_cleanup_setting_description": "Išvalyti senus, nebgaliojančius duomenis iš duomenų bazės", + "nightly_tasks_generate_memories_setting": "Kurti prisiminimus", + "nightly_tasks_generate_memories_setting_description": "Iš duomenų kurti naujus prisiminimus", + "nightly_tasks_missing_thumbnails_setting": "Kurti trūkstamas miniatiūras", + "nightly_tasks_missing_thumbnails_setting_description": "Sudaryti įrašų be miniatiūrų eilę miniatiūrų generavimui", + "nightly_tasks_settings": "Naktinių užduočių nustatymai", + "nightly_tasks_settings_description": "Valdyti naktines užduotis", + "nightly_tasks_start_time_setting": "Pradžios laikas", + "nightly_tasks_start_time_setting_description": "Laikas kada serveris pradės vykdyti naktines užduotis", "no_paths_added": "Keliai nepridėti", "no_pattern_added": "Šablonas nepridėtas", - "note_apply_storage_label_previous_assets": "Pastaba: norėdami pritaikyti saugyklos etiketę seniau įkeltiems ištekliams, paleiskite", + "note_apply_storage_label_previous_assets": "Pastaba: norėdami pritaikyti Saugyklos Žymą seniau įkeltiems ištekliams, paleiskite", "note_cannot_be_changed_later": "PASTABA: Vėliau to pakeisti negalima!", "notification_email_from_address": "Iš adreso", - "notification_email_from_address_description": "Siuntėjo elektroninis adresas, pavyzdžiui: \"Immich Photo Server \"", - "notification_email_host_description": "Elektroninio pašto serverio savininkas (pvz. smtp.immich.app)", + "notification_email_from_address_description": "Siuntėjo el. pašto adresas, pavyzdžiui: \"Immich Photo Server \". Būtinai naudokite adresą iš kurio jums galima siųsti laiškus.", + "notification_email_host_description": "Elektroninio pašto serverio adresas (pvz. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Nepaisyti sertifikatų klaidų", "notification_email_ignore_certificate_errors_description": "Nepaisyti TLS sertifikato patvirtinimo klaidų (nerekomenduojama)", "notification_email_password_description": "Slaptažodis, naudojant autentikacijai su elektroninio pašto serveriu", @@ -177,20 +203,29 @@ "oauth_auto_register": "Automatinis registravimas", "oauth_auto_register_description": "Automatiškai užregistruoti naujus naudotojus po prisijungimo per OAuth", "oauth_button_text": "Mygtuko tekstas", + "oauth_client_secret_description": "Privalomas jei PKCE (Proof Key for Code Exchange) nepalaikomas pagal OAuth tiekėją", "oauth_enable_description": "Prisijungti su OAuth", "oauth_mobile_redirect_uri": "Mobiliojo peradresavimo URI", "oauth_mobile_redirect_uri_override": "Mobiliojo peradresavimo URI pakeitimas", "oauth_mobile_redirect_uri_override_description": "Įjunkite, kai OAuth teikėjas nepalaiko mobiliojo URI, tokio kaip ''{callback}''", + "oauth_role_claim": "Rolės Tvirtinimas", + "oauth_role_claim_description": "Suteikti admin teises automatiškai pagal šios rolės tvirtinimo buvimą. Tvirtinimas gali turėti priskirtus arba 'vartotoją' arba 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Tvarkyti OAuth prisijungimo nustatymus", "oauth_settings_more_details": "Detaliau apie šią funkciją galite paskaityti dokumentacijoje.", + "oauth_storage_label_claim": "Saugyklos Žyma pagal tvirtinimą", + "oauth_storage_label_claim_description": "Priskirti Saugyklos Žymą automatiškai pagal reikšmę vartotojo tvirtinime.", + "oauth_storage_quota_claim": "Saugyklos apimties tvirtinimas", + "oauth_storage_quota_claim_description": "Priskirti vartotojo saugyklos apimties kvotą automatiškai pagal šio tvirtinimo reikšmę.", "oauth_storage_quota_default": "Numatyta atminties kvota (GiB)", + "oauth_storage_quota_default_description": "Nustatoma appimties kvota GiB kai nėra nurodyta tvirtinime.", "oauth_timeout": "Užklausa viršijo laiko limitą", "oauth_timeout_description": "Laiko limitas užklausoms milisekundėmis", "password_enable_description": "Prisijungti su el. paštu ir slaptažodžiu", "password_settings": "Prisijungimas slaptažodžiu", "password_settings_description": "Tvarkyti prisijungimo slaptažodžiu nustatymus", "paths_validated_successfully": "Visi keliai patvirtinti sėkmingai", + "person_cleanup_job": "Išvalyti asmenis", "quota_size_gib": "Kvotos dydis (GiB)", "refreshing_all_libraries": "Perkraunamos visos bibliotekos", "registration": "Administratoriaus registracija", @@ -199,7 +234,7 @@ "reset_settings_to_default": "Atstatyti nustatymus į numatytuosius", "reset_settings_to_recent_saved": "Nustatymų atstatymas į neseniai išsaugotus nustatymus", "scanning_library": "Biblioteka skenuojama", - "search_jobs": "Ieškoma darbų…", + "search_jobs": "Ieškoma užduočių…", "send_welcome_email": "Siųsti sveikinimo el. laišką", "server_external_domain_settings": "Išorinis domenas", "server_external_domain_settings_description": "Bendrinimo nuorodų domenas, įskaitant http(s)://", @@ -209,40 +244,74 @@ "server_settings_description": "Tvarkyti serverio nustatymus", "server_welcome_message": "Sveikinimo pranešimas", "server_welcome_message_description": "Žinutė, rodoma prisijungimo puslapyje.", + "sidecar_job": "Sidecar metaduomenys", + "sidecar_job_description": "Aptikti ar sinchronizuoti sidecar metaduomenis iš failų sistemos", "slideshow_duration_description": "Sekundžių skaičius, kiek viena nuotrauka rodoma", "smart_search_job_description": "Vykdykite mašininį mokymąsi bibliotekos elementų išmaniajai paieškai", "storage_template_date_time_description": "Elemento sukūrimo laiko žymė yra naudojama laiko informacijai", "storage_template_date_time_sample": "Pavyzdinis laikas {date}", + "storage_template_enable_description": "Aktyvuoti saugyklos šabloną", + "storage_template_hash_verification_enabled": "Aktyvuoti Hash tikrinimą", + "storage_template_hash_verification_enabled_description": "Aktyvuojamas Hash tikrinimas, neišjungti nebent gerai suprantate galimas pasekmes", + "storage_template_migration": "Saugyklos tvarkymas pagal šabloną", + "storage_template_migration_description": "Taikyti dabartinį {template} anksčiau įkeltiems duomenims", + "storage_template_migration_info": "Saugyklos tvarkyklė konvertuos visus plėtinius mažosiomis raidėmis. Šablonas bus taikomas tik naujiems duomenims. Taikyti šabloną retroaktyviai anksčiau įkeltiems duomenims, paleiskite šią {job}.", + "storage_template_migration_job": "Saugyklos Tvarkymo Pagal Šabloną Užduotis", + "storage_template_more_details": "Daugiau detalių apie šią funkciją, atsižvelkite į Storage Template ir jo galimus implications", + "storage_template_onboarding_description_v2": "Kai aktyvuota, ši funkcija automatiškai sukurs failus pagal vartotojo-nustatytą šabloną. Daugiau informacijos, prašome skaityti documentation.", + "storage_template_path_length": "Preliminarus struktūros kelio ilgis/limitas:{length, number}/{limit, number}", + "storage_template_settings": "Saugyklos Šablonas", + "storage_template_settings_description": "Tvarkyti aplankų struktūrą bei failų pavadinimus įkeliamiems duomenims", + "storage_template_user_label": "{label} yra vartotojo Saugyklos Žymą", "system_settings": "Sistemos nustatymai", "tag_cleanup_job": "Žymų išvalymas", + "template_email_available_tags": "Savo šablone galite naudoti nurodytas kintamas reikšmes:{tags}", + "template_email_if_empty": "Jei šablone tuščia reikšmė, bus naudojamas numatytas pagal nutylėjimą El. pašto adresas.", "template_email_preview": "Peržiūra", "template_email_settings": "El. pašto Šablonai", "template_settings": "Pranešimų šablonai", "template_settings_description": "Tvarkyti pasirinktinius pranešimų šablonus", "theme_custom_css_settings": "Individualizuotas CSS", + "theme_custom_css_settings_description": "CSS leidžiantis keisti Immich dizainą.", "theme_settings": "Temos nustatymai", - "thumbnail_generation_job": "Generuoti miniatiūras", + "theme_settings_description": "Valdyti Immich web sąsajos pritaikymus", + "thumbnail_generation_job": "Generuoti Miniatiūras", "thumbnail_generation_job_description": "Didelių, mažų ir neryškių miniatiūrų generavimas kiekvienam bibliotekos elementui, taip pat miniatiūrų generavimas kiekvienam asmeniui", "transcoding_acceleration_api": "Spartinimo API", + "transcoding_acceleration_api_description": "API kurį naudos paspartintam perkodavimui. Tai veiks pagal \"geriausią bandymą\": nepavykus bus naudojamas programinis perkodavimas. VP9 gali veikti arba ne priklausomai nuo jūsų techninės įrangos.", "transcoding_acceleration_nvenc": "NVENC (reikalinga NVIDIA GPU)", "transcoding_acceleration_qsv": "Quick Sync (reikalingas 7-os arba vėlesnės generacijos Intel procesorius)", + "transcoding_acceleration_rkmpp": "RKMPP (tik Rockchip SOCs)", "transcoding_acceleration_vaapi": "VAAPI", + "transcoding_accepted_audio_codecs": "Priimtini garso kodekai", + "transcoding_accepted_audio_codecs_description": "Pasirinkti kurių garso kodekų nereikia perkoduoti. Naudojma tik kai kurioms perkodavimo taisyklėms.", "transcoding_accepted_containers": "Priimami konteineriai", + "transcoding_accepted_containers_description": "Pasirinkti kurių konteinerių formatų nereikia performuoti į MP4. Naudojama tik kai kurioms perkodavimo taisyklėms.", + "transcoding_accepted_video_codecs": "Priimami vaizdo kodekai", + "transcoding_accepted_video_codecs_description": "Pasirinkti vaizdo kodekus kurių nereikia perkoduoti. Naudojama tik kai kurioms perkodavimo taisyklėms.", "transcoding_advanced_options_description": "Parinktys, kurių daugelis naudotojų keisti neturėtų", "transcoding_audio_codec": "Garso kodekas", "transcoding_audio_codec_description": "Opus yra aukščiausios kokybės variantas, tačiau turi mažesnį suderinamumą su senesniais įrenginiais ar programine įranga.", "transcoding_bitrate_description": "Vaizdo įrašai viršija maksimalią leistiną bitų spartą arba nėra priimtino formato", + "transcoding_codecs_learn_more": "Sužinoti daugiau apie naudojamą terminologiją, naudokite FFmpeg dokumentaciją H.264 codec, HEVC codec and VP9 codec.", "transcoding_constant_quality_mode": "Pastovios kokybės režimas", + "transcoding_constant_quality_mode_description": "ICQ yra geriau nei CPQ, tačiau ne visi įrenginiai palaiko šį spartinimo būdą. Šis pasirinkimas būtų naudojamas kai nustatytas Kodavimas Pagal Kokybę. NVENC nepalaiko šio pasirinkimo todėl bus ignoruojamas.", + "transcoding_constant_rate_factor_description": "Video kokybės lygis. Tipinės reikšmės yra 23 jei H.264, 28 jei HVEC, 31 jei VP9, ir 35 jei AV1. Kuo mažesnis tuo kokybiškesnis tačiau didesni failai.", + "transcoding_disabled_description": "Nedaryti perkodavimo, įrašų peržiūra gali neveikti ant kai kūrių sąsajų", "transcoding_hardware_acceleration": "Techninės įrangos spartinimas", "transcoding_hardware_decoding": "Aparatinis dekodavimas", "transcoding_max_bitrate": "Maksimalus bitų srautas", + "transcoding_max_bitrate_description": "Pasirenkant max bitrate galima pasiekti labiau nuspėjamą failų dydį su minimaliais kokybės praradimais. Prie 720p, tipinės reikšmės yra 2600 kbits/s jei BP9 ar HVEC, arba 4500 kbits/s jei H.264. Neveiksnus jei pasirenkamas 0.", + "transcoding_preset_preset_description": "Kompresijos greitis. Siekiant tam tikro bitrate lėtesnis apdorojimas lems mažesnius failų dydžius ir padidins kokybę. VP9 ignoruos greičius virš \"gretesnis\" lygio.", "transcoding_target_resolution_description": "Didesnės skiriamosios gebos gali išsaugoti daugiau detalių, tačiau jas koduoti užtrunka ilgiau, failų dydžiai yra didesni ir gali sumažėti programos jautrumas.", "transcoding_video_codec": "Video kodekas", "trash_enabled_description": "Įgalinti šiukšliadėžės funkcijas", "trash_number_of_days": "Dienų skaičius", "trash_settings": "Šiukšliadėžės nustatymai", "trash_settings_description": "Tvarkyti šiukšliadėžės nustatymus", + "user_cleanup_job": "Vartotojų išvalymas", "user_delete_delay_settings": "Ištrynimo delsa", + "user_delete_delay_settings_description": "Skaičius dienų po ištrynimo kuomet vartotojo paskyrą ir susiję duomenys bus negražinamai ištrinti. Vartotojo Trynimo užduotis paleidžiama vidurnaktį ir tikrina kurie vartotojai gali būti trinami. Šio nustatymo pakeitimai bus naudojami sekančio užduoties paleidimo metu.", "user_management": "Naudotojų valdymas", "user_password_has_been_reset": "Naudotojo slaptažodis buvo iš naujo nustatytas:", "user_restore_description": "Naudotojo {user} paskyra bus atkurta.", @@ -305,7 +374,6 @@ "assets": "Elementai", "assets_added_count": "{count, plural, one {Pridėtas # elementas} few {Pridėti # elementai} other {Pridėta # elementų}}", "assets_added_to_album_count": "Į albumą {count, plural, one {įtrauktas # elementas} few {įtraukti # elementai} other {įtraukta # elementų}}", - "assets_added_to_name_count": "Į {hasName, select, true {{name}} other {naują}} albumą {count, plural, one {įtrauktas # elementas} few {įtraukti # elementai} other {įtraukta # elementų}}", "assets_count": "{count, plural, one {# elementas} few {# elementai} other {# elementų}}", "assets_moved_to_trash_count": "{count, plural, one {# elementas perkeltas} few {# elementai perkelti} other {# elementų perkelta}} į šiukšliadėžę", "assets_permanently_deleted_count": "{count, plural, one {# elementas ištrintas} few {# elementai ištrinti} other {# elementų ištrinta}} visam laikui", @@ -316,7 +384,11 @@ "authorized_devices": "Autorizuoti įrenginiai", "back": "Atgal", "back_close_deselect": "Atgal, uždaryti arba atžymėti", + "backup_background_service_current_upload_notification": "Įkeliamas {filename}", + "backup_background_service_upload_failure_notification": "Nepavyko įkelti {filename}", "backup_controller_page_background_wifi": "Only on WiFi", + "backup_controller_page_filename": "Failo pavadinimas: {filename}[{size}]", + "backup_controller_page_uploading_file_info": "Įkeliama failo info", "birthdate_saved": "Sėkmingai išsaugota gimimo data", "blurred_background": "Neryškus fonas", "bugs_and_feature_requests": "Klaidų ir funkcijų užklausos", @@ -345,6 +417,7 @@ "clear_all": "Išvalyti viską", "clear_message": "Išvalyti pranešimą", "clear_value": "Išvalyti reikšmę", + "client_cert_invalid_msg": "Netinkamas sertifikato failas arba neteisingas slaptažodis", "close": "Uždaryti", "collapse": "Suskleisti", "collapse_all": "Suskleisti viską", @@ -420,8 +493,11 @@ "do_not_show_again": "Daugiau nerodyti šio pranešimo", "documentation": "Dokumentacija", "download": "Atsisiųsti", + "download_include_embedded_motion_videos_description": "Pridėti prie judesio nuotraukų įterptus video kaip atskirą failą", "download_settings": "Atsisiųsti", "downloading": "Siunčiama", + "downloading_asset_filename": "Parsisiunčiamas resursas {filename}", + "drop_files_to_upload": "Užkelkite failus bet kurioje vietoje kad įkeltumėte", "duplicates": "Dublikatai", "duplicates_description": "Sutvarkykite kiekvieną elementų grupę nurodydami elementus, kurie yra dublikatai (jei tokių yra)", "duration": "Trukmė", @@ -493,6 +569,7 @@ "unable_to_delete_import_path": "Nepavyksta ištrinti importavimo kelio", "unable_to_delete_shared_link": "Nepavyko ištrinti bendrinimo nuorodos", "unable_to_delete_user": "Nepavyksta ištrinti naudotojo", + "unable_to_download_files": "Nepavyksta atsisiųsti failų", "unable_to_edit_exclusion_pattern": "Nepavyksta redaguoti išimčių šablono", "unable_to_edit_import_path": "Nepavyksta redaguoti išimčių kelio", "unable_to_enter_fullscreen": "Nepavyksta pereiti į viso ekrano režimą", @@ -517,6 +594,7 @@ "unable_to_scan_library": "Nepavyksta nuskaityti bibliotekos", "unable_to_set_feature_photo": "Nepavyksta nustatyti mėgstamiausios nuotraukos", "unable_to_set_profile_picture": "Nepavyksta nustatyti profilio nuotraukos", + "unable_to_submit_job": "Napvyko sukurti užduoties", "unable_to_trash_asset": "Nepavyko perkelti į šiukšliadėžę", "unable_to_upload_file": "Nepavyksta įkelti failo" }, @@ -539,6 +617,7 @@ "features_setting_description": "Valdyti aplikacijos funkcijas", "file_name": "Failo pavadinimas", "file_name_or_extension": "Failo pavadinimas arba plėtinys", + "filename": "Failopavadinimas", "filetype": "Failo tipas", "filter_people": "Filtruoti žmones", "folders": "Aplankai", @@ -574,8 +653,9 @@ }, "invite_people": "Kviesti žmones", "invite_to_album": "Pakviesti į albumą", + "ios_debug_info_no_sync_yet": "Jokia background sync užduotis dar nebuvo paleista", "items_count": "{count, plural, one {# elementas} few {# elementai} other {# elementų}}", - "jobs": "Darbai", + "jobs": "Užduotys", "keep": "Palikti", "keep_all": "Palikti visus", "keyboard_shortcuts": "Spartieji klaviatūros klavišai", @@ -589,7 +669,6 @@ "level": "Lygis", "library": "Biblioteka", "library_options": "Bibliotekos pasirinktys", - "link_options": "Nuorodų parinktys", "link_to_oauth": "Susieti su OAuth", "linked_oauth_account": "Susieta OAuth paskyra", "list": "Sąrašas", @@ -660,6 +739,7 @@ "no_results": "Nerasta", "no_results_description": "Pabandykite sinonimą arba bendresnį raktažodį", "not_in_any_album": "Nė viename albume", + "note_apply_storage_label_to_previously_uploaded assets": "Pastaba: Priskirti Saugyklos Žymą prie ankčiau įkeltų ištekliu, paleiskite šį", "notes": "Pastabos", "notification_toggle_setting_description": "Įjungti el. pašto pranešimus", "notifications": "Pranešimai", @@ -707,6 +787,14 @@ "place": "Vieta", "places": "Vietos", "play_memories": "Leisti atsiminimus", + "profile": "Profilis", + "profile_drawer_app_logs": "Logai", + "profile_drawer_client_out_of_date_major": "Mobili aplikacija jau pasenusios versijos. Prašome atsinaujinti į paskutinę didžiąją versiją.", + "profile_drawer_client_out_of_date_minor": "Mobili aplikacija jau pasenusios versijos. Prašome atsinaujinti į paskutinę mažąją versiją.", + "profile_drawer_client_server_up_to_date": "Klientas ir Serveris yra atnaujinti", + "profile_drawer_github": "GitHub", + "profile_drawer_server_out_of_date_major": "Serveris jau yra pasenusios versijos. Prašome atsinaujinti į paskutinę didžiąją versiją.", + "profile_drawer_server_out_of_date_minor": "Serveris jau yra pasenusios versijos. Prašome atsinaujinti į paskutinę mažąją versiją.", "profile_image_of_user": "{user} profilio nuotrauka", "profile_picture_set": "Profilio nuotrauka nustatyta.", "public_album": "Viešas albumas", @@ -893,7 +981,7 @@ "show_albums": "Rodyti albumus", "show_all_people": "Rodyti visus asmenis", "show_and_hide_people": "Rodyti ir paslėpti žmones", - "show_file_location": "Rodyti rinkmenos vietą", + "show_file_location": "Rodyti failo vietą", "show_gallery": "Rodyti galeriją", "show_hidden_people": "Rodyti paslėptus asmenis", "show_in_timeline": "Rodyti laiko skalėje", @@ -936,6 +1024,7 @@ "status": "Statusas", "stop_casting": "Nutraukti transliavimą", "storage": "Saugykla", + "storage_label": "Saugyklos Žyma", "storage_usage": "Naudojama {used} iš {available}", "submit": "Pateikti", "suggestions": "Pasiūlymai", diff --git a/i18n/lv.json b/i18n/lv.json index 3fcf228612..2c51cc3854 100644 --- a/i18n/lv.json +++ b/i18n/lv.json @@ -14,6 +14,7 @@ "add_a_location": "Pievienot atrašanās vietu", "add_a_name": "Pievienot vārdu", "add_a_title": "Pievienot virsrakstu", + "add_birthday": "Pievienot dzimšanas dienu", "add_endpoint": "Pievienot galapunktu", "add_exclusion_pattern": "Pievienot izslēgšanas šablonu", "add_import_path": "Pievienot importa ceļu", @@ -88,11 +89,14 @@ "machine_learning_url_description": "Mašīnmācīšanās servera URL", "manage_concurrency": "Vienlaicīgas darbības pārvaldība", "manage_log_settings": "Žurnāla iestatījumu pārvaldība", + "map_dark_style": "Tumšais stils", "map_gps_settings": "Kartes un GPS iestatījumi", "map_gps_settings_description": "Karšu un GPS (apgrieztās ģeokodēšanas) iestatījumu pārvaldība", + "map_light_style": "Gaišais stils", "map_manage_reverse_geocoding_settings": "Reversās ģeokodēšanas iestatījumu pārvaldība", "map_settings": "Karte", "map_settings_description": "Kartes iestatījumu pārvaldība", + "map_style_description": "URL uz style.json kartes tēmu", "metadata_extraction_job": "Metadatu iegūšana", "metadata_settings": "Metadatu iestatījumi", "metadata_settings_description": "Metadatu iestatījumu pārvaldība", @@ -110,6 +114,10 @@ "notification_email_test_email_sent": "Uz {email} ir nosūtīts testa e-pasts. Lūdzu, pārbaudi savu iesūtni.", "notification_settings": "Paziņojumu iestatījumi", "notification_settings_description": "Paziņojumu iestatījumu, tostarp e-pasta, pārvaldība", + "oauth_auto_launch": "Palaist automātiski", + "oauth_auto_launch_description": "Pie navigācijas uz pieslēgšanās lapu automātiski uzsākt OAuth pieslēgšanās plūsmu", + "oauth_auto_register": "Automātiska reģistrācija", + "oauth_auto_register_description": "Pēc pieslēgšanās ar OAuth automātiski reģistrēt jaunus lietotājus", "oauth_button_text": "Pogas teksts", "oauth_enable_description": "Pieslēgties ar OAuth", "oauth_settings": "OAuth", @@ -124,6 +132,7 @@ "require_password_change_on_login": "Pieprasīt lietotājam mainīt paroli pēc pirmās pieteikšanās", "scanning_library": "Skenē bibliotēku", "search_jobs": "Meklēt uzdevumus…", + "server_public_users": "Publiski lietotāji", "server_settings": "Servera iestatījumi", "server_settings_description": "Servera iestatījumu pārvaldība", "server_welcome_message": "Sveiciena ziņa", @@ -134,7 +143,12 @@ "storage_template_path_length": "Aptuvenais ceļa garuma ierobežojums: {length, number}/{limit, number}", "storage_template_settings": "Krātuves veidne", "system_settings": "Sistēmas iestatījumi", + "template_email_available_tags": "Sagatavē var izmantot šos mainīgos: {tags}", + "template_email_if_empty": "Ja sagatave ir tukša, tiks izmantots noklusējuma e-pasts.", + "template_email_invite_album": "Albuma ielūguma sagatave", "template_email_preview": "Priekšskatījums", + "template_email_settings": "E-pasta sagataves", + "template_email_update_album": "Atjaunināt albuma sagatavi", "template_settings_description": "Pielāgotu paziņojumu veidņu pārvaldība", "theme_custom_css_settings": "Pielāgots CSS", "theme_custom_css_settings_description": "Cascading Style Sheets ļauj pielāgot Immich izskatu.", @@ -178,6 +192,7 @@ "age_years": "{years, plural, zero {# gadu} one {# gads} other {# gadi}}", "album_added": "Albums pievienots", "album_cover_updated": "Albuma attēls atjaunināts", + "album_deleted": "Albums dzēsts", "album_info_card_backup_album_excluded": "NEIEKĻAUTS", "album_info_card_backup_album_included": "IEKĻAUTS", "album_info_updated": "Albuma informācija atjaunināta", @@ -196,6 +211,10 @@ "album_viewer_appbar_share_to": "Kopīgot Uz", "album_viewer_page_share_add_users": "Pievienot lietotājus", "albums": "Albumi", + "albums_default_sort_order": "Albuma noklusējuma kārtošanas secība", + "albums_default_sort_order_description": "Sākotnējā failu kārtošanas secība, veidojot jaunus albumus.", + "albums_feature_description": "Failu kolekcijas, kuras var koplietot ar citiem lietotājiem.", + "albums_on_device_count": "Albumi ierīcē ({count})", "all": "Viss", "all_albums": "Visi albumi", "all_people": "Visi cilvēki", @@ -204,6 +223,7 @@ "allow_edits": "Atļaut labošanu", "allow_public_user_to_download": "Atļaut lejupielādēt publiskiem lietotājiem", "allow_public_user_to_upload": "Atļaut augšupielādēt publiskiem lietotājiem", + "alt_text_qr_code": "QR koda attēls", "anti_clockwise": "Pretēji pulksteņrādītāja virzienam", "api_key": "API atslēga", "api_key_description": "Šī vērtība tiks parādīta tikai vienu reizi. Nokopējiet to pirms loga aizvēršanas.", @@ -219,7 +239,9 @@ "are_these_the_same_person": "Vai šī ir tā pati persona?", "asset_action_delete_err_read_only": "Nevar dzēst read only aktīvu(-s), notiek izlaišana", "asset_action_share_err_offline": "Nevar iegūt bezsaistes aktīvu(-s), notiek izlaišana", + "asset_added_to_album": "Pievienots albumam", "asset_adding_to_album": "Pievieno albumam…", + "asset_description_updated": "Faila apraksts ir atjaunināts", "asset_list_group_by_sub_title": "Grupēt pēc", "asset_list_layout_settings_dynamic_layout_title": "Dinamiskais izkārtojums", "asset_list_layout_settings_group_automatically": "Automātiski", @@ -228,12 +250,14 @@ "asset_list_layout_sub_title": "Izvietojums", "asset_list_settings_subtitle": "Fotorežģa izkārtojuma iestatījumi", "asset_list_settings_title": "Fotorežģis", + "asset_uploaded": "Augšupielādēts", "asset_uploading": "Augšupielādē…", "asset_viewer_settings_title": "Aktīvu Skatītājs", "assets": "aktīvi", "authorized_devices": "Autorizētās ierīces", "automatic_endpoint_switching_title": "Automātiska URL pārslēgšana", "back": "Atpakaļ", + "backup": "Dublēšana", "backup_album_selection_page_albums_device": "Albumi ierīcē ({count})", "backup_album_selection_page_albums_tap": "Pieskarieties, lai iekļautu, veiciet dubultskārienu, lai izslēgtu", "backup_album_selection_page_assets_scatter": "Aktīvi var būt izmētāti pa vairākiem albumiem. Tādējādi dublēšanas procesā albumus var iekļaut vai neiekļaut.", @@ -319,6 +343,7 @@ "cache_settings_title": "Kešdarbes iestatījumi", "camera": "Fotokamera", "cancel": "Atcelt", + "canceling": "Atceļ", "cannot_merge_people": "Nevar apvienot cilvēkus", "change_date": "Mainīt datumu", "change_description": "Mainīt aprakstu", @@ -339,6 +364,7 @@ "clear": "Notīrīt", "clear_all": "Notīrīt visu", "clear_value": "Notīrīt vērtību", + "client_cert_subtitle": "Atbalsta tikai PKCS12 (.p12, .pfx) formātu. Sertifikātu importēšana/noņemšana ir pieejama tikai pirms pieslēgšanās", "client_cert_title": "SSL klienta sertifikāts", "clockwise": "Pulksteņrādītāja virzienā", "close": "Aizvērt", @@ -459,6 +485,7 @@ "empty_trash": "Iztukšot atkritni", "enable_biometric_auth_description": "Lai iespējotu biometrisko autentifikāciju, Ievadiet savu PIN kodu", "end_date": "Beigu datums", + "enqueued": "Ierindots", "enter_wifi_name": "Ievadi Wi-Fi nosaukumu", "enter_your_pin_code": "Ievadi savu PIN kodu", "enter_your_pin_code_subtitle": "Ievadi savu PIN kodu, lai piekļūtu slēgtajai mapei", @@ -468,6 +495,9 @@ "cant_get_faces": "Nevar iegūt sejas", "cant_search_people": "Neizdevās veikt peronu meklēšanu", "failed_to_create_album": "Neizdevās izveidot albumu", + "import_path_already_exists": "Šis importa ceļš jau pastāv.", + "incorrect_email_or_password": "Nepareizs e-pasts vai parole", + "profile_picture_transparent_pixels": "Profila attēlos nevar būt caurspīdīgi pikseļi. Lūdzu, palielini un/vai pārvieto attēlu.", "unable_to_change_description": "Neizdevās nomainīt aprakstu", "unable_to_create_user": "Neizdevās izveidot lietotāju", "unable_to_delete_user": "Neizdevās dzēst lietotāju", @@ -494,6 +524,8 @@ "expired": "Derīguma termiņš beidzās", "explore": "Izpētīt", "export": "Eksportēt", + "export_database": "Eksportēt datubāzi", + "export_database_description": "Eksportēt SQLite datubāzi", "extension": "Paplašinājums", "external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom", "failed_to_authenticate": "Neizdevās autentificēties", @@ -504,7 +536,15 @@ "features_setting_description": "Lietotnes funkciju pārvaldība", "filename": "Faila nosaukums", "filetype": "Faila tips", + "folder": "Mape", + "folder_not_found": "Mape nav atrasta", "folders": "Mapes", + "gcast_enabled": "Google Cast", + "get_help": "Saņemt palīdzību", + "getting_started": "Pirmie soļi", + "go_back": "Doties atpakaļ", + "go_to_folder": "Doties uz mapi", + "go_to_search": "Doties uz meklēšanu", "haptic_feedback_switch": "Iestatīt haptisku reakciju", "haptic_feedback_title": "Haptiska Reakcija", "has_quota": "Ir kvota", @@ -529,10 +569,12 @@ "hour": "Stunda", "id": "ID", "image": "Attēls", + "image_saved_successfully": "Attēls saglabāts", "image_viewer_page_state_provider_download_started": "Lejupielāde Uzsākta", "image_viewer_page_state_provider_download_success": "Lejupielāde izdevās", "image_viewer_page_state_provider_share_error": "Kopīgošanas Kļūda", "immich_logo": "Immich logo", + "immich_web_interface": "Immich tīmekļa saskarne", "import_from_json": "Importēt no JSON", "import_path": "Importa ceļš", "in_albums": "{count, plural, one {# albumā} other {# albumos}}", @@ -545,18 +587,26 @@ "night_at_midnight": "Katru dienu pusnaktī", "night_at_twoam": "Katru dienu 2.00 naktī" }, + "invalid_date": "Nederīgs datums", + "invalid_date_format": "Nederīgs datuma formāts", "invite_people": "Ielūgt cilvēkus", "invite_to_album": "Uzaicināt albumā", + "ios_debug_info_last_sync_at": "Pēdējā sinhronizācija {dateTime}", + "ios_debug_info_no_processes_queued": "Nav ierindotu fona procesu", "jobs": "Uzdevumi", "keep": "Paturēt", "keep_all": "Paturēt visus", + "keep_this_delete_others": "Paturēt šo, dzēst citus", "keyboard_shortcuts": "Tastatūras saīsnes", "language": "Valoda", + "language_search_hint": "Meklēt valodas...", "language_setting_description": "Izvēlieties vēlamo valodu", + "large_files": "Lielie faili", "last_seen": "Pēdējo reizi redzēts", "latest_version": "Jaunākā versija", "latitude": "Ģeogrāfiskais platums", "leave": "Paturēt", + "lens_model": "Objektīva modelis", "let_others_respond": "Ļaut citiem atbildēt", "level": "Līmenis", "library": "Bibliotēka", @@ -566,6 +616,7 @@ "library_page_sort_created": "Jaunākais izveidotais", "library_page_sort_last_modified": "Pēdējo reizi modificēts", "library_page_sort_title": "Albuma virsraksts", + "licenses": "Licences", "list": "Saraksts", "loading": "Ielādē", "location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current WiFi network's name", @@ -600,7 +651,7 @@ "longitude": "Ģeogrāfiskais garums", "look": "Izskats", "loop_videos_description": "Iespējot, lai automātiski videoklips tiktu cikliski palaists detaļu skatītājā.", - "make": "Firma", + "make": "Ražotājs", "manage_shared_links": "Kopīgoto saišu pārvaldība", "manage_sharing_with_partners": "Koplietošanas ar partneriem pārvaldība", "manage_the_app_settings": "Lietotnes iestatījumu pārvaldība", @@ -609,7 +660,6 @@ "manage_your_devices": "Pieslēgto ierīču pārvaldība", "manage_your_oauth_connection": "OAuth savienojumu pārvaldība", "map": "Karte", - "map_assets_in_bound": "{count} fotoattēls", "map_assets_in_bounds": "{count} fotoattēli", "map_cannot_get_user_location": "Nevar iegūt lietotāja atrašanās vietu", "map_location_dialog_yes": "Jā", @@ -676,6 +726,9 @@ "next_memory": "Nākamā atmiņa", "no": "Nē", "no_albums_message": "Izveido albumu, lai organizētu savas fotogrāfijas un video", + "no_albums_with_name_yet": "Izskatās, ka tev vēl nav albumu ar šādu nosaukumu.", + "no_albums_yet": "Izskatās, ka tev vēl nav neviena albuma.", + "no_archived_assets_message": "Arhivē fotoattēlus un videoklipus, lai paslēptu tos no Fotoattēli skata", "no_assets_message": "NOKLIKŠĶINIET, LAI AUGŠUPIELĀDĒTU SAVU PIRMO FOTOATTĒLU", "no_assets_to_show": "Nav uzrādāmo aktīvu", "no_duplicates_found": "Dublikāti netika atrasti.", @@ -699,6 +752,10 @@ "official_immich_resources": "Oficiālie Immich resursi", "offline": "Bezsaistē", "ok": "Labi", + "onboarding": "Uzņemšana", + "onboarding_locale_description": "Izvēlies vēlamo valodu. To vēlāk var mainīt iestatījumos.", + "onboarding_theme_description": "Izvēlies savas instances krāsu motīvu. To vēlāk var mainīt iestatījumos.", + "onboarding_user_welcome_description": "Sāksim darbu!", "online": "Tiešsaistē", "only_favorites": "Tikai izlase", "open_in_map_view": "Atvērt kartes skatā", @@ -706,13 +763,16 @@ "open_the_search_filters": "Atvērt meklēšanas filtrus", "options": "Iestatījumi", "or": "vai", + "organize_your_library": "Bibliotēkas organizēšana", "original": "oriģināls", "other": "Citi", "other_devices": "Citas ierīces", "other_variables": "Citi mainīgie", "owned": "Īpašumā", "owner": "Īpašnieks", + "partner": "Partneris", "partner_can_access": "{partner} var piekļūt", + "partner_can_access_location": "Fotogrāfiju uzņemšanas vieta", "partner_list_user_photos": "{user} fotoattēli", "partner_list_view_all": "Apskatīt visu", "partner_page_empty_message": "Jūsu fotogrāfijas pagaidām nav kopīgotas ar nevienu partneri.", @@ -726,6 +786,7 @@ "password_does_not_match": "Parole nesakrīt", "path": "Ceļš", "pause": "Pauzēt", + "pause_memories": "Pauzēt atmiņas", "paused": "Nopauzēts", "people": "Cilvēki", "permission_onboarding_back": "Atpakaļ", @@ -738,48 +799,79 @@ "permission_onboarding_request": "Immich nepieciešama atļauja skatīt jūsu fotoattēlus un videoklipus.", "person": "Persona", "photos": "Fotoattēli", + "photos_and_videos": "Fotogrāfijas un video", "photos_from_previous_years": "Fotogrāfijas no iepriekšējiem gadiem", + "pick_a_location": "Izvēlies atrašanās vietu", "pin_verification": "PIN koda pārbaude", "place": "Atrašanās vieta", "places": "Vietas", + "play": "Atskaņot", + "play_memories": "Atskaņot atmiņas", "please_auth_to_access": "Lai piekļūtu, lūdzu, autentificējieties", "port": "Ports", "preferences_settings_title": "Iestatījumi", "preview": "Priekšskatījums", "previous": "Iepriekšējais", + "previous_memory": "Iepriekšējā atmiņa", "privacy": "Privātums", "profile": "Profils", "profile_drawer_app_logs": "Žurnāli", "profile_drawer_client_out_of_date_major": "Mobilā lietotne ir novecojusi. Lūdzu, atjaunini to uz jaunāko pamatversiju.", "profile_drawer_client_out_of_date_minor": "Mobilā lietotne ir novecojusi. Lūdzu, atjaunini to uz jaunāko papildversiju.", "profile_drawer_client_server_up_to_date": "Klients un serveris ir atjaunināti", + "profile_drawer_github": "GitHub", "profile_drawer_server_out_of_date_major": "Serveris ir novecojis. Lūdzu, atjaunini to uz jaunāko pamatversiju.", "profile_drawer_server_out_of_date_minor": "Serveris ir novecojis. Lūdzu, atjaunini to uz jaunāko papildversiju.", + "profile_image_of_user": "{user} profila attēls", + "profile_picture_set": "Profila attēls iestatīts.", + "public_album": "Publisks albums", "purchase_account_info": "Atbalstītājs", + "purchase_activated_subtitle": "Paldies, ka atbalstāt Immich un atvērtā koda programmatūru", + "purchase_activated_time": "Aktivizēts {date}", + "purchase_activated_title": "Tava atslēga ir sekmīgi aktivizēta", + "purchase_button_activate": "Aktivizēt", "purchase_button_buy": "Pirkt", + "purchase_button_buy_immich": "Iegādāties Immich", "purchase_button_never_show_again": "Nekad vairs nerādīt", "purchase_button_reminder": "Atgādināt man pēc 30 dienām", "purchase_button_remove_key": "Noņemt atslēgu", "purchase_button_select": "Izvēlēties", + "purchase_failed_activation": "Neizdevās aktivizēt! Lūdzu, pārbaudi savu e-pastu, lai iegūtu pareizo produkta atslēgu!", "purchase_individual_description_2": "Atbalstītāja statuss", "purchase_input_suggestion": "Vai tev ir produkta atslēga? Ievadi atslēgu zemāk", "purchase_license_subtitle": "Nopērc Immich licenci, lai atbalstītu turpmāku pakalpojuma attīstību", "purchase_lifetime_description": "Pirkums uz mūžu", "purchase_option_title": "IEGĀDES IESPĒJAS", + "purchase_panel_info_1": "Immich veidošanai ir nepieciešams daudz laika un pūļu, un pie tā strādā pilna laika inženieri, lai padarītu to pēc iespējas labāku. Mūsu misija ir panākt, lai atvērtā koda programmatūra un ētiska uzņēmējdarbības prakse kļūtu par ilgtspējīgu ienākumu avotu izstrādātājiem un izveidotu privātumu respektējošu ekosistēmu ar reālām alternatīvām ekspluatējošiem mākoņpakalpojumiem.", + "purchase_panel_info_2": "Tā kā mēs esam apņēmušies nepievienot maksas funkcionalitāti, šis pirkums nepiešķirs jums nekādas papildu Immich funkcijas. Mēs paļaujamies uz tādiem lietotājiem kā jūs, lai atbalstītu nepārtrauktu Immich attīstību.", "purchase_panel_title": "Atbalsti projektu", "purchase_remove_product_key": "Noņemt produkta atslēgu", + "purchase_remove_product_key_prompt": "Vai tiešām vēlaties noņemt produkta atslēgu?", "purchase_remove_server_product_key": "Noņemt servera produkta atslēgu", + "purchase_remove_server_product_key_prompt": "Vai tiešām vēlaties noņemt Servera produkta atslēgu?", "purchase_server_description_1": "Visam serverim", "purchase_server_description_2": "Atbalstītāja statuss", "purchase_server_title": "Serveris", "purchase_settings_server_activated": "Servera produkta atslēgu pārvalda administrators", + "queue_status": "Ierindo {count}/{total}", "rating_clear": "Noņemt vērtējumu", + "rating_description": "Rādīt EXIF vērtējumu informācijas panelī", + "reaction_options": "Reakcijas iespējas", "read_changelog": "Lasīt izmaiņu sarakstu", "recently_added_page_title": "Nesen Pievienotais", + "refresh_thumbnails": "Atsvaidzināt sīktēlus", + "refreshed": "Atsvaidzināts", "remove": "Noņemt", + "remove_assets_title": "Izņemt failus?", + "remove_deleted_assets": "Izņemt dzēstos failus", "remove_from_album": "Noņemt no albuma", + "remove_from_album_action_prompt": "No albuma izņemti {count} faili", "remove_from_favorites": "Noņemt no izlases", + "remove_from_lock_folder_action_prompt": "No slēgtās mapes izņemti {count} faili", "remove_from_locked_folder": "Izņemt no slēgtās mapes", + "remove_memory": "Noņemt atmiņu", + "remove_photo_from_memory": "Noņemt fotogrāfiju no šīs atmiņas", + "remove_url": "Noņemt URL", "remove_user": "Noņemt lietotāju", "removed_api_key": "Noņēma API atslēgu: {name}", "removed_from_archive": "Noņēma no arhīva", @@ -790,6 +882,11 @@ "replace_with_upload": "Aizstāt ar augšupielādi", "require_user_to_change_password_on_first_login": "Pieprasīt lietotājam mainīt paroli pēc pirmās pieteikšanās", "rescan": "Pārskenēt atkārtoti", + "reset": "Atiestatīt", + "reset_password": "Atiestatīt paroli", + "reset_people_visibility": "Atiestatīt cilvēku redzamību", + "reset_pin_code": "Atiestatīt PIN kodu", + "reset_to_default": "Atiestatīt noklusējuma iestatījumus", "resolve_duplicates": "Atrisināt dublēšanās gadījumus", "resolved_all_duplicates": "Visi dublikāti ir atrisināti", "restore": "Atjaunot", @@ -798,10 +895,12 @@ "resume": "Turpināt", "retry_upload": "Atkārtot augšupielādi", "review_duplicates": "Pārskatīt dublikātus", + "review_large_files": "Pārskatīt lielos failus", "role": "Loma", "role_editor": "Redaktors", "role_viewer": "Skatītājs", "save": "Saglabāt", + "save_to_gallery": "Saglabāt galerijā", "saved_api_key": "API atslēga saglabāta", "saved_profile": "Profils saglabāts", "saved_settings": "Iestatījumi saglabāti", @@ -813,9 +912,23 @@ "scanning_for_album": "Skenē albumu...", "search": "Meklēt", "search_albums": "Meklēt albumus", + "search_by_context": "Meklēt pēc konteksta", + "search_by_description": "Meklēt pēc apraksta", + "search_by_description_example": "Pārgājiens Līgatnē", + "search_by_filename": "Meklēt pēc faila nosaukuma vai paplašinājuma", "search_by_filename_example": "piemēram, IMG_1234.JPG vai PNG", + "search_camera_make": "Meklēt pēc fotokameras ražotāja...", + "search_camera_model": "Meklēt pēc fotokameras modeļa...", + "search_city": "Meklēt pēc pilsētas...", + "search_country": "Meklēt pēc valsts...", "search_filter_apply": "Lietot filtru", + "search_filter_camera_title": "Izvēlies fotokameras veidu", + "search_filter_date": "Datums", "search_filter_display_option_not_in_album": "Nav albumā", + "search_filter_filename": "Meklēt pēc faila nosaukuma", + "search_filter_location": "Atrašanās vieta", + "search_filter_location_title": "Izvēlies atrašanās vietu", + "search_for_existing_person": "Meklēt esošu personu", "search_no_people": "Nav cilvēku", "search_no_people_named": "Nav cilvēku ar vārdu \"{name}\"", "search_page_categories": "Kategorijas", @@ -836,6 +949,7 @@ "second": "Sekunde", "select_album_cover": "Izvēlieties albuma vāciņu", "select_all_duplicates": "Atlasīt visus dublikātus", + "select_from_computer": "Izvēlēties no datora", "select_photos": "Fotoattēlu Izvēle", "select_user_for_sharing_page_err_album": "Neizdevās izveidot albumu", "server_info_box_app_version": "Aplikācijas Versija", @@ -997,12 +1111,15 @@ "updated_at": "Atjaunināts", "updated_password": "Parole ir atjaunināta", "upload": "Augšupielādēt", + "upload_action_prompt": "{count} ierindoti augšupielādei", "upload_dialog_info": "Vai vēlaties veikt izvēlētā(-o) aktīva(-u) dublējumu uz servera?", "upload_dialog_title": "Augšupielādēt Aktīvu", + "upload_finished": "Augšupielāde pabeigta", "upload_status_duplicates": "Dublikāti", "upload_status_errors": "Kļūdas", "upload_status_uploaded": "Augšupielādēts", "upload_to_immich": "Augšupielādēt Immich ({count})", + "uploading_media": "Augšupielādē failus", "url": "URL", "usage": "Lietojums", "use_biometric": "Izmantot biometrisko autentifikāciju", @@ -1031,6 +1148,7 @@ "viewer_stack_use_as_main_asset": "Izmantot kā Galveno Aktīvu", "viewer_unstack": "At-Stekot", "waiting": "Gaida", + "warning": "Brīdinājums", "week": "Nedēļa", "welcome": "Laipni lūgti", "welcome_to_immich": "Laipni lūgti Immich", diff --git a/i18n/mk.json b/i18n/mk.json index 31d223cbd6..938d2158da 100644 --- a/i18n/mk.json +++ b/i18n/mk.json @@ -199,7 +199,6 @@ "level": "Ниво", "library": "Библиотека", "light": "Светло", - "link_options": "Опции за линк", "list": "Листа", "loading": "Вчитување", "log_out": "Одјави се", diff --git a/i18n/ml.json b/i18n/ml.json index 8d25cb5604..8787367bb6 100644 --- a/i18n/ml.json +++ b/i18n/ml.json @@ -3,10 +3,71 @@ "account": "അക്കൗണ്ട്", "account_settings": "അക്കൗണ്ട് സെറ്റിംഗ്സ്", "acknowledge": "അംഗീകരിക്കുക", + "action": "ആക്ഷന്‍", + "action_common_update": "പുതുക്കുക", + "actions": "പ്രവർത്തികൾ", + "active": "സജീവമായവ", + "activity": "പ്രവർത്തനങ്ങൾ", "add": "ചേർക്കുക", "add_a_description": "ഒരു വിവരണം ചേർക്കുക", "add_a_location": "ഒരു സ്ഥലം ചേർക്കുക", "add_a_name": "ഒരു പേര് ചേർക്കുക", "add_a_title": "ഒരു ശീർഷകം ചേർക്കുക", - "add_endpoint": "എൻഡ്പോയിന്റ് ചേർക്കുക" + "add_endpoint": "എൻഡ്പോയിന്റ് ചേർക്കുക", + "add_exclusion_pattern": "ഒഴിവാക്കാനുള്ള മാതൃക ചേർക്കുക", + "add_import_path": "ഇറക്കുമതി ചെയ്യുക", + "add_location": "സ്ഥലനാമം ചേര്‍ക്കുക", + "add_more_users": "കൂടുതല്‍ ഉപയോക്താക്കളെ ചേര്‍ക്കുക", + "add_partner": "പങ്കാളിയെ ചേര്‍ക്കുക", + "add_path": "പാത ചേര്‍ക്കുക", + "add_photos": "ചിത്രങ്ങള്‍ ചേര്‍ക്കുക", + "add_tag": "ടാഗ് ചേര്‍ക്കുക", + "add_to": "ചേര്‍ക്കുക", + "add_to_album": "ആല്‍ബത്തിലേക്ക് ചേര്‍ക്കുക", + "add_to_album_bottom_sheet_added": "{album} - ലേക്ക് ചേര്‍ത്തു", + "add_to_album_bottom_sheet_already_exists": "{album} ആൽബത്തിൽ ഇപ്പോള്‍ തന്നെ ഉണ്ട്", + "add_to_shared_album": "പങ്കിട്ട ആൽബത്തിലേക്ക് ചേർക്കുക", + "add_url": "URL ചേര്‍ക്കുക", + "added_to_archive": "ചരിത്രരേഖയായി (ആര്‍ക്കൈവ്) ചേര്‍ത്തിരിക്കുന്നു", + "added_to_favorites": "ഇഷ്ടപ്പെട്ടവയിലേക്ക് ചേര്‍ത്തു", + "added_to_favorites_count": "{count, number} ഇഷ്ടപ്പെട്ടവയിലേക്ക് ചേര്‍ത്തു", + "admin": { + "add_exclusion_pattern_description": "ഒഴിവാക്കൽ ചിഹ്നങ്ങള്‍ ചേർക്കുക. *, **, ? എന്നിവ ഉപയോഗിച്ചുള്ള ഗ്ലോബിംഗ് പിന്തുണയ്ക്കുന്നു. \"Raw\" എന്ന് പേരുള്ള ഏതെങ്കിലും ഡയറക്ടറിയിലെ എല്ലാ ഫയലുകളും അവഗണിക്കാൻ, \"**/Raw/**\" ഉപയോഗിക്കുക. \".tif\" ൽ അവസാനിക്കുന്ന എല്ലാ ഫയലുകളും അവഗണിക്കാൻ, \"**/*.tif\" ഉപയോഗിക്കുക. ഒരു പരിപൂർണ്ണമായ പാത അവഗണിക്കാൻ, \"/path/to/ignore/**\" ഉപയോഗിക്കുക.", + "admin_user": "ഭരണാധികാരി", + "asset_offline_description": "ഈ പുറത്തുള്ള ശേഖരത്തിലെ വസ്തുക്കള്‍ ഇനി ഡിസ്കിൽ കാണുന്നില്ല, അവയെ ട്രാഷിലേക്ക് നീക്കിയിരിക്കുന്നു. ഫയൽ ലൈബ്രറിക്കുള്ളിൽ നിന്ന് നീക്കിയിട്ടുണ്ടെങ്കിൽ, പുതിയ അനുബന്ധ വസ്തുവിനായി നിങ്ങളുടെ സമയക്രമം (ടൈംലൈന്‍) പരിശോധിക്കുക. ഈ വസ്തു പുനഃസ്ഥാപിക്കാൻ, താഴെയുള്ള ഫയൽ പാത്ത് ഇമ്മിച്ചിന് എത്തിപ്പെടാന്‍ കഴിയുമെന്ന് ഉറപ്പാക്കുകയും ശേഖരം പുനഃപരിശോധിക്കുകയും (സ്കാൻ) ചെയ്യുക.", + "authentication_settings": "ആധികാരികതാ സജ്ജീകരണങ്ങൾ", + "authentication_settings_description": "പാസ്സ്‌വേര്‍ഡ്‌, OAuth തുടങ്ങിയ സജ്ജീകരണങ്ങള്‍", + "authentication_settings_disable_all": "എല്ലാ പ്രവേശന (ലോഗിൻ) രീതികളും പ്രവർത്തനരഹിതമാക്കണമെന്ന് നിങ്ങൾക്ക് ഉറപ്പാണോ? പ്രവേശനങ്ങള്‍ പൂർണ്ണമായും പ്രവർത്തനരഹിതമാക്കപ്പെടും.", + "background_task_job": "പശ്ചാത്തല പ്രവര്‍ത്തികള്‍", + "backup_database": "ഡാറ്റാബേസ് ഡംമ്പ് ഉണ്ടാക്കുക", + "backup_database_enable_description": "ഡാറ്റാബേസ് ഡമ്പുകൾ പ്രാപ്തമാക്കുക", + "backup_keep_last_amount": "പഴയ ഡാറ്റാബേസ് ഡമ്പുകൾ എത്രയെണ്ണം സൂക്ഷിക്കണം", + "backup_settings": "ഡാറ്റാബേസ് ഡമ്പ് ക്രമീകരണങ്ങള്‍", + "backup_settings_description": "ഡാറ്റാബേസ് ഡമ്പ് ക്രമീകരണങ്ങൾ കൈകാര്യം ചെയ്യുക.", + "cleared_jobs": "{job} - ന്‍റെ ജോലികള്‍ മായ്ച്ചിരിക്കുന്നു", + "config_set_by_file": "ക്രമീകരണങ്ങള്‍ ഇപ്പോള്‍ ഒരു ക്രമീകരണ ഫയല്‍ വഴിയാണ് നിശ്ചയിക്കുന്നത്", + "confirm_delete_library": "{library} മായ്ച്ചു കളയണം എന്നുറപ്പാണോ?", + "confirm_delete_library_assets": "ഈ ശേഖരം ഇല്ലാതാക്കണം എന്ന് ഉറപ്പാണോ? ഇത് ഇമ്മിച്ചിൽ നിന്ന് {count, plural, one {# contained asset} other {all # contained assets}} ഇല്ലാതാക്കും, ഇത് പഴയപടിയാക്കാൻ കഴിയില്ല. ഫയലുകൾ ഡിസ്കിൽ തന്നെ തുടരും.", + "confirm_email_below": "തീര്‍ച്ചപ്പെടുത്താന്‍ {email} താഴെ കൊടുക്കുക", + "confirm_reprocess_all_faces": "എല്ലാ മുഖങ്ങളും വീണ്ടും കണ്ടെത്തണം എന്ന് ഉറപ്പാണോ? ഇത് ഇതിനകം പേരു ചേര്‍ത്ത മുഖങ്ങളെയും ആളുകളെയും മായ്ക്കും.", + "confirm_user_password_reset": "{user} എന്ന ഉപയോക്താവിന്‍റെ പാസ്സ്‌വേര്‍ഡ്‌ പുനഃക്രമീകരിക്കണം എന്നുറപ്പാണോ?", + "confirm_user_pin_code_reset": "{user} എന്ന ഉപയോക്താവിന്‍റെ PIN പുനഃക്രമീകരിക്കണം എന്നുറപ്പാണോ?", + "create_job": "ജോലി സൃഷ്ടിക്കുക", + "cron_expression": "ക്രോണ്‍ (cron) പ്രയോഗശൈലി", + "cron_expression_description": "ക്രോൺ ഫോർമാറ്റ് ഉപയോഗിച്ച് സ്കാനിംഗ് ഇടവേള സജ്ജമാക്കുക. കൂടുതൽ വിവരങ്ങൾക്ക് Crontab Guru സന്ദര്‍ശിക്കുക", + "cron_expression_presets": "മുന്‍കൂട്ടി തയ്യാര്‍ ചെയ്ത ക്രോണ്‍ പ്രവര്‍ത്തനശൈലികള്‍", + "disable_login": "ലോഗിന്‍ തടയുക", + "duplicate_detection_job_description": "സമാനമായ ചിത്രങ്ങൾ കണ്ടെത്താൻ വസ്തുവഹകളില്‍ യന്ത്രപഠനം പ്രവർത്തിപ്പിക്കുക. ഇത് സ്മാർട്ട് സര്‍ച്ചിനെ ആശ്രയിക്കുന്നു", + "exclusion_pattern_description": "നിങ്ങളുടെ ലൈബ്രറി സ്കാൻ ചെയ്യുമ്പോൾ ഫയലുകളും ഫോൾഡറുകളും അവഗണിക്കാൻ ഒഴിവാക്കല്‍ മാതൃകകള്‍ നിങ്ങളെ അനുവദിക്കുന്നു. RAW ഫയലുകൾ പോലുള്ള നിങ്ങൾക്ക് ഇറക്കുമതി ചെയ്യാൻ താൽപ്പര്യമില്ലാത്ത ഫയലുകൾ അടങ്ങിയ ഫോൾഡറുകൾ ഉണ്ടെങ്കിൽ ഇത് ഉപയോഗപ്രദമാണ്.", + "external_library_management": "ബാഹ്യമായശേഖരങ്ങളുടെ നിയന്ത്രണം", + "face_detection": "മുഖങ്ങള്‍ കണ്ടെത്തുക", + "face_detection_description": "യന്ത്രപഠനം ഉപയോഗിച്ച് വസ്തുക്കളിലെ മുഖങ്ങൾ കണ്ടെത്തുക. വീഡിയോകൾക്ക്, തംബ്‌നെയിൽ മാത്രമേ പരിഗണിക്കൂ. \"Refresh\" എല്ലാ വസ്തുക്കളും (വീണ്ടും) പ്രോസസ്സ് ചെയ്യുന്നു. കൂടാതെ \"Reset\" നിലവിലുള്ള എല്ലാ മുഖളും വിവരങ്ങളും മായ്‌ക്കുന്നു. \"Missing\" ഇതുവരെ ക്രമീകരിക്കാത്ത വസ്തുക്കളെ വരിയിലേക്ക് നിർത്തുന്നു. മുഖം തിരിച്ചറിയൽ പൂർത്തിയായ ശേഷം കണ്ടെത്തിയ മുഖങ്ങൾ മുഖം തിരിച്ചറിയലിനായി ക്യൂവിൽ നിർത്തും, അവയെ നിലവിലുള്ളതോ പുതിയതോ ആയ ആളുകളിലേക്ക് ഗ്രൂപ്പുചെയ്യും.", + "facial_recognition_job_description": "കണ്ടെത്തിയ മുഖങ്ങളെ ആളുകളുടെ കൂട്ടം ആക്കുക. മുഖം കണ്ടെത്തല്‍ ഘട്ടത്തിനു ശേഷമേ ഇത് ഉണ്ടാകൂ. \"Reset\" വീണ്ടും കൂട്ടങ്ങളെ ഉണ്ടാക്കും. \"Missing\" ആളെ നിയോഗിക്കാത്ത മുഖങ്ങളെ വരിയിലേക്ക് ചേര്‍ക്കുന്നു.", + "failed_job_command": "{job} എന്ന ജോലിക്ക് വേണ്ടിയുള്ള ആജ്ഞ {command} പരാജയപ്പെട്ടിരിക്കുന്നു", + "force_delete_user_warning": "മുന്നറിയിപ്പ്: ഇത് ഉപയോക്താവിനെയും എല്ലാ വസ്തുക്കളേയും ഉടനടി നീക്കം ചെയ്യും. ഇത് പഴയപടിയാക്കാനോ ഫയലുകൾ വീണ്ടെടുക്കാനോ കഴിയില്ല.", + "image_format": "ഘടന", + "image_format_description": "WebP ഉണ്ടാക്കാന്‍ സമയം എടുക്കും എങ്കിലും JPEG ഫയലുകളെക്കാള്‍ ചെറുതായിരിക്കും.", + "image_fullsize_description": "അധികവിവരങ്ങള്‍ ഒഴിവാക്കിയ ചിത്രം, വലുതാക്കി കാണിക്കുമ്പോള്‍ ഉപയോഗിക്കപ്പെടുന്നു", + "image_fullsize_enabled": "പൂര്‍ണ വലുപ്പത്തില്‍ ഉള്ള ചിത്രങ്ങള്‍ ഉണ്ടാക്കാന്‍പ്രാപ്തമാക്കുക" + } } diff --git a/i18n/mn.json b/i18n/mn.json index 8a18a1d5e5..85092fef0d 100644 --- a/i18n/mn.json +++ b/i18n/mn.json @@ -15,10 +15,13 @@ "add_a_name": "Нэр өгөх", "add_a_title": "Гарчиг оруулах", "add_endpoint": "Endpoint нэмэх", + "add_import_path": "Импортлох зам нэмэх", "add_location": "Байршил оруулах", "add_more_users": "Өөр хэрэглэгчид нэмэх", "add_partner": "Хамтрагч нэмэх", + "add_path": "Зам нэмэх", "add_photos": "Зураг нэмэх", + "add_tag": "Шошго нэмэх", "add_to_album": "Цомогт оруулах", "add_to_album_bottom_sheet_added": "{album}-д нэмлээ", "add_to_album_bottom_sheet_already_exists": "{album}-д аль хэдийн орсон байна", @@ -28,6 +31,7 @@ "added_to_favorites": "Дуртай зурганд нэмэх", "added_to_favorites_count": "Дуртай зурагнуудад {count, number} нэмэгдлээ", "admin": { + "admin_user": "Админ хэрэглэгч", "authentication_settings": "Танин нэвтрэлт тохиргоо", "authentication_settings_description": "Нууц үгийн удирдлага, OAuth болон бусад танин нэвтрэлтийн тохиргоо", "authentication_settings_disable_all": "Бүх нэвтрэх аргуудыг идэвхигүй болгохдоо итгэлтэй байна уу? Нэвтрэх үйлдэл бүрэн идэвхигүй болно.", @@ -35,11 +39,15 @@ "backup_database": "Өгөгдлийн сангийн дамп үүсгэх", "backup_database_enable_description": "Өгөгдлийн сангийн дамп идэвхижүүлэх", "backup_keep_last_amount": "Өмнөх хэдэн дампыг хадгалах вэ", + "backup_settings": "Датабаз дамп тохиргоо", + "backup_settings_description": "Датабазаас дамп хийх тохиргоонууд.", "config_set_by_file": "Тохиргоог одоогоор файлаас авч байна", "confirm_delete_library": "Та {library} гэсэн санг устгахдаа итгэлтэй байна уу?", "confirm_delete_library_assets": "Та энэ санг устгахдаа итгэлтэй байна уу? Энэ үйлдлээр таны {count, plural, one {# contained asset} other {all # contained assets}} серверээс устах бөгөөд буцаах боломжгүй. Гэхдээ файлууд диск дээрээ үлдэнэ.", "confirm_email_below": "Баталгаажуулахын тулд та \"{email}\" гэж бичнэ үү", "confirm_reprocess_all_faces": "Бүх царайг дахин процесс хийх үү? Тэгвэл бүх нэрс арилах болно.", + "confirm_user_password_reset": "{user}-ийн нууц үгийг дахин тохируулах уу?", + "confirm_user_pin_code_reset": "{user} хэрэглэгчийн PIN code дахин тохируулах уу?", "face_detection": "Нүүр илрүүлэх", "image_quality": "Чанар", "job_settings": "Ажлын тохиргоо", diff --git a/i18n/mr.json b/i18n/mr.json index 6c2ab7406c..781174c79e 100644 --- a/i18n/mr.json +++ b/i18n/mr.json @@ -4,6 +4,7 @@ "account_settings": "खाते व्यवस्था", "acknowledge": "मान्यता", "action": "कृती", + "action_common_update": "अद्ययावत", "actions": "कृत्ये", "active": "सक्रिय", "activity": "गतिविधि", @@ -13,6 +14,7 @@ "add_a_location": "एक स्थळ टाका", "add_a_name": "नाव टाका", "add_a_title": "शीर्षक टाका", + "add_endpoint": "एंडपॉइंट जोडा", "add_exclusion_pattern": "अपवाद नमुना जोडा", "add_import_path": "आयात मार्ग टाका", "add_location": "स्थळ टाका", @@ -20,8 +22,11 @@ "add_partner": "भागीदार जोडा", "add_path": "मार्ग टाका", "add_photos": "छायाचित्रे जोडा", + "add_tag": "टॅग जोडा", "add_to": "त्या मध्ये जोडा…", "add_to_album": "संग्रहात टाका", + "add_to_album_bottom_sheet_added": "{album} मध्ये जोडले गेले", + "add_to_album_bottom_sheet_already_exists": "आधीच {album} मध्ये आहे", "add_to_shared_album": "सामायिक संग्रहात टाका", "add_url": "URL जोडा", "added_to_archive": "संग्रहालयात जोडले", @@ -29,6 +34,7 @@ "added_to_favorites_count": "आवडत्यात {count, number} टाकले", "admin": { "add_exclusion_pattern_description": "अपवाद अनुकूलन जोडा. ** आणि ? या उपयोगात ग्लोबिंग समर्थित आहे. कोणत्याही \"Raw\" नावाच्या निर्देशिकेमधील सर्व खतावण्या दुर्लक्षीत करण्यासाठी \"/Raw/\" वापरा. \".tif\" या सामान्य पथावर समाप्त असलेल्या सर्व खतावण्या दुर्लक्षीत करण्यासाठी \"**/.tif\" वापरा. विशिष्ट पथ दुर्लक्ष करण्यासाठी \"/path/to/ignore/**\" वापरा.", + "admin_user": "प्रशासन वापरकर्ता", "asset_offline_description": "ही बाह्य संग्रहालय संसाधने डिस्कवर नाहीत आणि ट्रॅशमध्ये विस्थापित केली गेली आहेत. जर फाइल संग्रहालयामध्ये विस्थापित केली गेली आहे, तर नवीन संगत संसाधन किंव्हा रोजीनिशी मध्ये तपासा. हा संसाधन वापर करण्यासाठी कृपया निम्नलिखित खतावणी पथाला इम्मीच द्वारा वापरू शकतो याची तपासणी करा आणि तो संग्रहालय चाळा.", "authentication_settings": "प्रमाणीकरण साधक", "authentication_settings_description": "परवलीचा शब्द, OAuth आणि अन्य प्रमाणीकरण प्रबंधन करा", @@ -47,6 +53,7 @@ "confirm_email_below": "पुष्टी करण्या साठी, खाली \"{email}\" टंकलिखित करा", "confirm_reprocess_all_faces": "तुम्हाला खात्री आहे का की तुम्हाला सर्व चेहऱ्यांवर पुन्हा प्रक्रिया करायची आहे? यामुळे नाव दिलेले लोकही साफ होतील.", "confirm_user_password_reset": "तुम्हाला नक्की {user} चा परवलीचा शब्द बदलायचा आहे का?", + "confirm_user_pin_code_reset": "तुम्हाला नक्की {user} चा पिन कोड रीसेट करायचा आहे का?", "create_job": "कार्य बनवा", "cron_expression": "वेळापत्रक सूत्र", "cron_expression_description": "चाळन्याचे वेळापत्रक क्रॉन पद्धती ने करा. अधिक माहिती साठी पहा: क्रॉन गुरु", @@ -55,6 +62,16 @@ "duplicate_detection_job_description": "सारख्या छायाचित्रांचा शोध घेण्यासाठी यांत्रिकी प्रशिक्षण द्या. ही कार्यक्षमता चतुर शोधप्रणालीवर अवलंबून आहे", "exclusion_pattern_description": "आपले संग्रहालय चाळताना अपवाद नमुने आपल्याला खतावण्या आणि र्निर्देशिकेला दुर्लक्षीत करू देतात. आपल्याकडे कच्च्या खतावण्या सारख्या आयात करू इच्छित नसलेल्या असंपादित (RAW) खतावण्या असलेल्या निर्देशिका असल्यास हे उपयुक्त आहे.", "external_library_management": "बाह्य संग्रहालय व्यवस्थापन", - "face_detection": "मुख संशोधन" + "face_detection": "मुख संशोधन", + "face_detection_description": "मशीन लर्निंग वापरून मालमत्तांमधील चेहरे शोधा. व्हिडिओंसाठी, फक्त थंबनेलचा विचार केला जातो. \"रिफ्रेश\" (पुन्हा) सर्व मालमत्तांवर प्रक्रिया करते. \"रीसेट\" याव्यतिरिक्त सर्व वर्तमान चेहरा डेटा साफ करते. \"गहाळ\" मालमत्तांवर अद्याप प्रक्रिया न केलेल्या रांगेत ठेवते. शोधलेले चेहरे फेस डिटेक्शन पूर्ण झाल्यानंतर फेशियल रेकग्निशनसाठी रांगेत ठेवले जातील, त्यांना विद्यमान किंवा नवीन लोकांमध्ये गटबद्ध केले जाईल.", + "facial_recognition_job_description": "शोधलेले चेहरे लोकांमध्ये गटबद्ध करा. हे चरण चेहरा शोधणे पूर्ण झाल्यानंतर चालते. \"रीसेट करा\" (पुन्हा) सर्व चेहरे क्लस्टर कर. \"गहाळ\" चेहरे रांगेत समाविष्ट करते ज्यांना नियुक्त केलेली व्यक्ती नाही.", + "failed_job_command": "{command} कमांड जॉबसाठी अयशस्वी झाला: {job}", + "force_delete_user_warning": "सावधान: हे वापरकर्ता आणि सर्व मालमत्ता ताबडतोब काढून टाकेल. हे पूर्ववत करता येणार नाही आणि फायली पुनर्प्राप्त करता येणार नाहीत.", + "image_format": "फॉरमॅट", + "image_format_description": "WebP JPEG पेक्षा लहान फायली तयार करते, परंतु एन्कोड करण्यास हळू असते.", + "image_fullsize_description": "झूम इन केल्यावर वापरल्या जाणाऱ्या स्ट्रिप केलेल्या मेटाडेटासह पूर्ण आकाराची प्रतिमा", + "image_fullsize_enabled": "पूर्ण-आकारातील प्रतिमा निर्मिती", + "image_fullsize_enabled_description": "वेब-फ्रेंडली नसलेल्या फॉरमॅटसाठी पूर्ण-आकाराची प्रतिमा तयार करा. जेव्हा \"embedded preview\" चालुआसेल तेव्हा, \"embedded preview\" थेट रूपांतरणाशिवाय वापरले जातात. JPEG सारख्या वेब-फ्रेंडली फॉरमॅटवर परिणाम होत नाही.", + "image_fullsize_quality_description": "१-१०० पर्यंत पूर्ण-आकारातील प्रतिमा गुणवत्ता. जास्त तेव्हडे चांगले, परंतु मोठ्या फायली तयार करते." } } diff --git a/i18n/ms.json b/i18n/ms.json index cfe935102e..e7e0432a4c 100644 --- a/i18n/ms.json +++ b/i18n/ms.json @@ -14,6 +14,7 @@ "add_a_location": "Tambah lokasi", "add_a_name": "Tambah nama", "add_a_title": "Tambah tajuk", + "add_endpoint": "Tambah titik akhir", "add_exclusion_pattern": "Tambahkan corak pengecualian", "add_import_path": "Tambahkan laluan import", "add_location": "Tambah lokasi", @@ -21,6 +22,7 @@ "add_partner": "Tambah rakan", "add_path": "Tambah laluan", "add_photos": "Tambah gambar", + "add_tag": "Tambah tag", "add_to": "Tambah ke…", "add_to_album": "Tambah ke album", "add_to_album_bottom_sheet_added": "Dimasukkan ke {album}", @@ -32,17 +34,18 @@ "added_to_favorites_count": "Menambahkan {count, number} ke kegemaran", "admin": { "add_exclusion_pattern_description": "Tambahkan corak pengecualian. Globbing menggunakan *, **, dan ? disokong. Untuk mengabaikan semua fail dalam mana-mana direktori bernama \"Raw\", gunakan \"**/Raw/**\". Untuk mengabaikan semua fail yang berakhir dengan \".tif\", gunakan \"**/*.tif\". Untuk mengabaikan laluan mutlak, gunakan \"/path/to/ignore/**\".", + "admin_user": "Pengguna Pentadbir", "asset_offline_description": "Aset pustaka luaran ini tidak lagi ditemui pada cakera dan telah dialihkan ke sampah. Jika fail telah dialihkan dalam pustaka, semak garis masa anda untuk aset baharu yang sepadan. Untuk memulihkan aset ini, sila pastikan bahawa laluan fail di bawah boleh diakses oleh Immich dan mengimbas pustaka.", "authentication_settings": "Tetapan Pengesahan", "authentication_settings_description": "Urus kata laluan, OAuth dan tetapan pengesahan lain", "authentication_settings_disable_all": "Adakah anda pasti mahu melumpuhkan semua kaedah log masuk? Log masuk akan dilumpuhkan sepenuhnya.", "authentication_settings_reenable": "Untuk menghidupkan semula, guna Arahan Pelayan.", "background_task_job": "Tugas Latar Belakang", - "backup_database": "Sandar pangkalan data", - "backup_database_enable_description": "Aktifkan sandaran pangkalan data", - "backup_keep_last_amount": "Jumlah sandaran sebelumnya yang hendak disimpan", - "backup_settings": "Tetapan Sandaran", - "backup_settings_description": "Urus tetapan sandaran pangkalan data", + "backup_database": "Buat Salinan Pangkalan Data", + "backup_database_enable_description": "Dayakan salinan pangkalan data", + "backup_keep_last_amount": "Jumlah salinan pangkalan data sebelumnya untuk disimpan", + "backup_settings": "Tetapan Salinan Pangkalan Data", + "backup_settings_description": "Urus tetapan salinan pangkalan data.", "cleared_jobs": "Kerja telah dibersihkan untuk: {job}", "config_set_by_file": "Konfigurasi kini ditetapkan oleh fail konfigurasi", "confirm_delete_library": "Adakah anda pasti mahu memadamkan {library}?", @@ -72,7 +75,7 @@ "image_fullsize_quality_description": "Kualiti imej bersaiz penuh dari 1-100. Lebih tinggi adalah lebih baik, tetapi menghasilkan fail yang lebih besar.", "image_fullsize_title": "Tetapan Imej bersaiz penuh", "image_prefer_embedded_preview": "Cadangkan pratonton terbenam", - "image_prefer_embedded_preview_setting_description": "Gunakan pratonton terbenam dalam foto RAW sebagai input kepada pemprosesan imej apabila tersedia. Cara ini boleh menghasilkan warna yang lebih tepat untuk sesetengah imej, tetapi kualiti pratonton bergantung pada kamera dan imej mungkin mempunyai lebih banyak artifak mampatan.", + "image_prefer_embedded_preview_setting_description": "Gunakan pratonton terbenam dalam foto RAW sebagai input untuk pemprosesan imej apabila tersedia. Ini boleh menghasilkan warna yang lebih tepat untuk sesetengah imej, tetapi kualiti pratonton bergantung kepada kamera dan imej mungkin mengandungi lebih banyak artifak pemampatan.", "image_prefer_wide_gamut": "Cadangkan warna gamut yang luas", "image_prefer_wide_gamut_setting_description": "Gunakan Paparan P3 untuk lakaran kenit. Ini lebih baik mengekalkan kerancakan imej dengan ruang warna yang luas, tetapi imej mungkin kelihatan berbeza pada peranti lama dengan versi penyemak imbas lama. Imej sRGB disimpan sebagai sRGB untuk mengelakkan peralihan warna.", "image_preview_description": "Imej bersaiz sederhana dengan metadata yang dilucutkan, digunakan semasa melihat aset tunggal dan untuk pembelajaran mesin", @@ -102,7 +105,7 @@ "library_scanning_enable_description": "Dayakan pengimbasan perpustakaan berkala", "library_settings": "Perpustakaan Luaran", "library_settings_description": "Urus tetapan perpustakaan luaran", - "library_tasks_description": "Laksanakan tugas perpustakaan", + "library_tasks_description": "Imbas pustaka luaran untuk aset yang baru dan/atau telah diubah", "library_watching_enable_description": "Perhatikan perpustakaan luaran untuk perubahan fail", "library_watching_settings": "Perhati perpustakaan (EKSPERIMEN)", "library_watching_settings_description": "Perhati fail yang diubah secara automatik", @@ -137,7 +140,7 @@ "machine_learning_smart_search_description": "Cari imej secara semantik menggunakan pembenaman CLIP", "machine_learning_smart_search_enabled": "Dayakan carian pintar", "machine_learning_smart_search_enabled_description": "Jika ditutup, gambar-gambar tidak akan dikodkan untuk carian pintar.", - "machine_learning_url_description": "URL pelayan pembelajaran mesin. Jika lebih daripada satu URL disediakan, setiap pelayan akan dicuba satu demi satu sehingga satu menjawab dengan jayanya, mengikut urutan dari pertama hingga terakhir.", + "machine_learning_url_description": "URL pelayan pembelajaran mesin. Jika lebih daripada satu URL disediakan, setiap pelayan akan dicuba satu demi satu mengikut turutan, dari yang pertama hingga yang terakhir, sehingga salah satu memberi maklum balas yang berjaya. Pelayan yang tidak memberi maklum balas akan diabaikan sementara sehingga ia kembali dalam talian.", "manage_concurrency": "Urus Concurrency", "manage_log_settings": "Urus tetapan log", "map_dark_style": "Tema gelap", @@ -146,13 +149,15 @@ "map_gps_settings_description": "Urus Tetapan Peta & GPS (Geokod Terbalik)", "map_implications": "Ciri peta bergantung pada perkhidmatan jubin luaran (tiles.immich.cloud)", "map_light_style": "Tema terang", - "map_manage_reverse_geocoding_settings": "Urus tetapan Geocoding Songsang", + "map_manage_reverse_geocoding_settings": "Urus tetapan Penentuan Alamat Songsang", "map_reverse_geocoding": "Geokoding Sonsang", "map_reverse_geocoding_enable_description": "Dayakan pengekodan geo terbalik", "map_reverse_geocoding_settings": "Tetapan Pengekodan Geo Terbalik", "map_settings": "Peta", "map_settings_description": "Urus tetapan peta", "map_style_description": "URL ke tema peta style.json", + "memory_cleanup_job": "Pembersihan memori", + "memory_generate_job": "Penjanaan memori", "metadata_extraction_job": "Sari metadata", "metadata_extraction_job_description": "Sari maklumat metadata dari setiap aset, seperti GPS, muka-muka, dan pelaraian", "metadata_faces_import_setting": "Dayakan import muka", @@ -161,12 +166,26 @@ "metadata_settings_description": "Urus tetapan metadata", "migration_job": "Migrasi", "migration_job_description": "Pindahkan imej kecil untuk aset-aset dan muka-muka kepada struktur folder terkini", + "nightly_tasks_cluster_faces_setting_description": "Jalankan pengecaman wajah kepada wajah baharu yang dijumpai", + "nightly_tasks_cluster_new_faces_setting": "Kumpulan wajah baharu", + "nightly_tasks_database_cleanup_setting": "Tugasan membersihkan pangkalan data", + "nightly_tasks_database_cleanup_setting_description": "Membersihkan data lama, luput dari pangkalan data", + "nightly_tasks_generate_memories_setting": "Menjana memori", + "nightly_tasks_generate_memories_setting_description": "Mencipta memori dari aset", + "nightly_tasks_missing_thumbnails_setting": "Menjana lakaran kecil yang hilang", + "nightly_tasks_missing_thumbnails_setting_description": "Aturan aset tanpa lakaran kecil untuk janaan lakaran kecil", + "nightly_tasks_settings": "Tetapan tugasan malam", + "nightly_tasks_settings_description": "Mengurus tugasan malam", + "nightly_tasks_start_time_setting": "Masa mula", + "nightly_tasks_start_time_setting_description": "Masa di mana pelayan mula bekerja pada tugasan malam", + "nightly_tasks_sync_quota_usage_setting": "Penyelarasan penggunaan kuota", + "nightly_tasks_sync_quota_usage_setting_description": "Kemaskini kuota simpanan pengguna, berdasarkan kepada penggunaan terkini", "no_paths_added": "Tiada laluan yang ditambah", "no_pattern_added": "Tiada corak ditambah", "note_apply_storage_label_previous_assets": "Nota: Untuk menggunakan Label Storan pada aset yang dimuat naik sebelum ini, jalankan", "note_cannot_be_changed_later": "NOTA: Ini tidak boleh diubah kemudian!", "notification_email_from_address": "Dari alamat", - "notification_email_from_address_description": "Alamat e-mel penghantar, sebagai contoh: \"Immich Photo Server \"", + "notification_email_from_address_description": "Alamat e-mel penghantar, sebagai contoh: \"Pelayan Gambar Immich \". Pastikan menggunakan alamat yang dibenarkan anda untuk menghantar e-mel.", "notification_email_host_description": "Hos e-mel pelayan (cth. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Abaikan ralat-ralat sijil", "notification_email_ignore_certificate_errors_description": "Abaikan ralat pengesahan sijil TLS (tidak disyorkan)", @@ -186,19 +205,24 @@ "oauth_auto_register": "Daftar secara automatik", "oauth_auto_register_description": "Daftar secara automatik pengguna-pengguna baharu selepas mendaftar masuk dengan OAuth", "oauth_button_text": "Teks butang", + "oauth_client_secret_description": "Diperlukan jika PKCE (Proof Key for Code Exchange) tidak disokong oleh penyedia OAuth", "oauth_enable_description": "Log masuk dengan OAuth", "oauth_mobile_redirect_uri": "URI ubah hala mudah alih", "oauth_mobile_redirect_uri_override": "Penggantian URI ubah hala mudah alih", "oauth_mobile_redirect_uri_override_description": "Aktifkan apabila pembekal OAuth tidak membenarkan URI mudah alih, seperti ''{callback}''", + "oauth_role_claim": "Tebus peranan", + "oauth_role_claim_description": "Automatik memberi kebenaran pentadbir berdasarkan tuntutan ini. Tuntutan ini mungkin mempunyai sama ada 'pengguna' atau 'pentadbir'.", "oauth_settings": "OAuth", - "oauth_settings_description": "Urus tetapan-tetapan log masuk OAuth", + "oauth_settings_description": "Urus tetapan log masuk OAuth", "oauth_settings_more_details": "Untuk maklumat lanjut tentang ciri ini, rujuk ke dokumen.", "oauth_storage_label_claim": "Tuntutan label storan", "oauth_storage_label_claim_description": "Tetapkan label storan pengguna secara automatik kepada nilai tuntutan ini.", "oauth_storage_quota_claim": "Tuntutan kuota storan", "oauth_storage_quota_claim_description": "Tetapkan kuota storan pengguna secara automatik kepada nilai tuntutan ini.", "oauth_storage_quota_default": "Kuota storan lalai (GiB)", - "oauth_storage_quota_default_description": "Kuota dalam GiB untuk digunakan apabila tiada tuntutan disediakan (Masukkan 0 untuk kuota tanpa had).", + "oauth_storage_quota_default_description": "Kuota dalam GiB yang akan digunakan jika tiada tuntutan disediakan.", + "oauth_timeout": "Had Masa Permintaan", + "oauth_timeout_description": "Had masa untuk permintaan dalam milisaat", "password_enable_description": "Log masuk dengan e-mel dan kata laluan", "password_settings": "Kata Laluan Log Masuk", "password_settings_description": "Urus tetapan-tetapan kata laluan log masuk", @@ -207,12 +231,12 @@ "quota_size_gib": "Saiz Kuota (GiB)", "refreshing_all_libraries": "Menyegarkan semua perpustakaan", "registration": "Pendaftaran Pentadbir", - "registration_description": "Memandangkan anda adalah pengguna pertama pada sistem, anda akan ditugaskan sebagai Admin dan bertanggungjawab untuk tugas pentadbiran, serta pengguna tambahan yang akan anda tambah.", + "registration_description": "Memandangkan anda adalah pengguna pertama pada sistem, anda akan ditugaskan sebagai Pentadbir dan bertanggungjawab untuk tugas pentadbiran, serta pengguna tambahan yang akan anda tambah.", "require_password_change_on_login": "Perlukan pengguna menukar kata laluan ketika log masuk pertama", "reset_settings_to_default": "Tetapkan semula tetapan kepada lalai", "reset_settings_to_recent_saved": "Tetapkan semula tetapan kepada tetapan yang disimpan baru-baru ini", "scanning_library": "Mengimbas perpustakaan", - "search_jobs": "Cari kerja…", + "search_jobs": "Cari tugasan…", "send_welcome_email": "Hantar e-mel alu-aluan", "server_external_domain_settings": "Domain luaran", "server_external_domain_settings_description": "Domain untuk pautan kongsi awam, termasuk http(s)://", @@ -222,7 +246,7 @@ "server_settings_description": "Urus tetapan pelayan", "server_welcome_message": "Mesej alu-aluan", "server_welcome_message_description": "Mesej yang dipaparkan pada halaman log masuk.", - "sidecar_job": "Metadata kereta sisi", + "sidecar_job": "Metadata sampingan", "sidecar_job_description": "Temui atau segerakkan metadata sampingan daripada sistem fail", "slideshow_duration_description": "Bilangan saat untuk memaparkan setiap imej", "smart_search_job_description": "Jalankan pembelajaran mesin pada aset-aset untuk menyokong carian pintar", @@ -233,9 +257,10 @@ "storage_template_hash_verification_enabled_description": "Mendayakan pengesahan hac, jangan lumpuhkan melainkan anda pasti akan implikasinya", "storage_template_migration": "Penghijrahan templat storan", "storage_template_migration_description": "Gunakan {template} semasa pada aset-aset yang dimuat naik sebelum ini", - "storage_template_migration_info": "Perubahan templat hanya akan digunakan pada aset baharu. Untuk menggunakan templat secara retroaktif pada aset-aset yang dimuat naik sebelum ini, jalankan {job}.", + "storage_template_migration_info": "Templat storan akan menukar semua sambungan fail kepada huruf kecil. Perubahan templat hanya akan digunakan untuk aset baru. Untuk menggunakan templat ini secara retroaktif pada aset yang telah dimuat naik sebelum ini, jalankan {job}.", "storage_template_migration_job": "Kerja Migrasi Templat Storan", "storage_template_more_details": "Untuk butiran lanjut tentang ciri ini, rujuk kepada Templat Storan dan implikasi", + "storage_template_onboarding_description_v2": "Apabila diaktifkan, ciri ini akan mengatur fail secara automatik berdasarkan templat yang ditetapkan oleh pengguna. Untuk maklumat lanjut, sila rujuk dokumentasi.", "storage_template_path_length": "Anggaran kepanjangan laluan: {length, number}/{limit, number}", "storage_template_settings": "Templat Storan", "storage_template_settings_description": "Urus struktur folder dan nama fail aset dimuat naik", @@ -250,7 +275,7 @@ "template_email_update_album": "Templat Kemas kini Album", "template_email_welcome": "Templat e-mel alu-aluan", "template_settings": "Templat Pemberitahuan", - "template_settings_description": "Urus templat tersuai untuk pemberitahuan.", + "template_settings_description": "Urus templat tersuai untuk notifikasi", "theme_custom_css_settings": "CSS tersuai", "theme_custom_css_settings_description": "Lembaran Gaya Lata membolehkan reka bentuk Immich disuaikan.", "theme_settings": "Tetapan Tema", @@ -282,7 +307,7 @@ "transcoding_encoding_options": "Pilihan Pengekodan", "transcoding_encoding_options_description": "Tetapkan codec, resolusi, kualiti dan pilihan lain untuk video yang dikodkan", "transcoding_hardware_acceleration": "Pecutan Perkakasan", - "transcoding_hardware_acceleration_description": "Eksperimen; lebih pantas, tetapi akan mempunyai kualiti yang lebih rendah pada kadar bit yang sama", + "transcoding_hardware_acceleration_description": "Eksperimen: pengekodan semula yang lebih pantas tetapi mungkin mengurangkan kualiti pada kadar bit yang sama", "transcoding_hardware_decoding": "Penyahkodan perkakasan", "transcoding_hardware_decoding_setting_description": "Mendayakan pecutan hujung ke hujung dan bukannya hanya mempercepatkan pengekodan. Mungkin tidak berfungsi pada semua video.", "transcoding_max_b_frames": "Bingkai-B maksimum", @@ -311,8 +336,61 @@ "transcoding_threads_description": "Nilai yang lebih tinggi membawa kepada pengekodan yang lebih pantas, tetapi meninggalkan lebih sedikit ruang untuk pemproses tugas lain semasa aktif. Nilai ini tidak boleh lebih daripada bilangan teras CPU. Memaksimumkan penggunaan jika ditetapkan kepada 0.", "transcoding_tone_mapping": "Pemetaan nada", "transcoding_tone_mapping_description": "Percubaan untuk mengekalkan penampilan video HDR apabila ditukar kepada SDR. Setiap algoritma membuat pertukaran yang berbeza untuk warna, perincian dan kecerahan. Hable mengekalkan perincian, Mobius mengekalkan warna, dan Reinhard mengekalkan kecerahan.", - "transcoding_transcode_policy": "Dasar transkod" + "transcoding_transcode_policy": "Dasar transkod", + "transcoding_transcode_policy_description": "Dasar untuk bila video perlu ditranskod. Video HDR akan sentiasa ditranskod (kecuali jika pengekodan semula dinyahdayakan).", + "transcoding_two_pass_encoding": "Pengekodan dua lelaran", + "transcoding_two_pass_encoding_setting_description": "Transkod dalam dua lelaran untuk menghasilkan video yang ditranskod dengan kualiti lebih baik. Apabila kadar bit maksimum diaktifkan (diperlukan untuk berfungsi dengan H.264 dan HEVC), mod ini akan menggunakan julat kadar bit berdasarkan kadar bit maksimum dan mengabaikan CRF. Untuk VP9, CRF boleh digunakan jika kadar bit maksimum dinyahdayakan.", + "transcoding_video_codec": "Kodek video", + "transcoding_video_codec_description": "VP9 mempunyai kecekapan tinggi dan keserasian web yang baik, tetapi mengambil masa lebih lama untuk ditranskod. HEVC memberikan prestasi yang serupa, tetapi kurang serasi dengan web. H.264 sangat serasi dan pantas untuk ditranskod, tetapi menghasilkan fail yang jauh lebih besar. AV1 ialah kodek paling cekap tetapi tidak disokong pada peranti lama.", + "trash_enabled_description": "Dayakan ciri Tong Sampah", + "trash_number_of_days": "Bilangan hari", + "trash_number_of_days_description": "Bilangan hari untuk menyimpan aset dalam tong sampah sebelum dipadam secara kekal", + "trash_settings": "Tetapan Tong Sampah", + "trash_settings_description": "Urus tetapan tong sampah", + "user_cleanup_job": "Pembersihan pengguna", + "user_delete_delay": "Akaun dan aset {user} akan dijadualkan untuk dipadam secara kekal dalam {delay, plural, one {# hari} other {# hari}}.", + "user_delete_delay_settings": "Kelewatan pemadaman", + "user_delete_delay_settings_description": "Bilangan hari selepas penghapusan sebelum akaun dan aset pengguna dipadam secara kekal. Tugasan pemadaman pengguna dijalankan pada tengah malam untuk menyemak pengguna yang sedia untuk dipadam. Perubahan pada tetapan ini akan dinilai semasa pelaksanaan seterusnya.", + "user_delete_immediately": "Akaun dan aset {user} akan dimasukkan ke dalam baris gilir untuk dipadam secara kekal serta-merta.", + "user_delete_immediately_checkbox": "Masukkan pengguna dan aset ke dalam baris gilir untuk dipadam serta-merta", + "user_details": "Butiran Pengguna", + "user_management": "Pengurusan Pengguna", + "user_password_has_been_reset": "Katalaluan pengguna telah ditetapkan semula:", + "user_password_reset_description": "Sila berikan katalaluan sementara kepada pengguna dan maklumkan bahawa mereka perlu menukar katalaluan semasa log masuk yang seterusnya.", + "user_restore_description": "Akaun {user} akan dipulihkan.", + "user_restore_scheduled_removal": "Pulihkan pengguna – pemadaman dijadualkan pada {date, date, long}", + "user_settings": "Tetapan Pengguna", + "user_settings_description": "Urus tetapan pengguna", + "user_successfully_removed": "Pengguna {email} telah berjaya dipadam.", + "version_check_enabled_description": "Dayakan semakan versi", + "version_check_implications": "Ciri semakan versi bergantung kepada komunikasi berkala dengan github.com", + "version_check_settings": "Semakan Versi", + "version_check_settings_description": "Dayakan/nyahdayakan notifikasi versi baharu", + "video_conversion_job": "Transkod video", + "video_conversion_job_description": "Transkod video untuk keserasian yang lebih luas dengan pelayar dan peranti" }, + "admin_email": "Emel Pentadbir", + "admin_password": "Kata laluan Pentadbir", + "administration": "Pentadbiran", + "advanced": "Lanjutan", + "advanced_settings_beta_timeline_subtitle": "Cuba pengalaman aplikasi baharu", + "advanced_settings_beta_timeline_title": "Garis masa beta", + "advanced_settings_enable_alternate_media_filter_subtitle": "Gunakan pilihan ini untuk menapis media semasa penyegerakan berdasarkan kriteria alternatif. Hanya cuba jika anda menghadapi masalah dengan aplikasi mengesan semua album.", + "advanced_settings_enable_alternate_media_filter_title": "[EKSPERIMENTAL] Gunakan penapis penyelarasan album peranti alternatif", + "advanced_settings_log_level_title": "Tahap log: {level}", + "advanced_settings_prefer_remote_subtitle": "Sesetengah peranti sangat perlahan untuk memuatkan imej kecil daripada aset lokal. Aktifkan tetapan ini untuk memuatkan imej dari jauh sebagai gantinya.", + "advanced_settings_prefer_remote_title": "Utamakan imej jauh", + "advanced_settings_proxy_headers_subtitle": "Tentukan pengepala proksi yang perlu dihantar oleh Immich dengan setiap permintaan rangkaian", + "advanced_settings_proxy_headers_title": "Pengepala Proksi", + "advanced_settings_self_signed_ssl_subtitle": "Langkau pengesahan sijil SSL untuk titik hujung pelayan. Diperlukan untuk sijil yang ditandatangani sendiri.", + "advanced_settings_self_signed_ssl_title": "Benarkan sijil SSL yang ditandatangani sendiri", + "advanced_settings_sync_remote_deletions_subtitle": "Automatik memadam atau memulihkan satu asset di peranti ini apabila tindakan itu diambil di dalam laman sesawang", + "advanced_settings_sync_remote_deletions_title": "Selaraskan pemadaman kawalan jauh [UJI KAJI]", + "advanced_settings_tile_subtitle": "Tetapan lanjutan pengguna", + "advanced_settings_troubleshooting_subtitle": "Dayakan ciri tambahan untuk menyelesaikan masalah", + "advanced_settings_troubleshooting_title": "Menyelesaikan masalah", + "age_months": "Umur {bulan, plural, satu {# bulan} lain {# bulan}}", + "age_year_months": "Umur 1 tahun, {bulan, plural, satu {# bulan} lain {# bulan}}", "deduplication_criteria_1": "Saiz imej dalam bait", "deduplication_criteria_2": "Kiraan data EXIF", "deduplication_info": "Maklumat Pendeduplikasian", @@ -361,5 +439,6 @@ "year": "Tahun", "yes": "Ya", "you_dont_have_any_shared_links": "Anda tidak mempunyai apa-apa pautan yang dikongsi", + "your_wifi_name": "Nama Wi-Fi anda", "zoom_image": "Zum Gambar" } diff --git a/i18n/nb_NO.json b/i18n/nb_NO.json index 3478262019..1d8a3455e7 100644 --- a/i18n/nb_NO.json +++ b/i18n/nb_NO.json @@ -14,6 +14,7 @@ "add_a_location": "Legg til sted", "add_a_name": "Legg til navn", "add_a_title": "Legg til tittel", + "add_birthday": "Legg til bursdag", "add_endpoint": "API endepunkt", "add_exclusion_pattern": "Legg til ekskluderingsmønster", "add_import_path": "Legg til importsti", @@ -22,8 +23,8 @@ "add_partner": "Legg til partner", "add_path": "Legg til sti", "add_photos": "Legg til bilder", - "add_tag": "Legg til tag", - "add_to": "Legg til…", + "add_tag": "Legg til tagg", + "add_to": "Legg til i…", "add_to_album": "Legg til album", "add_to_album_bottom_sheet_added": "Lagt til i {album}", "add_to_album_bottom_sheet_already_exists": "Allerede i {album}", @@ -35,7 +36,7 @@ "admin": { "add_exclusion_pattern_description": "Legg til ekskluderingsmønstre. Globbing med *, ** og ? støttes. For å ignorere alle filer i en hvilken som helst mappe som heter \"Raw\", bruk \"**/Raw/**\". For å ignorere alle filer som slutter på \".tif\", bruk \"**/*.tif\". For å ignorere en absolutt filplassering, bruk \"/filsti/til/ignorer/**\".", "admin_user": "Administrasjonsbruker", - "asset_offline_description": "Denne eksterne bibliotekressursen finnes ikke lenger på disk og har blitt flyttet til papirkurven. Hvis filen ble flyttet innad i biblioteket, sjekk tidslinjen din for den tilsvarende ressursen. For å gjenopprette ressursen, vennligst sørg for at filstien under er tilgjengelig for Immich og skan biblioteket.", + "asset_offline_description": "Dette eksterne biblioteksobjektet finnes ikke lenger på disk og har blitt flyttet til papirkurven. Hvis filen ble flyttet innad i biblioteket, se etter det tilsvarende objektet i tidslinjen din. For å gjenopprette objektet, vennligst sørg for at filstien under er tilgjengelig for Immich og skann biblioteket.", "authentication_settings": "Godkjenningsinnstillinger", "authentication_settings_description": "Administrer passord, OAuth, og andre innstillinger for autentisering", "authentication_settings_disable_all": "Er du sikker på at du ønsker å deaktivere alle innloggingsmetoder? Innlogging vil bli fullstendig deaktivert.", @@ -44,16 +45,23 @@ "backup_database": "Opprett database-dump", "backup_database_enable_description": "Aktiver database-dump", "backup_keep_last_amount": "Antall database-dumps å beholde", + "backup_onboarding_1_description": "ekstern kopi i skyen eller på et annet fysisk sted.", + "backup_onboarding_2_description": "lokale kopier på forsskjellige enheter. Dette inkluderer hovedfilene og en lokal sikkerhetskopi av disse filene.", + "backup_onboarding_3_description": "totale kopier av dataene dine, inkludert originalfilene. Dette inkluderer én ekstern kopi og to lokale kopier.", + "backup_onboarding_description": "En 3-2-1 sikkerhetskopieringsstrategi anbefales for å beskytte dataene dine. Du bør beholde kopier av opplastede bilder/videoer samt Immich-databasen for en omfattende sikkerhetskopieringsløsning.", + "backup_onboarding_footer": "For mer informasjon om sikkerhetskopiering av Immich, se dokumentasjonen.", + "backup_onboarding_parts_title": "En 3-2-1 sikkerhetskopi inkluderer:", + "backup_onboarding_title": "Sikkerhetskopier", "backup_settings": "Database-dump instillinger", "backup_settings_description": "Håndter innstillinger for database-dump.", "cleared_jobs": "Ryddet opp jobber for: {job}", "config_set_by_file": "Konfigurasjonen er for øyeblikket satt av en konfigurasjonsfil", "confirm_delete_library": "Er du sikker på at du vil slette biblioteket {library}?", - "confirm_delete_library_assets": "Er du sikker på at du vil slette dette biblioteket? Dette vil slette alle {count, plural, one {# contained asset} other {all # contained assets}} tilhørende eiendeler fra Immich og kan ikke angres. Filene vil forbli på disken.", + "confirm_delete_library_assets": "Er du sikker på at du vil slette dette biblioteket? Dette vil slette alt innhold ({count, plural, one {# objekt} other {# objekter}}) og tilhørende eiendeler fra Immich og kan ikke angres. Filene vil forbli på disken.", "confirm_email_below": "For å bekrefte, skriv inn \"{email}\" nedenfor", "confirm_reprocess_all_faces": "Er du sikker på at du vil behandle alle ansikter på nytt? Dette vil også fjerne navngitte personer.", "confirm_user_password_reset": "Er du sikker på at du vil tilbakestille passordet til {user}?", - "confirm_user_pin_code_reset": "Er du sikker på at du vil resette {user}'s PIN kode?", + "confirm_user_pin_code_reset": "Er du sikker på at du vil tilbakestille PIN-koden til {user} ?", "create_job": "Lag jobb", "cron_expression": "Cron uttrykk", "cron_expression_description": "Still inn skanneintervallet med cron-formatet. For mer informasjon henvises til f.eks. Crontab Guru", @@ -114,7 +122,7 @@ "logging_settings": "Logger", "machine_learning_clip_model": "Clip-modell", "machine_learning_clip_model_description": "Navnet på en CLIP-modell finnes her. Merk at du må kjøre 'Smart Søk'-jobben på nytt for alle bilder etter at du har endret modell.", - "machine_learning_duplicate_detection": "Duplikat-deteksjon", + "machine_learning_duplicate_detection": "Duplikatsøk", "machine_learning_duplicate_detection_enabled": "Aktiver duplikatdeteksjon", "machine_learning_duplicate_detection_enabled_description": "Hvis deaktivert: helt identiske filer vil fremdeles de-duplisert.", "machine_learning_duplicate_detection_setting_description": "Bruk CLIP-embeddings for å finne sannsynlige duplikater", @@ -166,6 +174,20 @@ "metadata_settings_description": "Administrer metadatainnstillinger", "migration_job": "Migrering", "migration_job_description": "Migrer miniatyrbilder for filer og ansikter til den nyeste mappestrukturen", + "nightly_tasks_cluster_faces_setting_description": "Kjør ansiktsgjenkjenning på nylige oppdagede ansikter", + "nightly_tasks_cluster_new_faces_setting": "Grupper nye ansikter", + "nightly_tasks_database_cleanup_setting": "Opprydningsjobber for databasen", + "nightly_tasks_database_cleanup_setting_description": "Rydder opp i gamle, utgåtte data fra databasen", + "nightly_tasks_generate_memories_setting": "Genererer minner", + "nightly_tasks_generate_memories_setting_description": "Generer nye minner fra objekter", + "nightly_tasks_missing_thumbnails_setting": "Generer manglende miniatyrbilder", + "nightly_tasks_missing_thumbnails_setting_description": "Legg til objekter i kø som mangler miniatyrbilder for generering", + "nightly_tasks_settings": "Innstillinger for nattjobber", + "nightly_tasks_settings_description": "Endre på nattjobber", + "nightly_tasks_start_time_setting": "Starttid", + "nightly_tasks_start_time_setting_description": "Tiden som serveren starter med nattjobbene", + "nightly_tasks_sync_quota_usage_setting": "Synkroniser kvotebruk", + "nightly_tasks_sync_quota_usage_setting_description": "Oppdater brukerkvote basert på nåværende bruk", "no_paths_added": "Ingen filstier lagt til", "no_pattern_added": "Ingen mønster lagt til", "note_apply_storage_label_previous_assets": "Merk: For å bruke lagringsetiketten på tidligere opplastede filer, kjør", @@ -196,6 +218,8 @@ "oauth_mobile_redirect_uri": "Mobil omdirigerings-URI", "oauth_mobile_redirect_uri_override": "Mobil omdirigerings-URI overstyring", "oauth_mobile_redirect_uri_override_description": "Aktiver når OAuth-leverandøren ikke tillater en mobil URI, som ''{callback}''", + "oauth_role_claim": "Krev Rolle", + "oauth_role_claim_description": "Gi automatisk administratortilgang basert på tilstedeværelsen av dette kravet. Kravet kan ha enten «bruker» eller «administrator».", "oauth_settings": "OAuth", "oauth_settings_description": "Administrer innstillinger for OAuth-innlogging", "oauth_settings_more_details": "For mer informasjon om denne funksjonen, se dokumentasjonen.", @@ -212,7 +236,7 @@ "password_settings_description": "Administrer innstillinger for passordinnlogging", "paths_validated_successfully": "Alle filstier validert uten problemer", "person_cleanup_job": "Person opprydding", - "quota_size_gib": "Kvotestørrelse (GiB)", + "quota_size_gib": "Kvote (GiB)", "refreshing_all_libraries": "Oppdaterer alle biblioteker", "registration": "Administrator registrering", "registration_description": "Siden du er den første brukeren på systemet, vil du bli utnevnt til administrator og ha ansvar for administrative oppgaver. Du vil også opprette eventuelle nye brukere.", @@ -236,12 +260,12 @@ "smart_search_job_description": "Kjør maskinlæring på filer for å støtte smart søk", "storage_template_date_time_description": "Elementets opprettelsestidspunkt brukes for datotid-informasjonen", "storage_template_date_time_sample": "Eksempeltid {date}", - "storage_template_enable_description": "Aktiver lagringstemplatmotoren", + "storage_template_enable_description": "Aktiver lagringsmal-motoren", "storage_template_hash_verification_enabled": "Hash verifisering aktivert", "storage_template_hash_verification_enabled_description": "Aktiver hasjverifisering. Ikke deaktiver dette med mindre du er sikker på konsekvensene", - "storage_template_migration": "Lagringsmal migrering", + "storage_template_migration": "Implementer lagringsmal", "storage_template_migration_description": "Bruk gjeldende {template} på tidligere opplastede bilder", - "storage_template_migration_info": "Lagringsmalen vil endre filtypen til små bokstaver. Malendringer vil kun gjelde nye ressurser. For å anvende malen på tidligere opplastede ressurser, kjør {job}.", + "storage_template_migration_info": "Lagringsmalen vil endre filtypen til små bokstaver. Malendringer vil kun gjelde nye objekter. For å anvende malen på tidligere opplastede objekter, kjør {job}.", "storage_template_migration_job": "Migreringsjobb for lagringsmal", "storage_template_more_details": "For mer informasjon om denne funksjonen, se lagringsmalen og dens konsekvenser", "storage_template_onboarding_description_v2": "Når aktivert vil denne funksjonen automatisk organisere filer basert på en brukerdefinert mal. For mer informasjon, se denne linken dokumentasjon.", @@ -250,19 +274,19 @@ "storage_template_settings_description": "Administrer mappestrukturen og filnavnet til opplastede fil", "storage_template_user_label": "{label} er brukerens Lagringsetikett", "system_settings": "Systeminstillinger", - "tag_cleanup_job": "Tag opprydding", + "tag_cleanup_job": "Tagg-opprydding", "template_email_available_tags": "Du kan bruke følgende variabler i din mal: {tags}", "template_email_if_empty": "Hvis malen er tom, vil standard epost bli brut.", "template_email_invite_album": "Inviter Album Mal", "template_email_preview": "Forhåndsvis", - "template_email_settings": "Epost mal", + "template_email_settings": "E-postmaler", "template_email_update_album": "Oppdater Album Mal", - "template_email_welcome": "Mal for velkomst epost", + "template_email_welcome": "Mal for velkomst-e-post", "template_settings": "Varslings Mal", "template_settings_description": "Administrer tilpassede maler for varsling", "theme_custom_css_settings": "Egendefinert CSS", "theme_custom_css_settings_description": "Cascading Style Sheets gjør det mulig å tilpasse designet av Immich.", - "theme_settings": "Tema innstillinger", + "theme_settings": "Tema-innstillinger", "theme_settings_description": "Administrer tilpasning av Immich webgrensesnitt", "thumbnail_generation_job": "Generer miniatyrbilder", "thumbnail_generation_job_description": "Generer store, små og uskarpe miniatyrbilder for hver fil, samt miniatyrbilder for hver person", @@ -357,10 +381,12 @@ "admin_password": "Administrator Passord", "administration": "Administrasjon", "advanced": "Avansert", + "advanced_settings_beta_timeline_subtitle": "Prøv den nye app opplevelsen", + "advanced_settings_beta_timeline_title": "Beta tidslinje", "advanced_settings_enable_alternate_media_filter_subtitle": "Bruk denne innstillingen for å filtrere mediefiler under synkronisering basert på alternative kriterier. Bruk kun denne innstillingen dersom man opplever problemer med at applikasjonen ikke oppdager alle album.", "advanced_settings_enable_alternate_media_filter_title": "[EKSPERIMENTELT] Bruk alternativ enhet album synk filter", "advanced_settings_log_level_title": "Loggnivå: {level}", - "advanced_settings_prefer_remote_subtitle": "Noen enheter er veldige trege til å hente mikrobilder fra enheten. Aktiver denne innstillingen for å hente de eksternt istedenfor.", + "advanced_settings_prefer_remote_subtitle": "Noen enheter er veldige trege til å hente miniatyrbilder fra enheten. Aktiver denne innstillingen for å hente de eksternt istedenfor.", "advanced_settings_prefer_remote_title": "Foretrekk eksterne bilder", "advanced_settings_proxy_headers_subtitle": "Definer proxy headere som Immich skal benytte ved enhver nettverksrequest", "advanced_settings_proxy_headers_title": "Proxy headere", @@ -379,6 +405,7 @@ "album_cover_updated": "Albumomslag oppdatert", "album_delete_confirmation": "Er du sikker på at du vil slette albumet {album}?", "album_delete_confirmation_description": "Hvis dette albumet deles, vil andre brukere miste tilgangen til dette.", + "album_deleted": "Album slettet", "album_info_card_backup_album_excluded": "EKSKLUDERT", "album_info_card_backup_album_included": "INKLUDERT", "album_info_updated": "Albuminformasjon oppdatert", @@ -388,6 +415,7 @@ "album_options": "Albumalternativer", "album_remove_user": "Fjerne bruker?", "album_remove_user_confirmation": "Er du sikker på at du vil fjerne {user}?", + "album_search_not_found": "Ingen album ble funnet som traff ditt søk", "album_share_no_users": "Ser ut til at du har delt dette albumet med alle brukere, eller du ikke har noen brukere å dele det med.", "album_updated": "Album oppdatert", "album_updated_setting_description": "Motta e-postvarsling når et delt album får nye filer", @@ -407,6 +435,7 @@ "albums_default_sort_order": "Standard sorteringsrekkefølge for albumer", "albums_default_sort_order_description": "Standard sorteringsrekkefølge for bilder når man lager et nytt album.", "albums_feature_description": "Samlinger av bilder som kan deles med andre brukere.", + "albums_on_device_count": "Albumer på enheten {count}", "all": "Alle", "all_albums": "Alle album", "all_people": "Alle personer", @@ -426,7 +455,8 @@ "app_bar_signout_dialog_title": "Logg ut", "app_settings": "Appinstillinger", "appears_in": "Vises i", - "archive": "Arkiver", + "archive": "Arkiv", + "archive_action_prompt": "{count} lagt til i arkivet", "archive_or_unarchive_photo": "Arkiver eller ta ut av arkivet", "archive_page_no_archived_assets": "Ingen arkiverte objekter funnet", "archive_page_title": "Arkiv ({count})", @@ -462,26 +492,25 @@ "asset_viewer_settings_subtitle": "Endre dine visningsinnstillinger for galleriet", "asset_viewer_settings_title": "Objektviser", "assets": "Filer", - "assets_added_count": "Lagt til {count, plural, one {# element} other {# elementer}}", - "assets_added_to_album_count": "Lagt til {count, plural, one {# asset} other {# assets}} i album", - "assets_added_to_name_count": "Lagt til {count, plural, one {# asset} other {# assets}} i {hasName, select, true {{name}} other {new album}}", - "assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} kan ikke legges til i albumet", + "assets_added_count": "Lagt til {count, plural, one {# objekt} other {# objekter}}", + "assets_added_to_album_count": "Lagt til {count, plural, one {# objekter} other {# objekt}} i album", + "assets_cannot_be_added_to_album_count": "{count, plural, one {Objektet} other {Objektene}} kan ikke legges til i albumet", "assets_count": "{count, plural, one {# fil} other {# filer}}", "assets_deleted_permanently": "{count} objekt(er) slettet permanent", "assets_deleted_permanently_from_server": "{count} objekt(er) slettet permanent fra Immich-serveren", "assets_downloaded_failed": "{count, plural, one {Nedlasting av # fil - {error} fil feilet} other {Nedlastede # filer - {error} filer feilet}}", - "assets_downloaded_successfully": "{count, plural, one {Downloaded # file successfully} other {Downloaded # files successfully}}", - "assets_moved_to_trash_count": "Flyttet {count, plural, one {# asset} other {# assets}} til søppel", - "assets_permanently_deleted_count": "Permanent slettet {count, plural, one {# asset} other {# assets}}", - "assets_removed_count": "Slettet {count, plural, one {# asset} other {# assets}}", + "assets_downloaded_successfully": "{count, plural, one {Nedlastet # fil vellykket} other {Nedlastede # filer vellykket}}", + "assets_moved_to_trash_count": "Flyttet {count, plural, one {# objekt} other {# objekter}} til søppel", + "assets_permanently_deleted_count": "Slettet {count, plural, one {# objekt} other {# objekter}} permanent", + "assets_removed_count": "Slettet {count, plural, one {# objekt} other {# objekter}}", "assets_removed_permanently_from_device": "{count} objekt(er) slettet permanent fra enheten din", "assets_restore_confirmation": "Er du sikker på at du vil gjenopprette alle slettede eiendeler? Denne handlingen kan ikke angres! Vær oppmerksom på at frakoblede ressurser ikke kan gjenopprettes på denne måten.", - "assets_restored_count": "Gjenopprettet {count, plural, one {# asset} other {# assets}}", + "assets_restored_count": "Gjenopprettet {count, plural, one {# objekt} other {# objekter}}", "assets_restored_successfully": "{count} objekt(er) gjenopprettet", "assets_trashed": "{count} objekt(er) slettet", - "assets_trashed_count": "Kastet {count, plural, one {# asset} other {# assets}}", + "assets_trashed_count": "Kastet {count, plural, one {# objekt} other {# objekter}}", "assets_trashed_from_server": "{count} objekt(er) slettet fra Immich serveren", - "assets_were_part_of_album_count": "{count, plural, one {Asset was} other {Assets were}} er allerede lagt til i albumet", + "assets_were_part_of_album_count": "{count, plural, one {Objektet} other {Objektene}} er allerede lagt til i albumet", "authorized_devices": "Autoriserte enheter", "automatic_endpoint_switching_subtitle": "Koble til lokalt over angitt Wi-Fi når det er tilgjengelig, og bruk alternative tilkoblinger andre steder", "automatic_endpoint_switching_title": "Automatisk URL bytte", @@ -490,6 +519,7 @@ "back_close_deselect": "Tilbake, lukk eller fjern merking", "background_location_permission": "Bakgrunnstillatelse for plassering", "background_location_permission_content": "For å bytte nettverk når du kjører i bakgrunnen, må Immich *alltid* ha presis posisjonstilgang slik at appen kan lese Wi-Fi-nettverkets navn", + "backup": "Sikkerhetskopiering", "backup_album_selection_page_albums_device": "Album på enhet ({count})", "backup_album_selection_page_albums_tap": "Trykk for å inkludere, dobbelttrykk for å ekskludere", "backup_album_selection_page_assets_scatter": "Objekter kan bli spredd over flere album. Album kan derfor bli inkludert eller ekskludert under sikkerhetskopieringen.", @@ -521,7 +551,7 @@ "backup_controller_page_background_turn_off": "Skru av bakgrunnstjenesten", "backup_controller_page_background_turn_on": "Skru på bakgrunnstjenesten", "backup_controller_page_background_wifi": "Kun på Wi-Fi", - "backup_controller_page_backup": "Sikkerhetskopier", + "backup_controller_page_backup": "Sikkerhetskopiere", "backup_controller_page_backup_selected": "Valgte: ", "backup_controller_page_backup_sub": "Opplastede bilder og videoer", "backup_controller_page_created": "Opprettet: {date}", @@ -553,6 +583,8 @@ "backup_options_page_title": "Backupinnstillinger", "backup_setting_subtitle": "Administrer opplastingsinnstillinger for bakgrunn og forgrunn", "backward": "Bakover", + "beta_sync": "Beta synkroniseringsstatus", + "beta_sync_subtitle": "Håndter det nye synkroniseringssystemet", "biometric_auth_enabled": "Biometrisk autentisering aktivert", "biometric_locked_out": "Du er låst ute av biometrisk verifisering", "biometric_no_options": "Ingen biometriske valg tilgjengelige", @@ -563,14 +595,14 @@ "bugs_and_feature_requests": "Feil og funksjonsforespørsler", "build": "Bygg", "build_image": "Lag Bilde", - "bulk_delete_duplicates_confirmation": "Er du sikker på at du vil slette {count, plural, one {# duplicate asset} other {# duplicate assets}} dupliserte filer? Dette vil beholde største filen fra hver gruppe og vil permanent slette alle andre duplikater. Du kan ikke angre denne handlingen!", - "bulk_keep_duplicates_confirmation": "Er du sikker på at du vil beholde {count, plural, one {# duplicate asset} other {# duplicate assets}} dupliserte filer? Dette vil løse alle dupliserte grupper uten å slette noe.", - "bulk_trash_duplicates_confirmation": "Er du sikker på ønsker å slette {count, plural, one {# duplicate asset} other {# duplicate assets}} dupliserte filer? Dette vil beholde største filen fra hver gruppe, samt slette alle andre duplikater.", + "bulk_delete_duplicates_confirmation": "Er du sikker på at du vil slette {count, plural, one {# duplisert fil} other {# dupliserte filer}}? Dette vil beholde største filen fra hver gruppe og vil permanent slette alle andre duplikater. Du kan ikke angre denne handlingen!", + "bulk_keep_duplicates_confirmation": "Er du sikker på at du vil beholde {count, plural, one {# duplikat} other {# duplikater}}? Dette vil løse alle duplikatgrupper uten å slette noe.", + "bulk_trash_duplicates_confirmation": "Er du sikker på ønsker å slette {count, plural, one {# duplisert objekt} other {# dupliserte objekter}}? Dette vil beholde største filen fra hver gruppe, samt slette alle andre duplikater.", "buy": "Kjøp Immich", "cache_settings_clear_cache_button": "Tøm buffer", "cache_settings_clear_cache_button_title": "Tømmer app-ens buffer. Dette vil ha betydelig innvirkning på appens ytelse inntil bufferen er gjenoppbygd.", "cache_settings_duplicated_assets_clear_button": "TØM", - "cache_settings_duplicated_assets_subtitle": "Bilder og videoer som er svartelistet av app'en", + "cache_settings_duplicated_assets_subtitle": "Bilder og videoer som er ignorert av app'en", "cache_settings_duplicated_assets_title": "Dupliserte objekter ({count})", "cache_settings_statistics_album": "Bibliotekminiatyrbilder", "cache_settings_statistics_full": "Originalbilder", @@ -587,6 +619,7 @@ "cancel": "Avbryt", "cancel_search": "Avbryt søk", "canceled": "Avbrutt", + "canceling": "Avbryter", "cannot_merge_people": "Kan ikke slå sammen personer", "cannot_undo_this_action": "Du kan ikke gjøre om denne handlingen!", "cannot_update_the_description": "Kan ikke oppdatere beskrivelsen", @@ -700,10 +733,11 @@ "current_server_address": "Nåværende serveradresse", "custom_locale": "Tilpasset lokalisering", "custom_locale_description": "Formater datoer og tall basert på språk og region", + "custom_url": "Tilpasset URL", "daily_title_text_date": "E MMM. dd", "daily_title_text_date_year": "E MMM. dddd, yyyy", "dark": "Mørk", - "darkTheme": "Aktiver mørkt utsende", + "dark_theme": "Aktiver mørk-modus", "date_after": "Dato etter", "date_and_time": "Dato og tid", "date_before": "Dato før", @@ -719,6 +753,8 @@ "default_locale": "Standard språkinnstilling", "default_locale_description": "Formater datoer og tall basert på nettleserens språkinnstilling", "delete": "Slett", + "delete_action_confirmation_message": "Er du sikker på at du vil slette dette objektet? Dette vil flytte objektet til søppelkassen og vil gi deg beskjed om du vil slette det lokalt", + "delete_action_prompt": "{count} slettet", "delete_album": "Slett album", "delete_api_key_prompt": "Er du sikker på at du vil slette denne API-nøkkelen?", "delete_dialog_alert": "Disse objektene vil bli slettet permanent fra Immich og fra enheten din", @@ -732,9 +768,12 @@ "delete_key": "Slett nøkkel", "delete_library": "Slett bibliotek", "delete_link": "Slett lenke", + "delete_local_action_prompt": "{count} slettet lokalt", "delete_local_dialog_ok_backed_up_only": "Slett kun sikkerhetskopierte objekter", "delete_local_dialog_ok_force": "Slett uansett", "delete_others": "Slett andre", + "delete_permanently": "Slett permanent", + "delete_permanently_action_prompt": "{count} slettet permanent", "delete_shared_link": "Slett delt lenke", "delete_shared_link_dialog_title": "Slett delt link", "delete_tag": "Slett tag", @@ -745,6 +784,7 @@ "description": "Beskrivelse", "description_input_hint_text": "Legg til beskrivelse ...", "description_input_submit_error": "Feil ved oppdatering av beskrivelse, sjekk loggen for flere detaljer", + "deselect_all": "Avmerk alle", "details": "Detaljer", "direction": "Retning", "disabled": "Deaktivert", @@ -762,6 +802,7 @@ "documentation": "Dokumentasjon", "done": "Ferdig", "download": "Last ned", + "download_action_prompt": "Laster ned {count} objekter", "download_canceled": "Nedlasting avbrutt", "download_complete": "Nedlasting fullført", "download_enqueue": "Nedlasting satt i kø", @@ -788,6 +829,7 @@ "edit": "Rediger", "edit_album": "Rediger album", "edit_avatar": "Rediger avatar", + "edit_birthday": "Rediger Bursdag", "edit_date": "Rediger dato", "edit_date_and_time": "Rediger dato og tid", "edit_description": "Endre beskrivelse", @@ -799,6 +841,7 @@ "edit_key": "Rediger nøkkel", "edit_link": "Endre lenke", "edit_location": "Endre lokasjon", + "edit_location_action_prompt": "{count} lokasjon endret", "edit_location_dialog_title": "Lokasjon", "edit_name": "Redigere navn", "edit_people": "Rediger personer", @@ -817,6 +860,7 @@ "empty_trash": "Tøm papirkurv", "empty_trash_confirmation": "Er du sikker på at du vil tømme søppelbøtta? Dette vil slette alle filene i søppelbøtta permanent fra Immich.\nDu kan ikke angre denne handlingen!", "enable": "Aktivere", + "enable_backup": "Aktiver backup", "enable_biometric_auth_description": "Skriv inn PINkoden for å aktivere biometrisk autentisering", "enabled": "Aktivert", "end_date": "Slutt dato", @@ -837,7 +881,7 @@ "cant_apply_changes": "Kan ikke legge til endringene", "cant_change_activity": "Kan ikke {enabled, select, true {disable} other {enable}} aktivitet", "cant_change_asset_favorite": "Kan ikke endre favoritt til filen", - "cant_change_metadata_assets_count": "Kan ikke endre metadata for {count, plural, one {# asset} other {# assets}}", + "cant_change_metadata_assets_count": "Kan ikke endre metadata for {count, plural, one {# objekt} other {# objekter}}", "cant_get_faces": "Kan ikke finne ansikter", "cant_get_number_of_comments": "Kan ikke hente antall kommentarer", "cant_search_people": "Kan ikke søke etter mennesker", @@ -867,15 +911,15 @@ "incorrect_email_or_password": "Feil epost eller passord", "paths_validation_failed": "{paths, plural, one {# sti} other {# sti}} mislyktes validering", "profile_picture_transparent_pixels": "Profil bilde kan ikke ha gjennomsiktige piksler. Vennligst zoom inn og/eller flytt bilde.", - "quota_higher_than_disk_size": "Du har satt en kvote høyere enn diskstørrelsen", + "quota_higher_than_disk_size": "Du har satt kvoten større enn diskstørrelsen", "unable_to_add_album_users": "Kan ikke legge til brukere i albumet", "unable_to_add_assets_to_shared_link": "Kan ikke legge til bilder til delt lenke", "unable_to_add_comment": "Kan ikke legge til kommentar", "unable_to_add_exclusion_pattern": "Kan ikke legge til eksklusjonsmønster", "unable_to_add_import_path": "Kan ikke legge til importsti", "unable_to_add_partners": "Kan ikke legge til partnere", - "unable_to_add_remove_archive": "Kan ikke {archived, select, true {remove asset from} other {add asset to}} arkivet", - "unable_to_add_remove_favorites": "Kan ikke {favorite, select, true {add asset to} other {remove asset from}} favoritter", + "unable_to_add_remove_archive": "Kan ikke {archived, select, true {fjerne objekt fra} other {flytte objekt til}} arkivet", + "unable_to_add_remove_favorites": "Kan ikke {favorite, select, true {legge til objekt til} other {fjerne objekt fra}} favoritter", "unable_to_archive_unarchive": "Kan ikke {archived, select, true {archive} other {unarchive}}", "unable_to_change_album_user_role": "Kan ikke endre brukerens rolle i albumet", "unable_to_change_date": "Kan ikke endre dato", @@ -953,6 +997,7 @@ }, "exif": "EXIF", "exif_bottom_sheet_description": "Legg til beskrivelse ...", + "exif_bottom_sheet_description_error": "Feil ved oppdatering av beskrivelsen", "exif_bottom_sheet_details": "DETALJER", "exif_bottom_sheet_location": "PLASSERING", "exif_bottom_sheet_people": "MENNESKER", @@ -973,6 +1018,8 @@ "explorer": "Utforsker", "export": "Eksporter", "export_as_json": "Eksporter som JSON", + "export_database": "Eksporter database", + "export_database_description": "Eksporter SQLite databasen", "extension": "Utvidelse", "external": "Ekstern", "external_libraries": "Eksterne Bibliotek", @@ -984,6 +1031,7 @@ "failed_to_load_assets": "Feilet med å laste fil", "failed_to_load_folder": "Kunne ikke laste inn mappe", "favorite": "Favoritt", + "favorite_action_prompt": "{count} lagt til i favoritter", "favorite_or_unfavorite_photo": "Merk som favoritt eller fjern som favoritt", "favorites": "Favoritter", "favorites_page_no_favorites": "Ingen favorittobjekter funnet", @@ -1022,7 +1070,10 @@ "group_year": "Grupper etter år", "haptic_feedback_switch": "Aktivert haptisk tilbakemelding", "haptic_feedback_title": "Haptisk tilbakemelding", - "has_quota": "Har kvote", + "has_quota": "Kvote", + "hash_asset": "Hash objekter", + "hashed_assets": "Hashede objekter", + "hashing": "Hasher", "header_settings_add_header_tip": "Legg til header", "header_settings_field_validator_msg": "Verdi kan ikke være null", "header_settings_header_name_input": "Header navn", @@ -1055,6 +1106,7 @@ "host": "Vert", "hour": "Time", "id": "ID", + "idle": "Uvirksom", "ignore_icloud_photos": "Ignorer iCloud bilder", "ignore_icloud_photos_description": "Bilder som er lagret på iCloud vil ikke lastes opp til Immich", "image": "Bilde", @@ -1105,13 +1157,14 @@ "keep": "Behold", "keep_all": "Behold alle", "keep_this_delete_others": "Behold denne, slett de andre", - "kept_this_deleted_others": "Behold denne filen og slett {count, plural, one {# asset} other {# assets}}", + "kept_this_deleted_others": "Behold denne filen og slett {count, plural, one {# objekt} other {# objekter}}", "keyboard_shortcuts": "Tastatursnarveier", "language": "Språk", "language_no_results_subtitle": "Prøv å endre søkeord", "language_no_results_title": "Ingen språk funnet", "language_search_hint": "Søker etter språk...", - "language_setting_description": "Velg ditt foretrukket språk", + "language_setting_description": "Velg ditt foretrukne språk", + "large_files": "Store Filer", "last_seen": "Sist sett", "latest_version": "Siste versjon", "latitude": "Breddegrad", @@ -1127,16 +1180,18 @@ "library_page_sort_created": "Nylig opplastet", "library_page_sort_last_modified": "Sist endret", "library_page_sort_title": "Albumtittel", + "licenses": "Lisenser", "light": "Lys", "like_deleted": "Som slettede", "link_motion_video": "Koble bevegelsesvideo", - "link_options": "Lenkealternativer", "link_to_oauth": "Lenke til OAuth", "linked_oauth_account": "Lenket til OAuth-konto", "list": "Liste", "loading": "Laster", "loading_search_results_failed": "Klarte ikke å laste inn søkeresultater", + "local": "Lokal", "local_asset_cast_failed": "Kan ikke caste et bilde som ikke er lastet opp til serveren", + "local_assets": "Lokale objekter", "local_network": "Lokalt nettverk", "local_network_sheet_info": "Appen vil koble til serveren via denne URL-en når du bruker det angitte Wi-Fi-nettverket", "location_permission": "Stedstillatelse", @@ -1173,7 +1228,7 @@ "login_form_save_login": "Forbli innlogget", "login_form_server_empty": "Skriv inn en server-URL.", "login_form_server_error": "Kan ikke koble til server.", - "login_has_been_disabled": "Login har blitt deaktivert.", + "login_has_been_disabled": "Innlogging har blitt deaktivert.", "login_password_changed_error": "Det skjedde en feil ved oppdatering av passordet", "login_password_changed_success": "Passord oppdatert", "logout_all_device_confirmation": "Er du sikker på at du vil logge ut av alle enheter?", @@ -1193,8 +1248,7 @@ "manage_your_devices": "Administrer dine innloggede enheter", "manage_your_oauth_connection": "Administrer tilkoblingen din med OAuth", "map": "Kart", - "map_assets_in_bound": "{count} bilde", - "map_assets_in_bounds": "{count} bilder", + "map_assets_in_bounds": "{count, plural, one {# photo} other {# photos}}", "map_cannot_get_user_location": "Kan ikke hente brukerlokasjon", "map_location_dialog_yes": "Ja", "map_location_picker_page_use_location": "Bruk denne lokasjonen", @@ -1209,7 +1263,7 @@ "map_settings_dark_mode": "Mørk modus", "map_settings_date_range_option_day": "Siste 24 timer", "map_settings_date_range_option_days": "Siste {days} dager", - "map_settings_date_range_option_year": "Sist år", + "map_settings_date_range_option_year": "Siste år", "map_settings_date_range_option_years": "Siste {years} år", "map_settings_dialog_title": "Kartinnstillinger", "map_settings_include_show_archived": "Inkluder arkiverte", @@ -1227,7 +1281,7 @@ "memories_check_back_tomorrow": "Sjekk igjen i morgen for flere minner", "memories_setting_description": "Administrer hva du ser i minnene dine", "memories_start_over": "Start på nytt", - "memories_swipe_to_close": "Swipe opp for å lukke", + "memories_swipe_to_close": "Sveip opp for å lukke", "memory": "Minne", "memory_lane_title": "Minnefelt {title}", "menu": "Meny", @@ -1235,7 +1289,7 @@ "merge_people": "Slå sammen personer", "merge_people_limit": "Du kan bare slå sammen opp til 5 fjes om gangen", "merge_people_prompt": "Vil du slå sammen disse personene? Denne handlingen kan ikke reverseres.", - "merge_people_successfully": "Personene ble vellykket slått sammen", + "merge_people_successfully": "Sammenslåing av personer var vellykket", "merged_people_count": "Sammenslått {count, plural, one {# person} other {# people}}", "minimize": "Minimer", "minute": "Minutt", @@ -1246,10 +1300,11 @@ "more": "Mer", "move": "Flytt", "move_off_locked_folder": "Flytt ut av låst mappe", + "move_to_lock_folder_action_prompt": "{count} lagt til i låst mappe", "move_to_locked_folder": "Flytt til låst mappe", "move_to_locked_folder_confirmation": "Disse bildene og videoene vil bli fjernet fra alle albumer, og kun tilgjengelige via den låste mappen", - "moved_to_archive": "Flyttet {count, plural, one {# asset} other {# assets}} til arkivet", - "moved_to_library": "Flyttet {count, plural, one {# asset} other {# assets}} til biblioteket", + "moved_to_archive": "Flyttet {count, plural, one {# objekt} other {# objekter}} til arkivet", + "moved_to_library": "Flyttet {count, plural, one {# objekt} other {# objekter}} til biblioteket", "moved_to_trash": "Flyttet til papirkurven", "multiselect_grid_edit_date_time_err_read_only": "Kan ikke endre dato på objekt(er) med kun lese-rettigheter, hopper over", "multiselect_grid_edit_gps_err_read_only": "Kan ikke endre lokasjon på objekt(er) med kun lese-rettigheter, hopper over", @@ -1258,14 +1313,14 @@ "name": "Navn", "name_or_nickname": "Navn eller kallenavn", "networking_settings": "Nettverk", - "networking_subtitle": "Administrer serverendepunktinnstillingene", + "networking_subtitle": "Administrer serverendepunkt-innstillinger", "never": "aldri", "new_album": "Nytt Album", "new_api_key": "Ny API-nøkkel", "new_password": "Nytt passord", "new_person": "Ny person", - "new_pin_code": "Ny PIN kode", - "new_pin_code_subtitle": "Dette er første gang du åpner den låste mappen. Lag en PIN kode for å sikre tilgangen til denne siden", + "new_pin_code": "Ny PIN-kode", + "new_pin_code_subtitle": "Dette er første gang du åpner den låste mappen. Lag en PIN-kode for å sikre tilgangen til denne siden", "new_user_created": "Ny bruker opprettet", "new_version_available": "NY VERSJON TILGJENGELIG", "newest_first": "Nyeste først", @@ -1282,7 +1337,7 @@ "no_duplicates_found": "Ingen duplikater ble funnet.", "no_exif_info_available": "Ingen EXIF-informasjon tilgjengelig", "no_explore_results_message": "Last opp flere bilder for å utforske samlingen din.", - "no_favorites_message": "Legg til favoritter for å raskt finne dine beste bilder og videoer", + "no_favorites_message": "Legg til favoritter for å finne dine beste bilder og videoer raskt", "no_libraries_message": "Opprett et eksternt bibliotek for å se bildene og videoene dine", "no_locked_photos_message": "Bilder og videoer i den låste mappen er skjult og vil ikke vises når du blar i biblioteket.", "no_name": "Ingen navn", @@ -1292,7 +1347,8 @@ "no_results": "Ingen resultater", "no_results_description": "Prøv et synonym eller mer generelt søkeord", "no_shared_albums_message": "Opprett et album for å dele bilder og videoer med personer i nettverket ditt", - "not_in_any_album": "Ikke i noen album", + "no_uploads_in_progress": "Ingen opplasting pågår", + "not_in_any_album": "Ikke i noe album", "not_selected": "Ikke valgt", "note_apply_storage_label_to_previously_uploaded assets": "Merk: For å bruke lagringsetiketten på tidligere opplastede filer, kjør", "notes": "Notater", @@ -1305,7 +1361,7 @@ "notifications": "Notifikasjoner", "notifications_setting_description": "Administrer varsler", "oauth": "OAuth", - "official_immich_resources": "Offisielle Immich Resurser", + "official_immich_resources": "Offisielle Immich-ressurser", "offline": "Frakoblet", "ok": "Ok", "oldest_first": "Eldste først", @@ -1315,7 +1371,7 @@ "onboarding_privacy_description": "Følgene (valgfrie) funksjoner er avhengige av eksterne tjenester, og kan bli deaktivert når som helst under innstillinger.", "onboarding_server_welcome_description": "La oss sette opp din instans med noen standard innstillinger.", "onboarding_theme_description": "Velg et fargetema for din bruker. Du kan endre denne senere under dine instillinger.", - "onboarding_user_welcome_description": "La oss få deg startet!", + "onboarding_user_welcome_description": "La oss få deg i gang!", "onboarding_welcome_user": "Velkommen, {user}", "online": "Tilkoblet", "only_favorites": "Bare favoritter", @@ -1329,6 +1385,7 @@ "original": "original", "other": "Annet", "other_devices": "Andre enheter", + "other_entities": "Andre objekter", "other_variables": "Andre variabler", "owned": "Dine", "owner": "Eier", @@ -1368,10 +1425,10 @@ "permanent_deletion_warning": "Advarsel om permanent sletting", "permanent_deletion_warning_setting_description": "Vis en advarsel ved permanent sletting av filer", "permanently_delete": "Slett permanent", - "permanently_delete_assets_count": "Permanent slett {count, plural, one {asset} other {assets}}", - "permanently_delete_assets_prompt": "Er du sikker på at du vil permanent slette {count, plural, one {this asset?} other {these # assets?}} Dette vil også slette {count, plural, one {it from its} other {them from their}} album.", + "permanently_delete_assets_count": "Slett {count, plural, one {objekt} other {objekter}} permanent", + "permanently_delete_assets_prompt": "Er du sikker på at du vil permanent slette {count, plural, one {dette objektet?} other {disse # objektene?}} Dette vil også slette {count, plural, one {det fra dets} other {de fra deres}} album(er).", "permanently_deleted_asset": "Filen har blitt permanent slettet", - "permanently_deleted_assets_count": "Permanent slett {count, plural, one {# asset} other {# assets}}", + "permanently_deleted_assets_count": "Permanent slett {count, plural, one {# objekt} other {# objekter}}", "permission": "Tillatelse", "permission_empty": "Dine tillatelser burde ikke være tomme", "permission_onboarding_back": "Tilbake", @@ -1460,6 +1517,7 @@ "purchase_server_description_2": "Støttespiller status", "purchase_server_title": "Server", "purchase_settings_server_activated": "Produktnøkkel for server er administrert av administratoren", + "queue_status": "Kø {count}/{total}", "rating": "Stjernevurdering", "rating_clear": "Slett vurdering", "rating_count": "{count, plural, one {# sjerne} other {# stjerner}}", @@ -1467,14 +1525,14 @@ "reaction_options": "Reaksjonsalternativer", "read_changelog": "Les endringslogg", "reassign": "Tilordne på nytt", - "reassigned_assets_to_existing_person": "Tildelt på nytt {count, plural, one {# asset} other {# assets}} to {name, select, null {an existing person} other {{name}}}", - "reassigned_assets_to_new_person": "Tildelt på nytt {count, plural, one {# asset} other {# assets}} til en ny person", + "reassigned_assets_to_existing_person": "Flyttet {count, plural, one {# objekt} other {# objekter}} to {name, select, null {en eksisterende person} other {{name}}}", + "reassigned_assets_to_new_person": "Flyttet {count, plural, one {# objekt} other {# objekter}} til en ny person", "reassing_hint": "Tilordne valgte eiendeler til en eksisterende person", "recent": "Nylig", "recent-albums": "Nylige album", "recent_searches": "Nylige søk", "recently_added": "Nylig lagt til", - "recently_added_page_title": "Nylig lagt til", + "recently_added_page_title": "Nylig oppført", "recently_taken": "Nylig tatt", "recently_taken_page_title": "Nylig Tatt", "refresh": "Oppdater", @@ -1488,14 +1546,18 @@ "refreshing_faces": "Oppdaterer ansikter", "refreshing_metadata": "Oppdaterer matadata", "regenerating_thumbnails": "Regenererer miniatyrbilder", + "remote": "Eksternt", + "remote_assets": "Eksterne objekter", "remove": "Fjern", - "remove_assets_album_confirmation": "Er du sikker på at du fil slette {count, plural, one {# asset} other {# assets}} fra albumet?", - "remove_assets_shared_link_confirmation": "Er du sikker på at du vil slette {count, plural, one {# asset} other {# assets}} fra den delte lenken?", + "remove_assets_album_confirmation": "Er du sikker på at du fil slette {count, plural, one {# objekt} other {# objekter}} fra albumet?", + "remove_assets_shared_link_confirmation": "Er du sikker på at du vil slette {count, plural, one {# objekt} other {# objekter}} fra den delte lenken?", "remove_assets_title": "Vil du fjerne eiendeler?", "remove_custom_date_range": "Fjern egendefinert datoperiode", "remove_deleted_assets": "Fjern fra frakoblede filer", "remove_from_album": "Fjern fra album", + "remove_from_album_action_prompt": "{count} fjernet fra albumet", "remove_from_favorites": "Fjern fra favoritter", + "remove_from_lock_folder_action_prompt": "{count} fjernet fra låst mappe", "remove_from_locked_folder": "Fjern fra låst mappe", "remove_from_locked_folder_confirmation": "Er du sikker på at du vil flytte disse bildene og videoene ut av den låste mappen? De vil bli synlige i biblioteket.", "remove_from_shared_link": "Fjern fra delt lenke", @@ -1510,7 +1572,7 @@ "removed_from_favorites_count": "{count, plural, other {Removed #}} fra favoritter", "removed_memory": "Slettet minne", "removed_photo_from_memory": "Slettet bilde fra minne", - "removed_tagged_assets": "Fjern tag fra {count, plural, one {# asset} other {# assets}}", + "removed_tagged_assets": "Fjern tag fra {count, plural, one {# objekt} other {# objekter}}", "rename": "Gi nytt navn", "repair": "Reparer", "repair_no_results_message": "Usporrede og savnede filer vil vises her", @@ -1523,19 +1585,25 @@ "reset_password": "Tilbakestill passord", "reset_people_visibility": "Tilbakestill personsynlighet", "reset_pin_code": "Resett PINkode", + "reset_sqlite": "Reset SQLite Databasen", + "reset_sqlite_confirmation": "Er du sikker på at du vil resette SQLite databasen? Du blir nødt til å logge ut og inn igjen for å resynkronisere data", + "reset_sqlite_success": "Vellykket resetting av SQLite databasen", "reset_to_default": "Tilbakestill til standard", "resolve_duplicates": "Løs duplikater", "resolved_all_duplicates": "Løste alle duplikater", "restore": "Gjenopprett", "restore_all": "Gjenopprett alle", + "restore_trash_action_prompt": "{count} gjenopprettet fra søppelbøtten", "restore_user": "Gjenopprett bruker", "restored_asset": "Gjenopprettet ressurs", "resume": "Fortsett", "retry_upload": "Prøv opplasting på nytt", "review_duplicates": "Gjennomgå duplikater", + "review_large_files": "Se gjennom store filer", "role": "Rolle", "role_editor": "Redigerer", "role_viewer": "Visning", + "running": "Kjører", "save": "Lagre", "save_to_gallery": "Lagre til galleriet", "saved_api_key": "Lagret API-nøkkel", @@ -1630,7 +1698,7 @@ "server_offline": "Server frakoblet", "server_online": "Server tilkoblet", "server_privacy": "Server personvern", - "server_stats": "Server Statistikk", + "server_stats": "Serverstatistikk", "server_version": "Server Versjon", "set": "Sett", "set_as_album_cover": "Sett som albumomslag", @@ -1667,6 +1735,7 @@ "settings_saved": "Innstillinger lagret", "setup_pin_code": "Sett opp en PINkode", "share": "Del", + "share_action_prompt": "Delte {count} objekter", "share_add_photos": "Legg til bilder", "share_assets_selected": "{count} valgt", "share_dialog_preparing": "Forbereder ...", @@ -1688,6 +1757,7 @@ "shared_link_clipboard_copied_massage": "Kopiert til utklippslisten", "shared_link_clipboard_text": "Link: {link}\nPassord: {password}", "shared_link_create_error": "Feil ved oppretting av delbar link", + "shared_link_custom_url_description": "Få tilgang til denne delte lenken med en egendefinert URL", "shared_link_edit_description_hint": "Endre delebeskrivelse", "shared_link_edit_expire_after_option_day": "1 dag", "shared_link_edit_expire_after_option_days": "{count} dager", @@ -1713,6 +1783,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Håndter delte linker", "shared_link_options": "Alternativer for delte lenke", + "shared_link_password_description": "Krev et passord for å få tilgang til denne delte lenken", "shared_links": "Delte linker", "shared_links_description": "Del bilder og videoer med lenke", "shared_photos_and_videos_count": "{assetCount, plural, other {# delte bilder og videoer.}}", @@ -1768,10 +1839,11 @@ "sort_title": "Tittel", "source": "Kilde", "stack": "Stable", + "stack_action_prompt": "{count} stakket", "stack_duplicates": "Stable duplikater", "stack_select_one_photo": "Velg hovedbilde for bildestabbel", "stack_selected_photos": "Stable valgte bilder", - "stacked_assets_count": "Stable {count, plural, one {# asset} other {# assets}}", + "stacked_assets_count": "Stable {count, plural, one {# objekt} other {# objekter}}", "stacktrace": "Stakkspor", "start": "Start", "start_date": "Startdato", @@ -1787,6 +1859,7 @@ "storage_quota": "Lagringsplass", "storage_usage": "{used} av {available} brukt", "submit": "Send inn", + "success": "Vellykket", "suggestions": "Forslag", "sunrise_on_the_beach": "Soloppgang på stranden", "support": "Støtte", @@ -1796,6 +1869,8 @@ "sync": "Synkroniser", "sync_albums": "Synkroniser albumer", "sync_albums_manual_subtitle": "Synkroniser alle opplastede videoer og bilder til det valgte backupalbumet", + "sync_local": "Synkroniser lokalt", + "sync_remote": "Synkroniser eksternt", "sync_upload_album_setting_subtitle": "Opprett og last opp dine bilder og videoer til det valgte albumet på Immich", "tag": "Tagg", "tag_assets": "Merk ressurser", @@ -1804,8 +1879,9 @@ "tag_not_found_question": "Finner du ikke en merke? Opprett en nytt merke.", "tag_people": "Tag Folk", "tag_updated": "Oppdater merke: {tag}", - "tagged_assets": "Merket {count, plural, one {# asset} other {# assets}}", + "tagged_assets": "Merket {count, plural, one {# objekt} other {# objekter}}", "tags": "Merker", + "tap_to_run_job": "Trykk for å kjøre jobben", "template": "Mal", "theme": "Tema", "theme_selection": "Temavalg", @@ -1838,9 +1914,10 @@ "total": "Total", "total_usage": "Totalt brukt", "trash": "Papirkurv", + "trash_action_prompt": "{count} flyttet til søppel", "trash_all": "Slett alt", "trash_count": "Slett {count, number}", - "trash_delete_asset": "Slett ressurs", + "trash_delete_asset": "Slett objekt", "trash_emptied": "Søppelbøtte tømt", "trash_no_results_message": "Her vises bilder og videoer som er flyttet til papirkurven.", "trash_page_delete_all": "Slett alt", @@ -1852,16 +1929,18 @@ "trash_page_title": "Søppelbøtte ({count})", "trashed_items_will_be_permanently_deleted_after": "Elementer i papirkurven vil bli permanent slettet etter {days, plural, one {# dag} other {# dager}}.", "type": "Type", - "unable_to_change_pin_code": "Klarte ikke å endre PINkode", + "unable_to_change_pin_code": "Klarte ikke å endre PIN-kode", "unable_to_setup_pin_code": "Klarte ikke å sette opp PINkode", "unarchive": "Fjern fra arkiv", + "unarchive_action_prompt": "{count} slettet fra Arkiv", "unarchived_count": "{count, plural, other {uarkivert #}}", "undo": "Angre", "unfavorite": "Fjern favoritt", + "unfavorite_action_prompt": "{count} slettet fra Favoritter", "unhide_person": "Vis person", "unknown": "Ukjent", "unknown_country": "Ukjent Land", - "unknown_year": "Ukjent År", + "unknown_year": "Ukjent år", "unlimited": "Ubegrenset", "unlink_motion_video": "Koble fra bevegelsesvideo", "unlink_oauth": "Fjern kobling til OAuth", @@ -1873,25 +1952,31 @@ "unsaved_change": "Ulagrede endringer", "unselect_all": "Fjern alle valg", "unselect_all_duplicates": "Fjern markeringen av alle duplikater", - "unselect_all_in": "Fjern alle i {group}", + "unselect_all_in": "Fjern velging av alle i {group}", "unstack": "avstable", - "unstacked_assets_count": "Ikke stablet {count, plural, one {# asset} other {# assets}}", + "unstack_action_prompt": "{count} ustakket", + "unstacked_assets_count": "Ikke stablet {count, plural, one {# objekt} other {# objekter}}", + "untagged": "Umerket", "up_next": "Neste", "updated_at": "Oppdatert", "updated_password": "Passord oppdatert", "upload": "Last opp", + "upload_action_prompt": "{count} i kø for opplasting", "upload_concurrency": "Samtidig opplastning", + "upload_details": "Opplastingsdetaljer", "upload_dialog_info": "Vil du utføre backup av valgte objekt(er) til serveren?", "upload_dialog_title": "Last opp objekt", "upload_errors": "Opplasting fullført med {count, plural, one {# error} other {# errors}}, oppdater siden for å se nye opplastingsressurser.", + "upload_finished": "Opplasting fullført", "upload_progress": "Gjenstående {remaining, number} – behandlet {processed, number}/{total, number}", - "upload_skipped_duplicates": "Hoppet over {count, plural, one {# duplicate asset} other {# duplicate assets}}", + "upload_skipped_duplicates": "Hoppet over {count, plural, one {# duplisert objekt} other {# dupliserte objekter}}", "upload_status_duplicates": "Duplikater", "upload_status_errors": "Feil", "upload_status_uploaded": "Opplastet", "upload_success": "Opplasting vellykket, oppdater siden for å se nye opplastninger.", "upload_to_immich": "Last opp til Immich ({count})", "uploading": "Laster opp", + "uploading_media": "Laster opp media", "url": "URL", "usage": "Bruk", "use_biometric": "Bruk biometri", @@ -1900,7 +1985,7 @@ "user": "Bruker", "user_has_been_deleted": "Denne brukeren har blitt slettet.", "user_id": "Bruker ID", - "user_liked": "{user} likte {type, select, photo {this photo} video {this video} asset {this asset} other {it}}", + "user_liked": "{user} likte {type, select, photo {dette bildet} video {denne videoen} asset {dette objektet} other {dette}}", "user_pin_code_settings": "PINkode", "user_pin_code_settings_description": "Håndter din PINkode", "user_privacy": "Personverninnstillinger", @@ -1912,6 +1997,7 @@ "user_usage_stats_description": "Vis kontobruksstatistikk", "username": "Brukernavn", "users": "Brukere", + "users_added_to_album_count": "Lagt til {count, plural, one {# bruker} other {# brukere}} til albumet", "utilities": "Verktøy", "validate": "Valider", "validate_endpoint_error": "Skriv inn en gyldig URL", @@ -1919,7 +2005,7 @@ "version": "Versjon", "version_announcement_closing": "Din venn, Alex", "version_announcement_message": "Hei! En ny versjon av Immich er tilgjengelig. Vennligst ta deg tid til å lese utgivelsesnotatene for å sikre at oppsettet ditt er oppdatert for å forhindre feilkonfigurasjoner, spesielt hvis du bruker WatchTower eller en annen mekanisme som håndterer oppdatering av Immich-forekomsten din automatisk.", - "version_history": "Verson Historie", + "version_history": "Versjonshistorikk", "version_history_item": "Installert {version} den {date}", "video": "Video", "video_hover_setting": "Spill av forhåndsvisining mens du holder over musepekeren", @@ -1927,9 +2013,10 @@ "videos": "Videoer", "videos_count": "{count, plural, one {# Video} other {# Videoer}}", "view": "Vis", - "view_album": "Vis Album", + "view_album": "Vis album", "view_all": "Vis alle", "view_all_users": "Vis alle brukere", + "view_details": "Vis detaljer", "view_in_timeline": "Vis i tidslinje", "view_link": "Vis lenke", "view_links": "Vis lenker", @@ -1937,7 +2024,7 @@ "view_next_asset": "Vis neste fil", "view_previous_asset": "Vis forrige fil", "view_qr_code": "Vis QR-kode", - "view_stack": "Vis Stabbel", + "view_stack": "Vis stabel", "view_user": "Vis bruker", "viewer_remove_from_stack": "Fjern fra stabling", "viewer_stack_use_as_main_asset": "Bruk som hovedobjekt", @@ -1948,12 +2035,12 @@ "week": "Uke", "welcome": "Velkommen", "welcome_to_immich": "Velkommen til Immich", - "wifi_name": "Wi-Fi Navn", - "wrong_pin_code": "Feil PINkode", + "wifi_name": "Wi-Fi-navn", + "wrong_pin_code": "Feil PIN-kode", "year": "År", "years_ago": "{years, plural, one {# år} other {# år}} siden", "yes": "Ja", "you_dont_have_any_shared_links": "Du har ingen delte lenker", - "your_wifi_name": "Ditt Wi-Fi navn", + "your_wifi_name": "Ditt Wi-Fi-navn", "zoom_image": "Zoom Bilde" } diff --git a/i18n/nl.json b/i18n/nl.json index 07699e0b31..b287a7810f 100644 --- a/i18n/nl.json +++ b/i18n/nl.json @@ -14,6 +14,7 @@ "add_a_location": "Een locatie toevoegen", "add_a_name": "Naam toevoegen", "add_a_title": "Titel toevoegen", + "add_birthday": "Voeg een verjaardag toe", "add_endpoint": "Server toevoegen", "add_exclusion_pattern": "Uitsluitingspatroon toevoegen", "add_import_path": "Import-pad toevoegen", @@ -44,8 +45,15 @@ "backup_database": "Maak database back-up", "backup_database_enable_description": "Database back-ups activeren", "backup_keep_last_amount": "Aantal back-ups om te bewaren", - "backup_settings": "Database back-up instellingen", - "backup_settings_description": "Beheer database back-up instellingen.", + "backup_onboarding_1_description": "externe kopie in de cloud of op een andere fysieke locatie.", + "backup_onboarding_2_description": "lokale kopieën op verschillende apparaten. Dit omvat de hoofdbestanden én een lokale back-up van deze bestanden.", + "backup_onboarding_3_description": "totaal aantal kopieën van de gegevens, inclusief originele bestanden. Dit omvat 1 externe kopie en 2 lokale kopieën.", + "backup_onboarding_description": "Een 3-2-1 back-up strategie wordt aanbevolen om de gegevens te beschermen. Bewaar kopieën van de geüploade foto's/video's en de Immich database voor een complete back-up oplossing.", + "backup_onboarding_footer": "Raadpleeg de documentatie voor meer informatie over het maken van back-ups van Immich.", + "backup_onboarding_parts_title": "Een 3-2-1 back-up omvat:", + "backup_onboarding_title": "Back-ups", + "backup_settings": "Database dump instellingen", + "backup_settings_description": "Beheer database dump instellingen.", "cleared_jobs": "Taken gewist voor: {job}", "config_set_by_file": "Instellingen worden momenteel beheerd door een configuratiebestand", "confirm_delete_library": "Weet je zeker dat je de bibliotheek {library} wilt verwijderen?", @@ -93,7 +101,7 @@ "job_created": "Taak aangemaakt", "job_not_concurrency_safe": "Deze taak kan niet gelijktijdig worden uitgevoerd.", "job_settings": "Achtergrondtaak-instellingen", - "job_settings_description": "Beheer gelijktijdige taken", + "job_settings_description": "Beheer aantal gelijktijdige taken", "job_status": "Taakstatus", "jobs_delayed": "{jobCount, plural, other {# vertraagd}}", "jobs_failed": "{jobCount, plural, other {# mislukt}}", @@ -166,6 +174,20 @@ "metadata_settings_description": "Beheer metadata instellingen", "migration_job": "Migratie", "migration_job_description": "Migreer thumbnails voor assets en gezichten naar de nieuwste mapstructuur", + "nightly_tasks_cluster_faces_setting_description": "Gezichtsherkenning uitvoeren op nieuw gedetecteerde gezichten", + "nightly_tasks_cluster_new_faces_setting": "Cluster nieuwe gezichten", + "nightly_tasks_database_cleanup_setting": "Database opschoon taken", + "nightly_tasks_database_cleanup_setting_description": "Ruim oude data op van de database", + "nightly_tasks_generate_memories_setting": "Genereer herinneringen", + "nightly_tasks_generate_memories_setting_description": "Maak nieuwe herinneringen van assets", + "nightly_tasks_missing_thumbnails_setting": "Genereer ontbrekende thumbnails", + "nightly_tasks_missing_thumbnails_setting_description": "Assets zonder thumbnail in een wachtrij plaatsen voor het genereren van thumbnails", + "nightly_tasks_settings": "Instellingen voor nacht taken", + "nightly_tasks_settings_description": "Beheer nacht taken", + "nightly_tasks_start_time_setting": "Start tijd", + "nightly_tasks_start_time_setting_description": "De tijd waarop de server begint met het uitvoeren van de nacht taken", + "nightly_tasks_sync_quota_usage_setting": "Synchroniseer quota gebruik", + "nightly_tasks_sync_quota_usage_setting_description": "update gebruiker opslag quota, gebaseerd op huidig gebruik", "no_paths_added": "Geen paden toegevoegd", "no_pattern_added": "Geen patroon toegevoegd", "note_apply_storage_label_previous_assets": "Opmerking: om het opslaglabel toe te passen op eerder geüploade assets, voer de volgende taak uit", @@ -196,6 +218,8 @@ "oauth_mobile_redirect_uri": "Omleidings-URI voor mobiel", "oauth_mobile_redirect_uri_override": "Omleidings-URI voor mobiele app overschrijven", "oauth_mobile_redirect_uri_override_description": "Inschakelen wanneer de OAuth-provider geen mobiele URI toestaat, zoals ''{callback}''", + "oauth_role_claim": "Rol claim", + "oauth_role_claim_description": "Automatisch admin toegang geven als deze claim aanwezig is. De claim kan 'user' of 'admin' zijn.", "oauth_settings": "OAuth", "oauth_settings_description": "Beheer OAuth inloginstellingen", "oauth_settings_more_details": "Raadpleeg de documentatie voor meer informatie over deze functie.", @@ -305,7 +329,7 @@ "transcoding_policy_description": "Stel in wanneer een video wordt getranscodeerd", "transcoding_preferred_hardware_device": "Voorkeur hardwareapparaat", "transcoding_preferred_hardware_device_description": "Geldt alleen voor VAAPI en QSV. Stelt de dri node in die wordt gebruikt voor hardwaretranscodering.", - "transcoding_preset_preset": "Preset (-preset)", + "transcoding_preset_preset": "Voorkeuze (-preset)", "transcoding_preset_preset_description": "Compressiesnelheid. Langzamere presets produceren kleinere bestanden en verhogen de kwaliteit bij het targeten van een bepaalde bitrate. VP9 negeert snelheden boven 'faster'.", "transcoding_reference_frames": "Referentie frames", "transcoding_reference_frames_description": "Het aantal frames om naar te verwijzen bij het comprimeren van een bepaald frame. Hogere waarden verbeteren de compressie-efficiëntie, maar vertragen de codering. Bij 0 wordt deze waarde automatisch ingesteld.", @@ -357,10 +381,12 @@ "admin_password": "Beheerder wachtwoord", "administration": "Beheer", "advanced": "Geavanceerd", + "advanced_settings_beta_timeline_subtitle": "Probeer de nieuwe app-ervaring", + "advanced_settings_beta_timeline_title": "Beta tijdlijn", "advanced_settings_enable_alternate_media_filter_subtitle": "Gebruik deze optie om media te filteren tijdens de synchronisatie op basis van alternatieve criteria. Gebruik dit enkel als de app problemen heeft met het detecteren van albums.", "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTEEL] Gebruik een alternatieve album synchronisatie filter", "advanced_settings_log_level_title": "Logniveau: {level}", - "advanced_settings_prefer_remote_subtitle": "Sommige apparaten zijn traag met het laden van afbeeldingen die lokaal zijn opgeslagen op het apparaat. Activeer deze instelling om in plaats daarvan externe afbeeldingen te laden.", + "advanced_settings_prefer_remote_subtitle": "Sommige apparaten zijn traag met het laden van lokale afbeeldingen. Activeer deze instelling om in plaats daarvan externe afbeeldingen te laden.", "advanced_settings_prefer_remote_title": "Externe afbeeldingen laden", "advanced_settings_proxy_headers_subtitle": "Definieer proxy headers die Immich bij elk netwerkverzoek moet verzenden", "advanced_settings_proxy_headers_title": "Proxy headers", @@ -379,6 +405,7 @@ "album_cover_updated": "Album cover is bijgewerkt", "album_delete_confirmation": "Weet je zeker dat je het album {album} wilt verwijderen?", "album_delete_confirmation_description": "Als dit album gedeeld is, hebben andere gebruikers er geen toegang meer toe.", + "album_deleted": "Album verwijderd", "album_info_card_backup_album_excluded": "UITGESLOTEN", "album_info_card_backup_album_included": "INBEGREPEN", "album_info_updated": "Albumgegevens bijgewerkt", @@ -388,6 +415,7 @@ "album_options": "Albumopties", "album_remove_user": "Gebruiker verwijderen?", "album_remove_user_confirmation": "Weet je zeker dat je {user} wilt verwijderen?", + "album_search_not_found": "Geen albums gevonden die aan je zoekopdracht voldoen", "album_share_no_users": "Het lijkt erop dat je dit album met alle gebruikers hebt gedeeld, of dat je geen gebruikers hebt om mee te delen.", "album_updated": "Album bijgewerkt", "album_updated_setting_description": "Ontvang een e-mailmelding wanneer een gedeeld album nieuwe assets heeft", @@ -407,6 +435,7 @@ "albums_default_sort_order": "Standaard sorteervolgorde album", "albums_default_sort_order_description": "Initiële sorteervolgorde bij het maken van nieuwe albums.", "albums_feature_description": "Collectie van assets die je kan delen met andere gebruikers.", + "albums_on_device_count": "Albums op apparaat ({count})", "all": "Alle", "all_albums": "Alle albums", "all_people": "Alle mensen", @@ -427,6 +456,7 @@ "app_settings": "App instellingen", "appears_in": "Komt voor in", "archive": "Archief", + "archive_action_prompt": "{count} toegevoegd aan archief", "archive_or_unarchive_photo": "Foto archiveren of uit het archief halen", "archive_page_no_archived_assets": "Geen gearchiveerde assets gevonden", "archive_page_title": "Archief ({count})", @@ -450,7 +480,7 @@ "asset_list_layout_settings_group_by": "Groepeer assets per", "asset_list_layout_settings_group_by_month_day": "Maand + dag", "asset_list_layout_sub_title": "Layout", - "asset_list_settings_subtitle": "Fotorasterlayoutinstellingen", + "asset_list_settings_subtitle": "Fotoraster layout instellingen", "asset_list_settings_title": "Fotoraster", "asset_offline": "Asset offline", "asset_offline_description": "Deze externe asset is niet meer op de schijf te vinden. Neem contact op met de Immich beheerder voor hulp.", @@ -459,12 +489,11 @@ "asset_skipped_in_trash": "In prullenbak", "asset_uploaded": "Geüpload", "asset_uploading": "Uploaden…", - "asset_viewer_settings_subtitle": "Beheer je instellingen voor gallerijweergave", - "asset_viewer_settings_title": "Foto weergave", + "asset_viewer_settings_subtitle": "Beheer je instellingen voor galerijweergave", + "asset_viewer_settings_title": "Fotoweergave", "assets": "Assets", "assets_added_count": "{count, plural, one {# asset} other {# assets}} toegevoegd", "assets_added_to_album_count": "{count, plural, one {# asset} other {# assets}} aan het album toegevoegd", - "assets_added_to_name_count": "{count, plural, one {# asset} other {# assets}} toegevoegd aan {hasName, select, true {{name}} other {nieuw album}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {# asset} other {# assets}} konden niet aan album toegevoegd worden", "assets_count": "{count, plural, one {# asset} other {# assets}}", "assets_deleted_permanently": "{count} asset(s) permanent verwijderd", @@ -490,34 +519,35 @@ "back_close_deselect": "Terug, sluiten of deselecteren", "background_location_permission": "Achtergrond locatie toestemming", "background_location_permission_content": "Om van netwerk te wisselen terwijl de app op de achtergrond draait, heeft Immich *altijd* toegang tot de exacte locatie nodig om de naam van het WiFi-netwerk te kunnen lezen", + "backup": "Back-up", "backup_album_selection_page_albums_device": "Albums op apparaat ({count})", - "backup_album_selection_page_albums_tap": "Tik om in te voegen, dubbel tik om uit te sluiten", - "backup_album_selection_page_assets_scatter": "Assets kunnen over verschillende albums verdeeld zijn, dus albums kunnen inbegrepen of uitgesloten zijn van het backup proces.", - "backup_album_selection_page_select_albums": "Albums selecteren", + "backup_album_selection_page_albums_tap": "Tik om op te nemen, dubbel tik om uit te sluiten", + "backup_album_selection_page_assets_scatter": "Assets kunnen over verschillende albums verdeeld zijn, dus albums kunnen inbegrepen of uitgesloten zijn van het back-up proces.", + "backup_album_selection_page_select_albums": "Selecteer albums", "backup_album_selection_page_selection_info": "Selectie info", "backup_album_selection_page_total_assets": "Totaal unieke assets", "backup_all": "Alle", - "backup_background_service_backup_failed_message": "Fout bij back-uppen assets. Opnieuw proberen…", - "backup_background_service_connection_failed_message": "Fout bij verbinden server. Opnieuw proberen…", - "backup_background_service_current_upload_notification": "{filename} aan het uploaden...", + "backup_background_service_backup_failed_message": "Fout bij het back-uppen van de assets. Opnieuw proberen…", + "backup_background_service_connection_failed_message": "Fout bij het verbinden met de server. Opnieuw proberen…", + "backup_background_service_current_upload_notification": "{filename} wordt geüpload...", "backup_background_service_default_notification": "Controleren op nieuwe assets…", - "backup_background_service_error_title": "Backupfout", + "backup_background_service_error_title": "Back-up fout", "backup_background_service_in_progress_notification": "Back-up van assets maken…", "backup_background_service_upload_failure_notification": "Fout bij het uploaden van {filename}", "backup_controller_page_albums": "Back-up albums", - "backup_controller_page_background_app_refresh_disabled_content": "Schakel verversen op de achtergrond in via Instellingen > Algemeen > Ververs op achtergrond, om back-ups op de achtergrond te maken.", - "backup_controller_page_background_app_refresh_disabled_title": "Verversen op achtergrond uitgeschakeld", + "backup_controller_page_background_app_refresh_disabled_content": "Schakel verversen op de achtergrond in via 'Instellingen > Algemeen > Ververs op achtergrond', om back-ups op de achtergrond te maken.", + "backup_controller_page_background_app_refresh_disabled_title": "Verversen op achtergrond is uitgeschakeld", "backup_controller_page_background_app_refresh_enable_button_text": "Ga naar instellingen", "backup_controller_page_background_battery_info_link": "Laat zien hoe", - "backup_controller_page_background_battery_info_message": "Voor de beste back-upervaring, schakel je alle batterijoptimalisaties uit omdat deze op-de-achtergrondactiviteiten van Immich beperken.\n\nAangezien dit apparaatspecifiek is, zoek de vereiste informatie op voor de fabrikant van je apparaat.", + "backup_controller_page_background_battery_info_message": "Voor de beste achtergrond back-up ervaring schakelt u alle batterij optimalisaties uit die de achtergrondactiviteit voor Immich kunnen beperken.\n\nAangezien dit apparaat specifiek is, raden we aan om de vereiste informatie op te zoeken bij de fabrikant van je apparaat.", "backup_controller_page_background_battery_info_ok": "OK", - "backup_controller_page_background_battery_info_title": "Batterijoptimalisaties", + "backup_controller_page_background_battery_info_title": "Batterij optimalisaties", "backup_controller_page_background_charging": "Alleen tijdens opladen", - "backup_controller_page_background_configure_error": "Achtergrondserviceconfiguratie mislukt", - "backup_controller_page_background_delay": "Back-upvertraging voor nieuwe assets: {duration}", + "backup_controller_page_background_configure_error": "Achtergrondservice configuratie mislukt", + "backup_controller_page_background_delay": "Back-up vertraging voor nieuwe assets: {duration}", "backup_controller_page_background_description": "Schakel de achtergrondservice in om automatisch een back-up te maken van nieuwe assets zonder de app te hoeven openen", - "backup_controller_page_background_is_off": "Automatische achtergrondback-up staat uit", - "backup_controller_page_background_is_on": "Automatische achtergrondback-up staat aan", + "backup_controller_page_background_is_off": "Automatische achtergrond back-up staat uit", + "backup_controller_page_background_is_on": "Automatische achtergrond back-up staat aan", "backup_controller_page_background_turn_off": "Achtergrondservice uitzetten", "backup_controller_page_background_turn_on": "Achtergrondservice aanzetten", "backup_controller_page_background_wifi": "Alleen op WiFi", @@ -553,6 +583,8 @@ "backup_options_page_title": "Back-up instellingen", "backup_setting_subtitle": "Beheer achtergrond en voorgrond uploadinstellingen", "backward": "Achteruit", + "beta_sync": "Beta Sync Status", + "beta_sync_subtitle": "Beheer het nieuwe synchronisatiesysteem", "biometric_auth_enabled": "Biometrische authenticatie ingeschakeld", "biometric_locked_out": "Biometrische authenticatie is vergrendeld", "biometric_no_options": "Geen biometrische opties beschikbaar", @@ -570,7 +602,7 @@ "cache_settings_clear_cache_button": "Cache wissen", "cache_settings_clear_cache_button_title": "Wist de cache van de app. Dit zal de presentaties van de app aanzienlijk beïnvloeden totdat de cache opnieuw is opgebouwd.", "cache_settings_duplicated_assets_clear_button": "MAAK VRIJ", - "cache_settings_duplicated_assets_subtitle": "Foto's en video's op de zwarte lijst van de app", + "cache_settings_duplicated_assets_subtitle": "Foto’s en video's die de app negeert", "cache_settings_duplicated_assets_title": "Gedupliceerde assets ({count})", "cache_settings_statistics_album": "Bibliotheekthumbnails", "cache_settings_statistics_full": "Volledige afbeeldingen", @@ -587,6 +619,7 @@ "cancel": "Annuleren", "cancel_search": "Zoeken annuleren", "canceled": "Geannuleerd", + "canceling": "Annuleren", "cannot_merge_people": "Kan mensen niet samenvoegen", "cannot_undo_this_action": "Je kunt deze actie niet ongedaan maken!", "cannot_update_the_description": "Kan de beschrijving niet bijwerken", @@ -697,13 +730,14 @@ "curated_object_page_title": "Dingen", "current_device": "Huidig apparaat", "current_pin_code": "Huidige PIN code", - "current_server_address": "Huidige serveradres", + "current_server_address": "Huidig serveradres", "custom_locale": "Aangepaste landinstelling", "custom_locale_description": "Formatteer datums en getallen op basis van de taal en de regio", + "custom_url": "Aangepaste URL", "daily_title_text_date": "E dd MMM", "daily_title_text_date_year": "E dd MMM yyyy", "dark": "Donker", - "darkTheme": "Donker thema in-/uitschakelen", + "dark_theme": "Wissel naar donker thema", "date_after": "Datum na", "date_and_time": "Datum en tijd", "date_before": "Datum voor", @@ -715,10 +749,12 @@ "deduplication_criteria_1": "Grootte van afbeelding in bytes", "deduplication_criteria_2": "Aantal EXIF data", "deduplication_info": "Deduplicatie-info", - "deduplication_info_description": "Om automatisch bezittingen te preselecteren en duplicaten te verwijderen in bulk, kijken we naar:", + "deduplication_info_description": "Om automatisch items te preselecteren en duplicaten te verwijderen in bulk, kijken we naar:", "default_locale": "Standaard landinstelling", "default_locale_description": "Formatteer datums en getallen op basis van de landinstellingen van je browser", "delete": "Verwijderen", + "delete_action_confirmation_message": "Weet je zeker dat je dit item wilt verwijderen? Deze actie zorgt ervoor dat het item naar de prullenbak van de server wordt verplaatst en je wordt gevraagd of je deze ook lokaal wilt verwijderen", + "delete_action_prompt": "{count} verwijderd", "delete_album": "Album verwijderen", "delete_api_key_prompt": "Weet je zeker dat je deze API-sleutel wilt verwijderen?", "delete_dialog_alert": "Deze items zullen permanent verwijderd worden van Immich en je apparaat", @@ -732,9 +768,12 @@ "delete_key": "Verwijder key", "delete_library": "Verwijder bibliotheek", "delete_link": "Verwijder link", + "delete_local_action_prompt": "{count} lokaal verwijderd", "delete_local_dialog_ok_backed_up_only": "Verwijder alleen met back-up", "delete_local_dialog_ok_force": "Toch verwijderen", "delete_others": "Andere verwijderen", + "delete_permanently": "Permanent verwijderen", + "delete_permanently_action_prompt": "{count} permanent verwijderd", "delete_shared_link": "Verwijder gedeelde link", "delete_shared_link_dialog_title": "Verwijder gedeelde link", "delete_tag": "Tag verwijderen", @@ -745,6 +784,7 @@ "description": "Beschrijving", "description_input_hint_text": "Beschrijving toevoegen...", "description_input_submit_error": "Beschrijving bijwerken mislukt, controleer het logboek voor meer details", + "deselect_all": "Alles deselecteren", "details": "Details", "direction": "Richting", "disabled": "Uitgeschakeld", @@ -762,6 +802,7 @@ "documentation": "Documentatie", "done": "Klaar", "download": "Downloaden", + "download_action_prompt": "{count} assets downloaden", "download_canceled": "Download geannuleerd", "download_complete": "Download voltooid", "download_enqueue": "Download in wachtrij", @@ -788,6 +829,7 @@ "edit": "Bewerken", "edit_album": "Album bewerken", "edit_avatar": "Avatar bewerken", + "edit_birthday": "Wijzig verjaardag", "edit_date": "Datum bewerken", "edit_date_and_time": "Datum en tijd bewerken", "edit_description": "Beschrijving bewerken", @@ -799,6 +841,7 @@ "edit_key": "Key bewerken", "edit_link": "Link bewerken", "edit_location": "Locatie bewerken", + "edit_location_action_prompt": "{count} locatie(s) aangepast", "edit_location_dialog_title": "Locatie", "edit_name": "Naam bewerken", "edit_people": "Mensen bewerken", @@ -817,6 +860,7 @@ "empty_trash": "Prullenbak leegmaken", "empty_trash_confirmation": "Weet je zeker dat je de prullenbak wilt legen? Hiermee worden alle assets in de prullenbak permanent uit Immich verwijderd.\nJe kunt deze actie niet ongedaan maken!", "enable": "Inschakelen", + "enable_backup": "Back-up aanzetten", "enable_biometric_auth_description": "Voer uw pincode in om biometrische authenticatie in te schakelen", "enabled": "Ingeschakeld", "end_date": "Einddatum", @@ -953,6 +997,7 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "Beschrijving toevoegen...", + "exif_bottom_sheet_description_error": "Fout bij het bijwerken van de beschrijving", "exif_bottom_sheet_details": "DETAILS", "exif_bottom_sheet_location": "LOCATIE", "exif_bottom_sheet_people": "MENSEN", @@ -973,6 +1018,8 @@ "explorer": "Verkenner", "export": "Exporteren", "export_as_json": "Exporteren als JSON", + "export_database": "Exporteer database", + "export_database_description": "Exporteer de SQLite database", "extension": "Extensie", "external": "Extern", "external_libraries": "Externe bibliotheken", @@ -984,6 +1031,7 @@ "failed_to_load_assets": "Kan assets niet laden", "failed_to_load_folder": "Laden van map mislukt", "favorite": "Favoriet", + "favorite_action_prompt": "{count}toegevoegd aan Favorieten", "favorite_or_unfavorite_photo": "Foto markeren als of verwijderen uit favorieten", "favorites": "Favorieten", "favorites_page_no_favorites": "Geen favoriete assets gevonden", @@ -1023,6 +1071,9 @@ "haptic_feedback_switch": "Aanraaktrillingen inschakelen", "haptic_feedback_title": "Aanraaktrillingen", "has_quota": "Heeft limiet", + "hash_asset": "Hash asset", + "hashed_assets": "Gehashte assets", + "hashing": "Hashen", "header_settings_add_header_tip": "Header toevoegen", "header_settings_field_validator_msg": "Waarde kan niet leeg zijn", "header_settings_header_name_input": "Header naam", @@ -1031,7 +1082,7 @@ "headers_settings_tile_title": "Aangepaste proxy headers", "hi_user": "Hallo {name} ({email})", "hide_all_people": "Verberg alle mensen", - "hide_gallery": "Gallerij verbergen", + "hide_gallery": "Galerij verbergen", "hide_named_person": "Verberg persoon {name}", "hide_password": "Verberg wachtwoord", "hide_person": "Verberg persoon", @@ -1055,6 +1106,7 @@ "host": "Host", "hour": "Uur", "id": "ID", + "idle": "Inactief", "ignore_icloud_photos": "Negeer iCloud foto's", "ignore_icloud_photos_description": "Foto's die op iCloud zijn opgeslagen, worden niet geüpload naar de Immich server", "image": "Afbeelding", @@ -1112,6 +1164,7 @@ "language_no_results_title": "Geen talen gevonden", "language_search_hint": "Zoek talen...", "language_setting_description": "Selecteer je voorkeurstaal", + "large_files": "Grote bestanden", "last_seen": "Laatst gezien", "latest_version": "Nieuwste versie", "latitude": "Breedtegraad", @@ -1127,16 +1180,18 @@ "library_page_sort_created": "Meest recent gemaakt", "library_page_sort_last_modified": "Laatst aangepast", "library_page_sort_title": "Albumtitel", + "licenses": "Licenties", "light": "Licht", "like_deleted": "Like verwijderd", "link_motion_video": "Koppel bewegende video", - "link_options": "Opties voor koppeling", "link_to_oauth": "Koppel OAuth", "linked_oauth_account": "Gekoppeld OAuth account", "list": "Lijst", "loading": "Laden", "loading_search_results_failed": "Laden van zoekresultaten mislukt", + "local": "Lokaal", "local_asset_cast_failed": "Kan geen asset casten die nog niet geüpload is naar de server", + "local_assets": "Lokale Assets", "local_network": "Lokaal netwerk", "local_network_sheet_info": "De app maakt verbinding met de server via deze URL wanneer het opgegeven WiFi-netwerk wordt gebruikt", "location_permission": "Locatietoestemming", @@ -1193,8 +1248,7 @@ "manage_your_devices": "Beheer je ingelogde apparaten", "manage_your_oauth_connection": "Beheer je OAuth-koppeling", "map": "Kaart", - "map_assets_in_bound": "{count} foto", - "map_assets_in_bounds": "{count} foto's", + "map_assets_in_bounds": "{count, plural, one {# foto} other {# foto's}}", "map_cannot_get_user_location": "Kan locatie van de gebruiker niet ophalen", "map_location_dialog_yes": "Ja", "map_location_picker_page_use_location": "Gebruik deze locatie", @@ -1246,6 +1300,7 @@ "more": "Meer", "move": "Verplaats", "move_off_locked_folder": "Verplaats uit vergrendelde map", + "move_to_lock_folder_action_prompt": "{count} toegevoegd aan de vergrendelde map", "move_to_locked_folder": "Verplaats naar vergrendelde map", "move_to_locked_folder_confirmation": "Deze foto’s en video’s worden uit alle albums verwijderd en zijn alleen te bekijken in de vergrendelde map", "moved_to_archive": "{count, plural, one {# asset} other {# assets}} verplaatst naar archief", @@ -1292,6 +1347,7 @@ "no_results": "Geen resultaten", "no_results_description": "Probeer een synoniem of een algemener zoekwoord", "no_shared_albums_message": "Maak een album om foto's en video's te delen met mensen in je netwerk", + "no_uploads_in_progress": "Geen uploads bezig", "not_in_any_album": "Niet in een album", "not_selected": "Niet geselecteerd", "note_apply_storage_label_to_previously_uploaded assets": "Opmerking: om het opslaglabel toe te passen op eerder geüploade assets, voer de volgende taak uit", @@ -1329,6 +1385,7 @@ "original": "origineel", "other": "Overige", "other_devices": "Andere apparaten", + "other_entities": "Andere entities", "other_variables": "Andere variabelen", "owned": "Eigenaar", "owner": "Eigenaar", @@ -1460,6 +1517,7 @@ "purchase_server_description_2": "Supporterstatus", "purchase_server_title": "Server", "purchase_settings_server_activated": "De licentiesleutel van de server wordt beheerd door de beheerder", + "queue_status": "Wachtrij {count}/{total}", "rating": "Sterwaardering", "rating_clear": "Waardering verwijderen", "rating_count": "{count, plural, one {# ster} other {# sterren}}", @@ -1488,6 +1546,8 @@ "refreshing_faces": "Gezichten aan het vernieuwen", "refreshing_metadata": "Metadata aan het vernieuwen", "regenerating_thumbnails": "Thumbnails opnieuw aan het genereren", + "remote": "Externe", + "remote_assets": "Externe Assets", "remove": "Verwijderen", "remove_assets_album_confirmation": "Weet je zeker dat je {count, plural, one {# asset} other {# assets}} uit het album wilt verwijderen?", "remove_assets_shared_link_confirmation": "Weet je zeker dat je {count, plural, one {# asset} other {# assets}} uit deze gedeelde link wilt verwijderen?", @@ -1495,7 +1555,9 @@ "remove_custom_date_range": "Aangepast datumbereik verwijderen", "remove_deleted_assets": "Verwijder offline bestanden", "remove_from_album": "Verwijder uit album", + "remove_from_album_action_prompt": "{count} verwijderd uit het album", "remove_from_favorites": "Verwijderen uit favorieten", + "remove_from_lock_folder_action_prompt": "{count} verwijderd uit de vergrendelde map", "remove_from_locked_folder": "Verwijder uit de vergrendelde map", "remove_from_locked_folder_confirmation": "Weet je zeker dat je deze foto's en video's uit de vergrendelde map wilt verplaatsen? Ze zijn dan weer zichtbaar in je bibliotheek.", "remove_from_shared_link": "Verwijderen uit gedeelde link", @@ -1523,19 +1585,25 @@ "reset_password": "Wachtwoord resetten", "reset_people_visibility": "Zichtbaarheid mensen resetten", "reset_pin_code": "Reset PIN code", + "reset_sqlite": "SQLite database resetten", + "reset_sqlite_confirmation": "Ben je zeker dat je de SQLite database wilt resetten? Je zal moeten uitloggen om de data opnieuw te synchroniseren", + "reset_sqlite_success": "De SQLite database is succesvol gereset", "reset_to_default": "Resetten naar standaard", "resolve_duplicates": "Duplicaten oplossen", "resolved_all_duplicates": "Alle duplicaten opgelost", "restore": "Herstellen", "restore_all": "Herstel alle", + "restore_trash_action_prompt": "{count} teruggezet uit prullenbak", "restore_user": "Gebruiker herstellen", "restored_asset": "Asset hersteld", "resume": "Hervatten", "retry_upload": "Opnieuw uploaden", "review_duplicates": "Controleer duplicaten", + "review_large_files": "Grote bestanden beoordelen", "role": "Rol", "role_editor": "Bewerker", "role_viewer": "Bekijker", + "running": "Actief", "save": "Opslaan", "save_to_gallery": "Opslaan in galerij", "saved_api_key": "API-sleutel opgeslagen", @@ -1667,6 +1735,7 @@ "settings_saved": "Instellingen opgeslagen", "setup_pin_code": "Stel een PIN code in", "share": "Delen", + "share_action_prompt": "{count} assets gedeeld", "share_add_photos": "Foto's toevoegen", "share_assets_selected": "{count} geselecteerd", "share_dialog_preparing": "Voorbereiden...", @@ -1688,6 +1757,7 @@ "shared_link_clipboard_copied_massage": "Gekopieerd naar klembord", "shared_link_clipboard_text": "Link: {link}\nWachtwoord: {password}", "shared_link_create_error": "Fout bij het maken van een gedeelde link", + "shared_link_custom_url_description": "Krijg toegang tot deze gedeelde link met een aangepaste URL", "shared_link_edit_description_hint": "Voer beschrijving voor de gedeelde link in", "shared_link_edit_expire_after_option_day": "1 dag", "shared_link_edit_expire_after_option_days": "{count} dagen", @@ -1713,6 +1783,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Beheer gedeelde links", "shared_link_options": "Opties voor gedeelde links", + "shared_link_password_description": "Vraag een wachtwoord om toegang te krijgen tot deze gedeelde link", "shared_links": "Gedeelde links", "shared_links_description": "Deel foto's en video's via een link", "shared_photos_and_videos_count": "{assetCount, plural, other {# gedeelde foto's & video's.}}", @@ -1768,6 +1839,7 @@ "sort_title": "Titel", "source": "Bron", "stack": "Stapel", + "stack_action_prompt": "{count} gestapeld", "stack_duplicates": "Stapel duplicaten", "stack_select_one_photo": "Selecteer één primaire foto voor de stapel", "stack_selected_photos": "Geselecteerde foto's stapelen", @@ -1787,6 +1859,7 @@ "storage_quota": "Opslaglimiet", "storage_usage": "{used} van {available} gebruikt", "submit": "Verzenden", + "success": "Succes", "suggestions": "Suggesties", "sunrise_on_the_beach": "Zonsopkomst op het strand", "support": "Ondersteuning", @@ -1796,6 +1869,8 @@ "sync": "Sync", "sync_albums": "Albums synchroniseren", "sync_albums_manual_subtitle": "Synchroniseer alle geüploade video’s en foto’s naar de geselecteerde back-up albums", + "sync_local": "Lokaal synchroniseren", + "sync_remote": "Op afstand synchroniseren", "sync_upload_album_setting_subtitle": "Maak en upload je foto's en video's naar de geselecteerde albums op Immich", "tag": "Tag", "tag_assets": "Assets taggen", @@ -1806,6 +1881,7 @@ "tag_updated": "Tag bijgewerkt: {tag}", "tagged_assets": "{count, plural, one {# asset} other {# assets}} getagd", "tags": "Tags", + "tap_to_run_job": "Klik om job te starten", "template": "Template", "theme": "Thema", "theme_selection": "Thema selectie", @@ -1838,6 +1914,7 @@ "total": "Totaal", "total_usage": "Totaal gebruik", "trash": "Prullenbak", + "trash_action_prompt": "{count} verwijderd naar de prullenbak", "trash_all": "Verplaats alle naar prullenbak", "trash_count": "{count, number} naar prullenbak", "trash_delete_asset": "Assets naar prullenbak verplaatsen of verwijderen", @@ -1855,9 +1932,11 @@ "unable_to_change_pin_code": "PIN code kan niet gewijzigd worden", "unable_to_setup_pin_code": "PIN code kan niet ingesteld worden", "unarchive": "Herstellen uit archief", + "unarchive_action_prompt": "{count} verwijderd uit het archief", "unarchived_count": "{count, plural, other {# verwijderd uit archief}}", "undo": "Ongedaan maken", "unfavorite": "Verwijderen uit favorieten", + "unfavorite_action_prompt": "{count} verwijderd uit favorieten", "unhide_person": "Persoon zichtbaar maken", "unknown": "Onbekend", "unknown_country": "Onbekend Land", @@ -1875,15 +1954,20 @@ "unselect_all_duplicates": "Deselecteer alle duplicaten", "unselect_all_in": "Deselecteer alles in {group}", "unstack": "Ontstapelen", + "unstack_action_prompt": "{count} ontstapeld", "unstacked_assets_count": "{count, plural, one {# asset} other {# assets}} ontstapeld", + "untagged": "Ongemarkeerd", "up_next": "Volgende", "updated_at": "Geüpdatet", "updated_password": "Wachtwoord bijgewerkt", "upload": "Uploaden", - "upload_concurrency": "Upload gelijktijdigheid", + "upload_action_prompt": "{count} in de wachtrij voor uploaden", + "upload_concurrency": "Aantal gelijktijdige uploads", + "upload_details": "Uploaddetails", "upload_dialog_info": "Wil je een backup maken van de geselecteerde asset(s) op de server?", "upload_dialog_title": "Asset uploaden", "upload_errors": "Upload voltooid met {count, plural, one {# fout} other {# fouten}}, vernieuw de pagina om de nieuwe assets te zien.", + "upload_finished": "Uploaden is voltooid", "upload_progress": "Resterend {remaining, number} - Verwerkt {processed, number}/{total, number}", "upload_skipped_duplicates": "{count, plural, one {# duplicate asset} other {# duplicate assets}} overgeslagen", "upload_status_duplicates": "Duplicaten", @@ -1892,6 +1976,7 @@ "upload_success": "Uploaden gelukt, vernieuw de pagina om de nieuwe assets te zien.", "upload_to_immich": "Uploaden naar Immich ({count})", "uploading": "Aan het uploaden", + "uploading_media": "Media wordt geüpload", "url": "URL", "usage": "Gebruik", "use_biometric": "Gebruik biometrische authenticatie", @@ -1912,6 +1997,7 @@ "user_usage_stats_description": "Bekijk statistieken van accountgebruik", "username": "Gebruikersnaam", "users": "Gebruikers", + "users_added_to_album_count": "{count, plural, one {# Gebruiker} other {# Gebruikers}} toegevoegd aan album", "utilities": "Gereedschap", "validate": "Valideren", "validate_endpoint_error": "Vul een geldige URL in", @@ -1930,6 +2016,7 @@ "view_album": "Bekijk album", "view_all": "Bekijk alle", "view_all_users": "Bekijk alle gebruikers", + "view_details": "Bekijk details", "view_in_timeline": "Bekijk in tijdlijn", "view_link": "Bekijk link", "view_links": "Links bekijken", diff --git a/i18n/nn.json b/i18n/nn.json index 7fb5fdef02..8b04b5d4b2 100644 --- a/i18n/nn.json +++ b/i18n/nn.json @@ -22,18 +22,20 @@ "add_partner": "Legg til partnar", "add_path": "Legg til sti", "add_photos": "Legg til bilete", + "add_tag": "Legg til tagg", "add_to": "Legg til…", - "add_to_album": "Legg til album", + "add_to_album": "Legg til i album", "add_to_album_bottom_sheet_added": "Lagt til i {album}", "add_to_album_bottom_sheet_already_exists": "Allereie i {album}", - "add_to_shared_album": "Legg til delt album", + "add_to_shared_album": "Legg til i delt album", "add_url": "Legg til URL", - "added_to_archive": "Lagt til arkiv", - "added_to_favorites": "Lagt til favorittar", - "added_to_favorites_count": "Lagt {count, number} til favorittar", + "added_to_archive": "Lagt til i arkiv", + "added_to_favorites": "Lagt til i favorittar", + "added_to_favorites_count": "La til {count, number} i favorittar", "admin": { "add_exclusion_pattern_description": "Legg til utelatingsmønstre. Du kan bruke jokerteikna *, **, og ? for å finne filer som passar mønsteret. For å ignorere alle filer i ei mappe kalla \"Raw\", bruk \"Raw\", bruk \"**/Raw/**\". For å ignorere alle filer som sluttar på \".tif\", bruk \"**/*.tif\". For å ignorere ein absolutt sti, bruk \"/path/to/ignore/**\".", - "asset_offline_description": "Denne eksterne bibliotekressursen finst ikkje lenger på disk og har blitt flytta til papirkurven. Om fila blei flytta innad i biblioteket, sjekk tidslinja di for den tilsvarande ressursen. For å gjenopprette ressursen, vennligst sørg for at filstien under er tilgjengeleg for Immich og skann biblioteket.", + "admin_user": "Admin-brukar", + "asset_offline_description": "Denne eksterne bibliotekressursen finst ikkje lenger på disk og har blitt flytta til papirkorga. Om fila blei flytta innad i biblioteket, sjekk tidslinja di for den tilsvarande ressursen. For å gjenopprette ressursen, vennligst sørg for at filstien under er tilgjengeleg for Immich og skann biblioteket.", "authentication_settings": "Godkjenningsinnstillingar", "authentication_settings_description": "Handsam passord, OAuth, og godkjenningsinnstillingar", "authentication_settings_disable_all": "Er du sikker at du ynskjer å gjera alle innloggingsmetodar uverksame? Innlogging vil bli heilt uverksam.", @@ -43,7 +45,7 @@ "backup_database_enable_description": "Aktiver tryggingskopiering av database", "backup_keep_last_amount": "Antal tryggingskopiar å behalde", "backup_settings": "Tryggingskopi-innstillingar", - "backup_settings_description": "Handter innstillingar for tryggingskopiering av database. Merk: Desse jobbane vert ikkje overvaka, og du får inga varsling ved feil.", + "backup_settings_description": "Handter innstillingar for tryggingskopiering av database. Merk: Desse jobbane vert ikkje overvaka, og du får inga varsling ved feil.", "cleared_jobs": "Rydda jobbar for: {job}", "config_set_by_file": "Oppsettet blir sett av ei oppsettfil", "confirm_delete_library": "Er du sikker at du vil slette biblioteket {library}?", @@ -51,6 +53,7 @@ "confirm_email_below": "For å bekrefte, skriv \"{email}\" under", "confirm_reprocess_all_faces": "Er du sikker på at du vil behandle alle ansikt på nytt? Det vil òg fjerne namngjevne personar.", "confirm_user_password_reset": "Er du sikker at du vil tilbakestille passordet til {user}?", + "confirm_user_pin_code_reset": "Er du sikker på at du vil tilbakestille {user} sin PIN-kode?", "create_job": "Lag jobb", "cron_expression": "Cron uttrykk", "cron_expression_description": "Set inn skanningsintervall med cron-formatet. For meir informasjon sjå t.d. Crontab Guru", @@ -66,6 +69,10 @@ "force_delete_user_warning": "ÅTVARING: Handlinga fjernar brukaren og all data. Du kan ikkje angre, og filane kan ikkje gjenopprettast.", "image_format": "Format", "image_format_description": "WebP gjev mindre filstorleik enn JPEG, men er treigare å lage.", + "image_fullsize_description": "Bilete i full storleik utan metadata, i bruk når zooma inn", + "image_fullsize_enabled": "Skru på generering av bilete i full storleik", + "image_fullsize_quality_description": "Kvalitet på bilete i full storleik frå 1-100. Høgare er betre, men gjev større filer.", + "image_fullsize_title": "Innstillingar for bilete i full storleik", "image_prefer_embedded_preview": "Bruk helst innebygd førehandsvisning", "image_prefer_embedded_preview_setting_description": "Når mogleg bruk innebygd førehandsvisning av RAW bilete som inndata til biletehandsaming. For noko bilete kan det gje meir nøyaktige farger, men kvaliteten kjem an på kamera og det kan oppstå komprimeringsartefakt i bilete.", "image_prefer_wide_gamut": "Bruk helst breitt fargespektrum", @@ -121,6 +128,7 @@ "machine_learning_max_detection_distance": "Maksimal oppdagingsverdi", "machine_learning_max_detection_distance_description": "Den største skilnaden mellom to bilete for å rekne dei som duplikat, frå 0.001-0.1. Større verdiar finn fleire duplikat, men kan gje falske treff.", "machine_learning_max_recognition_distance": "Maksimal attkjenningsverdi", + "machine_learning_max_recognition_distance_description": "Maksimal forskjell på to ansikt for å bli rekna som same person, på ein skala frå 0-2. Eit lågare tal kan hindre to personar i å bli rekna som den same, medan eit høgare tal kan hindre at same individ vert merka som to forskjellige personar. Merk at det er lettare å slå saman to personar enn å dele éin person i to, så sikt mot den låge sida av skalaen når mogleg.", "machine_learning_min_detection_score": "Minimum deteksjonsresultat", "machine_learning_min_detection_score_description": "Minimum tillitspoeng for at eit ansikt skal bli oppdaga, på ein skala frå 0 til 1. Lågare verdiar vil oppdage fleire ansikt, men kan føre til feilaktige treff.", "machine_learning_min_recognized_faces": "Minimum gjenkjende ansikt", @@ -137,16 +145,38 @@ "map_gps_settings_description": "Administrer innstillingar for kart og GPS (Reversert geokoding)", "map_light_style": "Lys modus", "map_settings": "Kart", + "map_settings_description": "Endre kartinnstillingar", + "map_style_description": "URL til eit style.json-karttema", "metadata_extraction_job": "Hent ut metadata", + "metadata_extraction_job_description": "Hent ut metadata frå kvart bilete, slik som GPS, ansikt og oppløysing", + "metadata_faces_import_setting": "Skru på import av ansikt", + "metadata_faces_import_setting_description": "Importer ansikt frå bilete sine EXIF-data og sidecar-filer", "metadata_settings": "Metadata Innstillinger", + "metadata_settings_description": "Endre metadata-innstillingar", "migration_job": "Migrasjon", "notification_email_from_address": "Frå adresse", + "notification_email_test_email_failed": "Mislukka sending av test-e-post, sjekk konfigurasjonen din", + "notification_email_test_email_sent": "Det vart sendt ei test-melding til {email}. Sjekk e-posten din.", + "notification_email_username_description": "Brukarnamn for autentisering på e-post-serveren", + "notification_enable_email_notifications": "Aktiver e-post-varslingar", "notification_settings": "Varselinnstillingar", + "notification_settings_description": "Endre varslingsinnstillingar, inkludert e-post", "oauth_auto_launch": "Autostart", + "oauth_auto_launch_description": "Start OAuth-innloggingsprosessen automatisk når innloggingssida vert opna", + "oauth_auto_register_description": "Registrer nye brukarar automatisk etter innlogging med OAuth", "oauth_button_text": "Tekst på knapp", + "oauth_client_secret_description": "Krevjast dersom PKCE (Proof Key for Code Exchange) ikkje støttast av OAuth-tilbydaren", + "oauth_enable_description": "Logg inn med OAuth", + "oauth_settings": "OAuth", + "oauth_settings_description": "Innstillingar for innlogging med OAuth", + "oauth_storage_quota_default": "Standard lagringskvote (GiB)", + "oauth_timeout": "Tidsavbrot på førespurnad", + "oauth_timeout_description": "Tidsavbrot for førespurnadar i millisekund", "password_enable_description": "Logg inn med e-post og passord", "password_settings": "Passordinnlogging", + "password_settings_description": "Innstillingar for innlogging med passord", "person_cleanup_job": "Personopprydding", + "quota_size_gib": "Lagringskvote (GiB)", "refreshing_all_libraries": "Laster alle bibliotek opp att", "registration": "Administrator registrering", "registration_description": "Sidan du er den første brukaren på systemet, vil du bli utnevnt til administrator og ha ansvar for administrative oppgåver. Du vil òg opprette eventuelle nye brukarar.", @@ -164,8 +194,17 @@ "server_settings_description": "Administrer serverinnstillingar", "server_welcome_message": "Velkomstmelding", "server_welcome_message_description": "Ei melding som synast på innloggingssida.", + "sidecar_job": "Sidecar-metadata", + "sidecar_job_description": "Oppdag eller synkroniser sidecar-metadata frå filsystemet", + "slideshow_duration_description": "Antal sekund å vise kvart bilete", + "storage_template_date_time_sample": "Døme på tid {date}", + "storage_template_enable_description": "Aktiver lagringsmal-motoren", + "storage_template_migration": "Overgang til ny lagringsmal", + "storage_template_migration_job": "Omorganisering etter ny lagringsmal", + "storage_template_settings": "Lagringsmal", "system_settings": "Systeminnstillingar", "template_email_preview": "Førehandsvisning", + "thumbnail_generation_job": "Generer miniatyrbilete", "transcoding_acceleration_nvenc": "NVENC (Krev NVIDIA GPU)", "transcoding_acceleration_qsv": "Quick Sync (Krev 7. generasjons Intel CPU eller nyare)", "transcoding_acceleration_rkmpp": "RKMPP (Berre på Rockchip SOCer)", @@ -180,7 +219,11 @@ "transcoding_audio_codec": "Lydkodek", "transcoding_audio_codec_description": "Opus er det valet med høgast lydkvalitet, men mindre kompabilitet med gamlare einingar og programvare.", "transcoding_bitrate_description": "Videoar med bitrate over høgste tillatte verdi, eller i eit format som ikkje er tillate", - "transcoding_codecs_learn_more": "For å lære meir om nytta begrep, sjå FFmpeg dokumentasjon for H.264 codec, HEVC codec and VP9 codec." + "transcoding_codecs_learn_more": "For å lære meir om nytta begrep, sjå FFmpeg dokumentasjon for H.264 codec, HEVC codec and VP9 codec.", + "transcoding_constant_rate_factor_description": "Videokvalitet. Vanlege verdiar er 23 for H.264, 28 for HEVC, 31 for VP9, og 35 for AV1. Lågare er betre, men gjev større filer.", + "transcoding_hardware_acceleration": "Maskinvare-akselerasjon", + "transcoding_max_bitrate": "Maksimal bitrate", + "transcoding_optimal_description": "Videoar med for høg oppløysing, eller ikkje i eit godkjend format" }, "admin_email": "Adminisrator E-post", "admin_password": "Administratorpassord", diff --git a/i18n/pl.json b/i18n/pl.json index e84c6f53e6..3c6a8b12e0 100644 --- a/i18n/pl.json +++ b/i18n/pl.json @@ -5,7 +5,7 @@ "acknowledge": "Zrozumiałem/łam", "action": "Akcja", "action_common_update": "Aktualizuj", - "actions": "Akcje/i", + "actions": "Akcje", "active": "Aktywne", "activity": "Aktywność", "activity_changed": "Aktywność jest {enabled, select, true {włączona} other {wyłączona}}", @@ -166,6 +166,20 @@ "metadata_settings_description": "Zarządzaj ustawieniami metadanych", "migration_job": "Migracja", "migration_job_description": "Przenieś miniatury zasobów i twarzy do najnowszej struktury folderów", + "nightly_tasks_cluster_faces_setting_description": "Uruchom rozpoznawanie twarzy dla nowo wykrytych twarzy", + "nightly_tasks_cluster_new_faces_setting": "Zgrupuj nowe twarze", + "nightly_tasks_database_cleanup_setting": "Zadania związane z czyszczeniem bazy danych", + "nightly_tasks_database_cleanup_setting_description": "Wyczyść stare, nieaktualne dane z bazy danych", + "nightly_tasks_generate_memories_setting": "Generuj wspomnienia", + "nightly_tasks_generate_memories_setting_description": "Stwórz nowe wspomnienia z zasobów", + "nightly_tasks_missing_thumbnails_setting": "Wygeneruj brakujące miniatury", + "nightly_tasks_missing_thumbnails_setting_description": "Dodaj zasoby bez miniatur do kolejki generowania miniatur", + "nightly_tasks_settings": "Ustawienia nocnych zadań", + "nightly_tasks_settings_description": "Zarządzaj zadaniami wykonywanymi w nocy", + "nightly_tasks_start_time_setting": "Czas rozpoczęcia", + "nightly_tasks_start_time_setting_description": "Czas, w którym serwer rozpoczyna wykonywanie nocnych zadań", + "nightly_tasks_sync_quota_usage_setting": "Zsynchronizuj wykorzystanie kontyngentu", + "nightly_tasks_sync_quota_usage_setting_description": "Zaktualizuj kontyngent przestrzeni dyskowej użytkownika na podstawie aktualnego zużycia", "no_paths_added": "Nie dodano ścieżki", "no_pattern_added": "Nie dodano wzoru", "note_apply_storage_label_previous_assets": "Uwaga: aby zastosować etykietę magazynu do wcześniej przesłanych zasobów, uruchom", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "Mobilny adres zwrotny", "oauth_mobile_redirect_uri_override": "Zapasowy URI przekierowania mobilnego", "oauth_mobile_redirect_uri_override_description": "Włącz, gdy dostawca OAuth nie pozwala na mobilne identyfikatory URI typu ''{callback}''", + "oauth_role_claim": "Oświadczenie roli", + "oauth_role_claim_description": "Automatycznie przyznaj dostęp administratora na podstawie obecności tego oświadczenia. Oświadczenie może mieć wartość „użytkownik” lub „administrator”.", "oauth_settings": "OAuth", "oauth_settings_description": "Zarządzaj ustawieniami logowania OAuth", "oauth_settings_more_details": "Więcej informacji o tej funkcji znajdziesz w dokumentacji.", @@ -357,10 +373,12 @@ "admin_password": "Hasło Administratora", "administration": "Administracja", "advanced": "Zaawansowane", + "advanced_settings_beta_timeline_subtitle": "Wypróbuj nową funkcjonalność aplikacji", + "advanced_settings_beta_timeline_title": "Beta-Timeline", "advanced_settings_enable_alternate_media_filter_subtitle": "Użyj tej opcji do filtrowania mediów podczas synchronizacji alternatywnych kryteriów. Używaj tylko wtedy gdy aplikacja ma problemy z wykrywaniem wszystkich albumów.", "advanced_settings_enable_alternate_media_filter_title": "[EKSPERYMENTALNE] Użyj alternatywnego filtra synchronizacji albumu", "advanced_settings_log_level_title": "Poziom szczegółowości dziennika: {level}", - "advanced_settings_prefer_remote_subtitle": "Niektóre urządzenia bardzo wolno ładują miniatury z zasobów na urządzeniu. Aktywuj to ustawienie, aby ładować zdalne obrazy.", + "advanced_settings_prefer_remote_subtitle": "Niektóre urządzenia bardzo wolno ładują miniatury z lokalnych zasobów. Aktywuj to ustawienie, aby ładować zdalne obrazy.", "advanced_settings_prefer_remote_title": "Preferuj obrazy zdalne", "advanced_settings_proxy_headers_subtitle": "Zdefiniuj nagłówki proxy, które Immich powinien wysyłać z każdym żądaniem sieciowym", "advanced_settings_proxy_headers_title": "Nagłówki proxy", @@ -379,6 +397,7 @@ "album_cover_updated": "Okładka albumu została zaktualizowana", "album_delete_confirmation": "Czy na pewno chcesz usunąć album {album}?", "album_delete_confirmation_description": "Jeżeli album jest udostępniany, inny stracą do niego dostęp.", + "album_deleted": "Album usunięty", "album_info_card_backup_album_excluded": "WYKLUCZONE", "album_info_card_backup_album_included": "WŁĄCZONE", "album_info_updated": "Szczegóły albumu zostały zaktualizowane", @@ -388,6 +407,7 @@ "album_options": "Opcje albumu", "album_remove_user": "Usunąć użytkownika?", "album_remove_user_confirmation": "Na pewno chcesz usunąć {user}?", + "album_search_not_found": "Nie znaleziono albumów pasujących do Twojego wyszukiwania", "album_share_no_users": "Wygląda na to, że ten album albo udostępniono wszystkim użytkownikom, albo nie ma komu go udostępnić.", "album_updated": "Album zaktualizowany", "album_updated_setting_description": "Otrzymaj powiadomienie e-mail, gdy do udostępnionego Ci albumu zostaną dodane nowe zasoby", @@ -407,6 +427,7 @@ "albums_default_sort_order": "Domyślna kolejność sortowania w albumach", "albums_default_sort_order_description": "Początkowa kolejność sortowania zasobów przy tworzeniu nowych albumów.", "albums_feature_description": "Kolekcje zasobów, które można udostępniać innym użytkownikom.", + "albums_on_device_count": "Albumów na urzadzeniu ({count})", "all": "Wszystkie", "all_albums": "Wszystkie albumy", "all_people": "Wszystkie osoby", @@ -427,6 +448,7 @@ "app_settings": "Ustawienia Aplikacji", "appears_in": "W albumach", "archive": "Archiwum", + "archive_action_prompt": "{count} dodanych do Archiwum", "archive_or_unarchive_photo": "Dodaj lub usuń zasób z archiwum", "archive_page_no_archived_assets": "Nie znaleziono zarchiwizowanych zasobów", "archive_page_title": "Archiwum {count}", @@ -464,7 +486,6 @@ "assets": "Zasoby", "assets_added_count": "Dodano {count, plural, one {# zasób} few {# zasoby} other {# zasobów}}", "assets_added_to_album_count": "Dodano {count, plural, one {# zasób} few {# zasoby} other {# zasobów}} do albumu", - "assets_added_to_name_count": "Dodano {count, plural, one {# zasób} few {# zasoby} other {# zasobów}} do {hasName, select, true {{name}} other {new album}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {sztuka Elementu} other {szt. Elementów}} nie może być dodana do albumu", "assets_count": "{count, plural, one {# zasób} few {# zasoby} other {# zasobów}}", "assets_deleted_permanently": "{count} zostało trwale usuniętych", @@ -490,6 +511,7 @@ "back_close_deselect": "Wróć, zamknij lub odznacz", "background_location_permission": "Uprawnienia do lokalizacji w tle", "background_location_permission_content": "Aby móc przełączać sieć podczas pracy w tle, Immich musi *zawsze* mieć dostęp do dokładnej lokalizacji, aby aplikacja mogła odczytać nazwę sieci Wi-Fi", + "backup": "Kopia Zapasowa", "backup_album_selection_page_albums_device": "Albumy na urządzeniu ({count})", "backup_album_selection_page_albums_tap": "Stuknij, aby włączyć, stuknij dwukrotnie, aby wykluczyć", "backup_album_selection_page_assets_scatter": "Pliki mogą być rozproszone w wielu albumach. Dzięki temu albumy mogą być włączane lub wyłączane podczas procesu tworzenia kopii zapasowej.", @@ -553,6 +575,8 @@ "backup_options_page_title": "Opcje kopi zapasowej", "backup_setting_subtitle": "Zarządzaj ustawieniami przesyłania w tle i na pierwszym planie", "backward": "Do tyłu", + "beta_sync": "Status synchronizacji w wersji Beta", + "beta_sync_subtitle": "Zarządzaj nowym systemem synchronizacji", "biometric_auth_enabled": "Włączono logowanie biometryczne", "biometric_locked_out": "Uwierzytelnianie biometryczne jest dla Ciebie zablokowane", "biometric_no_options": "Brak możliwości biometrii", @@ -570,7 +594,7 @@ "cache_settings_clear_cache_button": "Wyczyść Cache", "cache_settings_clear_cache_button_title": "Czyści pamięć podręczną aplikacji. Wpłynie to znacząco na wydajność aplikacji, dopóki pamięć podręczna nie zostanie odbudowana.", "cache_settings_duplicated_assets_clear_button": "WYCZYŚĆ", - "cache_settings_duplicated_assets_subtitle": "Zdjęcia i filmy umieszczone na czarnej liście aplikacji", + "cache_settings_duplicated_assets_subtitle": "Zdjęcia i filmy umieszczone na liście ignorowanych w aplikacji", "cache_settings_duplicated_assets_title": "Zduplikowane zasoby ({count})", "cache_settings_statistics_album": "Biblioteka miniatur", "cache_settings_statistics_full": "Pełne Zdjęcia", @@ -587,6 +611,7 @@ "cancel": "Anuluj", "cancel_search": "Anuluj wyszukiwanie", "canceled": "Anulowano", + "canceling": "Anulowanie", "cannot_merge_people": "Złączenie osób nie powiodło się", "cannot_undo_this_action": "Nie da się tego cofnąć!", "cannot_update_the_description": "Nie można zaktualizować opisu", @@ -700,10 +725,11 @@ "current_server_address": "Aktualny adres serwera", "custom_locale": "Niestandardowy Region", "custom_locale_description": "Formatuj daty i liczby na podstawie języka i regionu", + "custom_url": "Niestandardowy URL", "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Ciemny", - "darkTheme": "Włącz ciemny motyw", + "dark_theme": "Przełącz ciemny motyw", "date_after": "Data po", "date_and_time": "Data i godzina", "date_before": "Data przed", @@ -719,6 +745,8 @@ "default_locale": "Domyślny Region", "default_locale_description": "Formatuj daty i liczby na podstawie ustawień Twojej przeglądarki", "delete": "Usuń", + "delete_action_confirmation_message": "Jesteś pewien, że chcesz usunąć ten zasób? Ta czynność przeniesie zasób do kosza na serwerze i wyświetli komunikat z pytaniem, czy chcesz go usunąć lokalnie", + "delete_action_prompt": "{count} usuniętych", "delete_album": "Usuń album", "delete_api_key_prompt": "Czy na pewno chcesz usunąć ten klucz API?", "delete_dialog_alert": "Te elementy zostaną trwale usunięte z Immich i z Twojego urządzenia", @@ -732,9 +760,12 @@ "delete_key": "Usuń klucz", "delete_library": "Usuń bibliotekę", "delete_link": "Usuń link", + "delete_local_action_prompt": "{count} lokalnie usunięto", "delete_local_dialog_ok_backed_up_only": "Usuń tylko kopię zapasową", "delete_local_dialog_ok_force": "Usuń mimo to", "delete_others": "Usuń inne", + "delete_permanently": "Usuń trwale", + "delete_permanently_action_prompt": "{count} trwale usuniętych", "delete_shared_link": "Usuń udostępniony link", "delete_shared_link_dialog_title": "Usuń udostępniony link", "delete_tag": "Usuń etykietę", @@ -745,6 +776,7 @@ "description": "Opis", "description_input_hint_text": "Dodaj opis...", "description_input_submit_error": "Błąd aktualizacji opisu, sprawdź dziennik, aby uzyskać więcej szczegółów", + "deselect_all": "Odznacz wszystkie", "details": "Szczegóły", "direction": "Kierunek", "disabled": "Wyłączone", @@ -762,6 +794,7 @@ "documentation": "Dokumentacja", "done": "Gotowe", "download": "Pobierz", + "download_action_prompt": "Pobieranie {count} zasobów", "download_canceled": "Pobieranie anulowane", "download_complete": "Pobieranie zakończone", "download_enqueue": "Pobieranie w kolejce", @@ -799,6 +832,7 @@ "edit_key": "Edytuj klucz", "edit_link": "Edytuj link", "edit_location": "Edytuj lokalizację", + "edit_location_action_prompt": "{count} edytowana lokalizacja", "edit_location_dialog_title": "Lokalizacja", "edit_name": "Edytuj nazwę", "edit_people": "Edytuj osoby", @@ -817,6 +851,7 @@ "empty_trash": "Opróżnij kosz", "empty_trash_confirmation": "Czy na pewno chcesz opróżnić kosz? Spowoduje to trwałe usunięcie wszystkich zasobów znajdujących się w koszu z Immich.\nNie można cofnąć tej operacji!", "enable": "Włącz", + "enable_backup": "Włącz kopię zapasową", "enable_biometric_auth_description": "Wprowadź kod PIN aby włączyć logowanie biometryczne", "enabled": "Włączone", "end_date": "Do dnia", @@ -953,6 +988,7 @@ }, "exif": "Metadane EXIF", "exif_bottom_sheet_description": "Dodaj Opis...", + "exif_bottom_sheet_description_error": "Wystąpił błąd podczas aktualizacji opisu", "exif_bottom_sheet_details": "SZCZEGÓŁY", "exif_bottom_sheet_location": "LOKALIZACJA", "exif_bottom_sheet_people": "LUDZIE", @@ -973,6 +1009,8 @@ "explorer": "Eksplorator", "export": "Eksportuj", "export_as_json": "Eksportuj jako JSON", + "export_database": "Exportuj bazę danych", + "export_database_description": "Exportuj bazę danych SQLite", "extension": "Rozszerzenie", "external": "Zewnętrzny", "external_libraries": "Biblioteki Zewnętrzne", @@ -984,6 +1022,7 @@ "failed_to_load_assets": "Nie udało się załadować zasobów", "failed_to_load_folder": "Nie udało się załadować folderu", "favorite": "Ulubione", + "favorite_action_prompt": "{count} dodane do ulubionych", "favorite_or_unfavorite_photo": "Dodaj lub usuń z ulubionych", "favorites": "Ulubione", "favorites_page_no_favorites": "Nie znaleziono ulubionych zasobów", @@ -1023,6 +1062,9 @@ "haptic_feedback_switch": "Włącz technologię haptyczną", "haptic_feedback_title": "Technologia haptyczna", "has_quota": "Ma limit", + "hash_asset": "Hashuj zasób", + "hashed_assets": "Zahashowane zasoby", + "hashing": "Hashowanie", "header_settings_add_header_tip": "Dodaj nagłówek", "header_settings_field_validator_msg": "Wartość nie może być pusta", "header_settings_header_name_input": "Nazwa nagłówka", @@ -1055,6 +1097,7 @@ "host": "Host", "hour": "Godzina", "id": "ID", + "idle": "Bezczynny", "ignore_icloud_photos": "Ignoruj zdjęcia w iCloud", "ignore_icloud_photos_description": "Zdjęcia przechowywane w usłudze iCloud nie zostaną przesłane na serwer Immich", "image": "Zdjęcie", @@ -1112,6 +1155,7 @@ "language_no_results_title": "Nie znaleziono żadnych języków", "language_search_hint": "Szukaj języków...", "language_setting_description": "Wybierz swój preferowany język", + "large_files": "Duże pliki", "last_seen": "Ostatnio widziane", "latest_version": "Najnowsza Wersja", "latitude": "Szerokość geograficzna", @@ -1127,16 +1171,18 @@ "library_page_sort_created": "Ostatnio utworzone", "library_page_sort_last_modified": "Ostatnio zmodyfikowany", "library_page_sort_title": "Tytuł albumu", + "licenses": "Licencje", "light": "Jasny", "like_deleted": "Polubienie usunięte", "link_motion_video": "Podłącz ruchome wideo", - "link_options": "Opcje linku", "link_to_oauth": "Połącz z OAuth", "linked_oauth_account": "Połączone konto OAuth", "list": "Lista", "loading": "Ładowanie", "loading_search_results_failed": "Ładowanie wyników wyszukiwania nie powiodło się", + "local": "Lokalny", "local_asset_cast_failed": "Nie można strumieniować zasobu, który nie został przesłany na serwer", + "local_assets": "Zasoby lokalne", "local_network": "Sieć lokalna", "local_network_sheet_info": "Aplikacja połączy się z serwerem za pośrednictwem tego adresu URL podczas korzystania z określonej sieci Wi-Fi", "location_permission": "Zezwolenie na lokalizację", @@ -1193,8 +1239,7 @@ "manage_your_devices": "Zarządzaj swoimi zalogowanymi urządzeniami", "manage_your_oauth_connection": "Zarządzaj swoim połączeniem OAuth", "map": "Mapa", - "map_assets_in_bound": "{count} zdjęcie", - "map_assets_in_bounds": "{count} zdjęć", + "map_assets_in_bounds": "{count, plural, one {# zdjęcie} few {# zdjęcia} other {# zdjęć}}", "map_cannot_get_user_location": "Nie można uzyskać lokalizacji użytkownika", "map_location_dialog_yes": "Tak", "map_location_picker_page_use_location": "Użyj tej lokalizacji", @@ -1246,6 +1291,7 @@ "more": "Więcej", "move": "Przenieś", "move_off_locked_folder": "Przenieś z folderu zablokowanego", + "move_to_lock_folder_action_prompt": "{count} dodanych do folderu zablokowanego", "move_to_locked_folder": "Przenieś do folderu zablokowanego", "move_to_locked_folder_confirmation": "Te zdjęcia i filmy zostaną usunięte ze wszystkich albumów i będą widzialne tylko w folderze zablokowanym", "moved_to_archive": "Przeniesiono {count, plural, one {# zasób} few {# zasoby} other {# zasobów}} do archiwum", @@ -1292,6 +1338,7 @@ "no_results": "Brak wyników", "no_results_description": "Spróbuj użyć synonimu lub bardziej ogólnego słowa kluczowego", "no_shared_albums_message": "Stwórz album aby udostępnić zdjęcia i filmy osobom w Twojej sieci", + "no_uploads_in_progress": "Brak przesyłań w toku", "not_in_any_album": "Bez albumu", "not_selected": "Nie wybrano", "note_apply_storage_label_to_previously_uploaded assets": "Uwaga: Aby przypisać etykietę magazynowania do wcześniej przesłanych zasobów, uruchom", @@ -1329,6 +1376,7 @@ "original": "oryginalny", "other": "Inne", "other_devices": "Inne urządzenia", + "other_entities": "Inne byty", "other_variables": "Inne zmienne", "owned": "Posiadany", "owner": "Właściciel", @@ -1460,6 +1508,7 @@ "purchase_server_description_2": "Status wspierającego", "purchase_server_title": "Serwer", "purchase_settings_server_activated": "Klucz produktu serwera jest zarządzany przez administratora", + "queue_status": "Kolejkowanie {count}/{total}", "rating": "Ocena gwiazdkowa", "rating_clear": "Wyczyść ocenę", "rating_count": "{count, plural, one {# gwiazdka} other {# gwiazdek}}", @@ -1488,6 +1537,8 @@ "refreshing_faces": "Odświeżanie twarzy", "refreshing_metadata": "Odświeżanie metadanych", "regenerating_thumbnails": "Regenerowanie miniatur", + "remote": "Zdalny", + "remote_assets": "Zasoby zdalne", "remove": "Usuń", "remove_assets_album_confirmation": "Czy na pewno chcesz usunąć {count, plural, one {# zasób} other {# zasoby}} z albumu?", "remove_assets_shared_link_confirmation": "Czy na pewno chcesz usunąć {count, plural, one {# zasób} other {# zasoby}} z tego udostępnionego linku?", @@ -1495,7 +1546,9 @@ "remove_custom_date_range": "Usuń niestandardowy zakres dat", "remove_deleted_assets": "Usuń Niedostępne Pliki", "remove_from_album": "Usuń z albumu", + "remove_from_album_action_prompt": "{count} usunięto z albumu", "remove_from_favorites": "Usuń z ulubionych", + "remove_from_lock_folder_action_prompt": "{count} usunięte z folderu zablokowanego", "remove_from_locked_folder": "Usuń z folderu zablokowanego", "remove_from_locked_folder_confirmation": "Czy na pewno chcesz przenieść te zdjęcia i filmy z folderu zablokowanego? Będą one widoczne w bibliotece.", "remove_from_shared_link": "Usuń z udostępnionego linku", @@ -1523,19 +1576,25 @@ "reset_password": "Resetuj hasło", "reset_people_visibility": "Zresetuj widoczność osób", "reset_pin_code": "Zresetuj kod PIN", + "reset_sqlite": "Zresetuj bazę danych SQLite", + "reset_sqlite_confirmation": "Czy na pewno chcesz zresetować bazę danych SQLite? Wymagane będzie wylogowanie oraz ponowne zalogowanie, aby zsynchronizować dane", + "reset_sqlite_success": "Pomyślnie zresetowano bazę danych SQLite", "reset_to_default": "Przywróć ustawienia domyślne", "resolve_duplicates": "Rozwiąż problemy z duplikatami", "resolved_all_duplicates": "Rozwiązano wszystkie duplikaty", "restore": "Przywrócić", "restore_all": "Przywróć wszystko", + "restore_trash_action_prompt": "{count} przywrócono z kosza", "restore_user": "Przywróć użytkownika", "restored_asset": "Przywrócony zasób", "resume": "Wznów", "retry_upload": "Prześlij ponownie", "review_duplicates": "Przejrzyj duplikaty", + "review_large_files": "Przejrzyj duże pliki", "role": "Rola", "role_editor": "Edytor", "role_viewer": "Widz", + "running": "W trakcie", "save": "Zapisz", "save_to_gallery": "Zapisz w galerii", "saved_api_key": "Zapisany klucz API", @@ -1667,6 +1726,7 @@ "settings_saved": "Ustawienia zapisane", "setup_pin_code": "Ustaw kod PIN", "share": "Udostępnij", + "share_action_prompt": "Udostępniono {count} zasobów", "share_add_photos": "Dodaj zdjęcia", "share_assets_selected": "Wybrano {count}", "share_dialog_preparing": "Przygotowywanie…", @@ -1688,6 +1748,7 @@ "shared_link_clipboard_copied_massage": "Skopiowane do schowka", "shared_link_clipboard_text": "Link: {link}\nHasło: {password}", "shared_link_create_error": "Błąd podczas tworzenia linka do udostępnienia", + "shared_link_custom_url_description": "Otwórz udostępniony link z niestandardowym adresem URL", "shared_link_edit_description_hint": "Wprowadź opis udostępnienia", "shared_link_edit_expire_after_option_day": "1 dniu", "shared_link_edit_expire_after_option_days": "{count} dniach", @@ -1713,6 +1774,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Zarządzaj udostępnionymi linkami", "shared_link_options": "Opcje udostępniania linku", + "shared_link_password_description": "Wymagaj hasła dostępu dla udostępnionego linku", "shared_links": "Udostępnione linki", "shared_links_description": "Udostępnij zdjęcia oraz filmy przez link", "shared_photos_and_videos_count": "{assetCount, plural, one {# udostępnione zdjęcie lub film.} other {# udostępnione zdjęcia i filmy.}}", @@ -1768,6 +1830,7 @@ "sort_title": "Tytuł", "source": "Źródło", "stack": "Stos", + "stack_action_prompt": "{count} zgrupowano", "stack_duplicates": "Stos duplikatów", "stack_select_one_photo": "Wybierz jedno główne zdjęcie do stosu", "stack_selected_photos": "Układaj wybrane zdjęcia", @@ -1787,6 +1850,7 @@ "storage_quota": "Limit pamięci", "storage_usage": "{used} z {available} użyte", "submit": "Zatwierdź", + "success": "Sukces", "suggestions": "Sugestie", "sunrise_on_the_beach": "Wschód słońca na plaży", "support": "Wsparcie", @@ -1796,6 +1860,8 @@ "sync": "Synchronizuj", "sync_albums": "Synchronizuj albumy", "sync_albums_manual_subtitle": "Zsynchronizuj wszystkie przesłane filmy i zdjęcia z wybranymi albumami kopii zapasowych", + "sync_local": "Synchronizacja lokalna", + "sync_remote": "Synchronizacja zdalna", "sync_upload_album_setting_subtitle": "Twórz i przesyłaj swoje zdjęcia i filmy do wybranych albumów w Immich", "tag": "Etykieta", "tag_assets": "Ustaw etykiety zasobów", @@ -1806,6 +1872,7 @@ "tag_updated": "Uaktualniono etykietę: {tag}", "tagged_assets": "Przypisano etykietę {count, plural, one {# zasobowi} other {# zasobom}}", "tags": "Etykiety", + "tap_to_run_job": "Uruchom zadanie", "template": "Szablon", "theme": "Motyw", "theme_selection": "Wybór motywu", @@ -1838,6 +1905,7 @@ "total": "Całkowity", "total_usage": "Całkowite wykorzystanie", "trash": "Kosz", + "trash_action_prompt": "{count} przeniesione do kosza", "trash_all": "Usuń wszystkie", "trash_count": "Kosz {count, number}", "trash_delete_asset": "Kosz/Usuń zasób", @@ -1855,9 +1923,11 @@ "unable_to_change_pin_code": "Nie można zmienić kodu PIN", "unable_to_setup_pin_code": "Nie można ustawić kodu PIN", "unarchive": "Cofnij archiwizację", + "unarchive_action_prompt": "{count} usunięto z archiwum", "unarchived_count": "{count, plural, one {# cofnięta archiwizacja} few {# cofnięte archiwizacje} other {# cofniętych archiwizacji}}", "undo": "Cofnij", "unfavorite": "Usuń z ulubionych", + "unfavorite_action_prompt": "{count} usunięto z ulubionych", "unhide_person": "Przywróć osobę", "unknown": "Nieznany", "unknown_country": "Nieznane państwo", @@ -1875,15 +1945,20 @@ "unselect_all_duplicates": "Odznacz wszystkie duplikaty", "unselect_all_in": "Odznacz wszystkie w {group}", "unstack": "Rozłóż stos", + "unstack_action_prompt": "{count} odgrupowano", "unstacked_assets_count": "{count, plural, one {Rozłożony # zasób} few {Rozłożone # zasoby} other {Rozłożonych # zasobów}}", + "untagged": "Nieoznaczone", "up_next": "Do następnego", "updated_at": "Zaktualizowany", "updated_password": "Pomyślnie zaktualizowano hasło", "upload": "Prześlij", + "upload_action_prompt": "{count} w kolejce do wysłania", "upload_concurrency": "Współbieżność wysyłania", + "upload_details": "Szczegóły przesyłania", "upload_dialog_info": "Czy chcesz wykonać kopię zapasową wybranych zasobów na serwerze?", "upload_dialog_title": "Prześlij Zasób", "upload_errors": "Przesyłanie zakończone z {count, plural, one {# błędem} other {# błędami}}. Odśwież stronę, aby zobaczyć nowo przesłane zasoby.", + "upload_finished": "Przesyłanie zakończone", "upload_progress": "Pozostałe {remaining, number} - Przetworzone {processed, number}/{total, number}", "upload_skipped_duplicates": "Pominięto {count, plural, one {# zduplikowany zasób} few {# zduplikowane zasoby} other {# zduplikowanych zasobów}}", "upload_status_duplicates": "Duplikaty", @@ -1892,6 +1967,7 @@ "upload_success": "Przesyłanie powiodło się, odśwież stronę, aby zobaczyć nowo przesłane zasoby.", "upload_to_immich": "Prześlij do Immich ({count})", "uploading": "Przesyłanie", + "uploading_media": "Przesyłanie multimediów", "url": "URL", "usage": "Użycie", "use_biometric": "Użyj biometrii", @@ -1912,6 +1988,7 @@ "user_usage_stats_description": "Wyświetl statystyki użytkowania konta", "username": "Nazwa użytkownika", "users": "Użytkownicy", + "users_added_to_album_count": "Dodano {count, plural, one {# użytkownika} other {# użytkowników}} do albumu", "utilities": "Narzędzia", "validate": "Walidacja", "validate_endpoint_error": "Proszę wprowadzić prawidłowy adres URL", @@ -1930,6 +2007,7 @@ "view_album": "Wyświetl Album", "view_all": "Pokaż wszystkie", "view_all_users": "Pokaż wszystkich użytkowników", + "view_details": "Zobacz szczegóły", "view_in_timeline": "Pokaż na osi czasu", "view_link": "Zobacz link", "view_links": "Pokaż łącza", diff --git a/i18n/pt.json b/i18n/pt.json index bb88cafbfa..efe4060730 100644 --- a/i18n/pt.json +++ b/i18n/pt.json @@ -166,6 +166,20 @@ "metadata_settings_description": "Gerir definições de metadados", "migration_job": "Migração", "migration_job_description": "Migra miniaturas de ficheiros e rostos para a estrutura de pastas mais recente", + "nightly_tasks_cluster_faces_setting_description": "Executar reconhecimento facial em faces detetadas recentemente", + "nightly_tasks_cluster_new_faces_setting": "Agrupar novas faces", + "nightly_tasks_database_cleanup_setting": "Tarefas de limpeza da base de dados", + "nightly_tasks_database_cleanup_setting_description": "Limpar dados antigos e expirados da base de dados", + "nightly_tasks_generate_memories_setting": "Gerar memórias", + "nightly_tasks_generate_memories_setting_description": "Criar novas memórias a partir de ficheiros", + "nightly_tasks_missing_thumbnails_setting": "Gerar miniaturas em falta", + "nightly_tasks_missing_thumbnails_setting_description": "Colocar em fila ficheiros sem miniaturas para a geração das mesmas", + "nightly_tasks_settings": "Definições de Tarefas Diárias", + "nightly_tasks_settings_description": "Gerir tarefas diárias", + "nightly_tasks_start_time_setting": "Hora de início", + "nightly_tasks_start_time_setting_description": "A hora em qual o servidor começa a executar as tarefas diárias", + "nightly_tasks_sync_quota_usage_setting": "Utilização da quota de sincronização", + "nightly_tasks_sync_quota_usage_setting_description": "Atualizar quotas de armazenamento de utilizadores, com base na utilização atual", "no_paths_added": "Nenhum caminho adicionado", "no_pattern_added": "Nenhum padrão adicionado", "note_apply_storage_label_previous_assets": "Observação: Para aplicar o Rótulo de Armazenamento a ficheiros carregados anteriormente, execute o", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "URI de redirecionamento móvel", "oauth_mobile_redirect_uri_override": "Substituição de URI de redirecionamento móvel", "oauth_mobile_redirect_uri_override_description": "Ative quando o provedor do OAuth não permite um URI móvel, como ''{callback}''", + "oauth_role_claim": "Reivindicação de Funções", + "oauth_role_claim_description": "Automaticamente concede acesso de administrador, com base na presença desta reivindicação. A reivindicação tanto pode ter \"user\" como \"admin\".", "oauth_settings": "OAuth", "oauth_settings_description": "Gerir definições de inicio de sessão do OAuth", "oauth_settings_more_details": "Para mais informações sobre esta funcionalidade, veja a documentação.", @@ -357,10 +373,12 @@ "admin_password": "Palavra-passe do administrador", "administration": "Administração", "advanced": "Avançado", + "advanced_settings_beta_timeline_subtitle": "Experimente as novas funcionalidades da aplicação", + "advanced_settings_beta_timeline_title": "Linha temporal da versão Beta", "advanced_settings_enable_alternate_media_filter_subtitle": "Utilize esta definição para filtrar ficheiros durante a sincronização baseada em critérios alternativos. Utilize apenas se a aplicação estiver com problemas a detetar todos os álbuns.", "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTAL] Utilizar um filtro alternativo de sincronização de álbuns em dispositivos", "advanced_settings_log_level_title": "Nível de registo: {level}", - "advanced_settings_prefer_remote_subtitle": "Alguns dispositivos são extremamente lentos para carregar miniaturas da memória. Ative esta opção para preferir imagens do servidor.", + "advanced_settings_prefer_remote_subtitle": "Alguns dispositivos são extremamente lentos a carregar miniaturas da memória interna. Ative esta opção para preferir imagens do servidor.", "advanced_settings_prefer_remote_title": "Preferir imagens do servidor", "advanced_settings_proxy_headers_subtitle": "Defina os cabeçalhos do proxy que o Immich deve enviar em todas comunicações com a rede", "advanced_settings_proxy_headers_title": "Cabeçalhos do Proxy", @@ -379,6 +397,7 @@ "album_cover_updated": "Capa do álbum atualizada", "album_delete_confirmation": "Tem a certeza de que quer eliminar o álbum {album}?", "album_delete_confirmation_description": "Se este álbum for partilhado, os outros utilizadores deixam de o poder aceder.", + "album_deleted": "Álbum eliminado", "album_info_card_backup_album_excluded": "EXCLUÍDO", "album_info_card_backup_album_included": "INCLUÍDO", "album_info_updated": "Informações do álbum atualizadas", @@ -388,6 +407,7 @@ "album_options": "Opções de álbum", "album_remove_user": "Remover utilizador?", "album_remove_user_confirmation": "Tem a certeza de que quer remover {user}?", + "album_search_not_found": "Nenhum álbum encontrado segundo a pesquisa", "album_share_no_users": "Parece que tem este álbum partilhado com todos os utilizadores ou que não existem utilizadores com quem o partilhar.", "album_updated": "Álbum atualizado", "album_updated_setting_description": "Receber uma notificação por e-mail quando um álbum partilhado tiver novos ficheiros", @@ -407,6 +427,7 @@ "albums_default_sort_order": "Ordem padrão de organização do álbum", "albums_default_sort_order_description": "Ordem inicial dos ficheiros ao criar novos álbuns.", "albums_feature_description": "Coleções de ficheiros que podem ser partilhados com outros utilizadores.", + "albums_on_device_count": "Álbums no dispositivo ({count})", "all": "Todos", "all_albums": "Todos os álbuns", "all_people": "Todas as pessoas", @@ -427,6 +448,7 @@ "app_settings": "Definições da Aplicação", "appears_in": "Aparece em", "archive": "Arquivo", + "archive_action_prompt": "{count} adicionados ao Arquivo", "archive_or_unarchive_photo": "Arquivar ou desarquivar foto", "archive_page_no_archived_assets": "Nenhum arquivo encontrado", "archive_page_title": "Arquivo ({count})", @@ -464,7 +486,6 @@ "assets": "Ficheiros", "assets_added_count": "{count, plural, one {# ficheiro adicionado} other {# ficheiros adicionados}}", "assets_added_to_album_count": "{count, plural, one {# ficheiro adicionado} other {# ficheiros adicionados}} ao álbum", - "assets_added_to_name_count": "{count, plural, one {# ficheiro adicionado} other {# ficheiros adicionados}} a {hasName, select, true {{name}} other {novo álbum}}", "assets_cannot_be_added_to_album_count": "Não foi possível adicionar {count, plural, one {ficheiro} other {ficheiros}} ao álbum", "assets_count": "{count, plural, one {# ficheiro} other {# ficheiros}}", "assets_deleted_permanently": "{count} ficheiro(s) eliminado(s) permanentemente", @@ -490,6 +511,7 @@ "back_close_deselect": "Voltar, fechar ou desmarcar", "background_location_permission": "Permissão de localização em segundo plano", "background_location_permission_content": "Para que seja possível trocar a URL quando estiver executando em segundo plano, o Immich deve *sempre* ter a permissão de localização precisa para que o aplicativo consiga ler o nome da rede Wi-Fi", + "backup": "Cópia de segurança", "backup_album_selection_page_albums_device": "Álbuns no dispositivo ({count})", "backup_album_selection_page_albums_tap": "Toque para incluir, duplo toque para excluir", "backup_album_selection_page_assets_scatter": "Os arquivos podem estar espalhados em vários álbuns. Assim, os álbuns podem ser incluídos ou excluídos durante o processo de backup.", @@ -553,6 +575,8 @@ "backup_options_page_title": "Opções de backup", "backup_setting_subtitle": "Gerenciar as configurações de envio em primeiro e segundo plano", "backward": "Para trás", + "beta_sync": "Estado de Sincronização Beta", + "beta_sync_subtitle": "Gerir o novo sistema de sincronização", "biometric_auth_enabled": "Autenticação biométrica ativada", "biometric_locked_out": "Está impedido de utilizar a autenticação biométrica", "biometric_no_options": "Sem opções biométricas disponíveis", @@ -570,7 +594,7 @@ "cache_settings_clear_cache_button": "Limpar cache", "cache_settings_clear_cache_button_title": "Limpa o cache do aplicativo. Isso afetará significativamente o desempenho do aplicativo até que o cache seja reconstruído.", "cache_settings_duplicated_assets_clear_button": "LIMPAR", - "cache_settings_duplicated_assets_subtitle": "Fotos e vídeos que estão na lista negra da aplicação", + "cache_settings_duplicated_assets_subtitle": "Fotos e vídeos que estão na lista de bloqueio da aplicação", "cache_settings_duplicated_assets_title": "Ficheiros duplicados ({count})", "cache_settings_statistics_album": "Miniaturas da biblioteca", "cache_settings_statistics_full": "Imagens completas", @@ -587,6 +611,7 @@ "cancel": "Cancelar", "cancel_search": "Cancelar pesquisa", "canceled": "Cancelado", + "canceling": "A cancelar", "cannot_merge_people": "Não foi possível unir pessoas", "cannot_undo_this_action": "Não é possível anular esta ação!", "cannot_update_the_description": "Não foi possível atualizar a descrição", @@ -703,7 +728,7 @@ "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Escuro", - "darkTheme": "Alternar tema escuro", + "dark_theme": "Alternar tema escuro", "date_after": "Data após", "date_and_time": "Data e Hora", "date_before": "Data antes", @@ -719,22 +744,27 @@ "default_locale": "Localização Padrão", "default_locale_description": "Formatar datas e números baseados na linguagem do seu navegador", "delete": "Eliminar", - "delete_album": "Eliminar álbum", - "delete_api_key_prompt": "Tem a certeza de que deseja eliminar esta chave de API?", + "delete_action_confirmation_message": "Tem a certeza de que quer eliminar este ficheiro? Está ação irá mover o ficheiro para a reciclagem do servidor e perguntar se quer apagá-lo localmente", + "delete_action_prompt": "{count} eliminados", + "delete_album": "Apagar álbum", + "delete_api_key_prompt": "Tem a certeza de que deseja remover esta chave de API?", "delete_dialog_alert": "Esses arquivos serão permanentemente apagados do Immich e de seu dispositivo", "delete_dialog_alert_local": "Estes arquivos serão permanentemente excluídos do seu dispositivo, mas continuarão disponíveis no servidor Immich", "delete_dialog_alert_local_non_backed_up": "Não há backup de alguns dos arquivos no servidor e eles serão excluídos permanentemente do seu dispositivo", "delete_dialog_alert_remote": "Estes arquivos serão permanentemente excluídos do servidor Immich", - "delete_dialog_ok_force": "Excluir mesmo assim", + "delete_dialog_ok_force": "Confirmo que quero excluir", "delete_dialog_title": "Excluir Permanentemente", "delete_duplicates_confirmation": "Tem a certeza de que deseja eliminar permanentemente estes itens duplicados?", "delete_face": "Remover rosto", - "delete_key": "Eliminar chave", + "delete_key": "Apagar chave", "delete_library": "Eliminar Biblioteca", "delete_link": "Eliminar link", + "delete_local_action_prompt": "{count} eliminados localmente", "delete_local_dialog_ok_backed_up_only": "Excluir apenas arquivos com backup", "delete_local_dialog_ok_force": "Excluir mesmo assim", "delete_others": "Excluir outros", + "delete_permanently": "Eliminar permanentemente", + "delete_permanently_action_prompt": "{count} eliminados permanentemente", "delete_shared_link": "Eliminar link de partilha", "delete_shared_link_dialog_title": "Excluir link compartilhado", "delete_tag": "Eliminar etiqueta", @@ -745,6 +775,7 @@ "description": "Descrição", "description_input_hint_text": "Adicionar descrição...", "description_input_submit_error": "Erro ao atualizar a descrição, verifique o registo para obter mais detalhes", + "deselect_all": "Remover seleção de tudo", "details": "Detalhes", "direction": "Direção", "disabled": "Desativado", @@ -762,6 +793,7 @@ "documentation": "Documentação", "done": "Feito", "download": "Transferir", + "download_action_prompt": "A descarregar {count} ficheiros", "download_canceled": "Cancelado", "download_complete": "Sucesso", "download_enqueue": "Na fila", @@ -799,6 +831,7 @@ "edit_key": "Editar chave", "edit_link": "Editar link", "edit_location": "Editar Localização", + "edit_location_action_prompt": "{count} locais alterados", "edit_location_dialog_title": "Localização", "edit_name": "Editar nome", "edit_people": "Editar pessoas", @@ -817,6 +850,7 @@ "empty_trash": "Esvaziar reciclagem", "empty_trash_confirmation": "Tem a certeza de que deseja esvaziar a reciclagem? Isto removerá todos os ficheiros da reciclagem do Immich permanentemente.\nNão é possível anular esta ação!", "enable": "Ativar", + "enable_backup": "Ativar Cópia de Segurança", "enable_biometric_auth_description": "Insira o código PIN para ativar a autenticação biométrica", "enabled": "Ativado", "end_date": "Data final", @@ -973,6 +1007,8 @@ "explorer": "Explorador", "export": "Exportar", "export_as_json": "Exportar como JSON", + "export_database": "Exportar Base de Dados", + "export_database_description": "Exportar a Base de Dados SQLite", "extension": "Extensão", "external": "Externo", "external_libraries": "Bibliotecas externas", @@ -984,6 +1020,7 @@ "failed_to_load_assets": "Ocorreu um erro ao carregar ficheiros", "failed_to_load_folder": "Ocorreu um erro ao carregar a pasta", "favorite": "Favorito", + "favorite_action_prompt": "{count} adicionados aos favoritos", "favorite_or_unfavorite_photo": "Marcar ou desmarcar a foto como favorita", "favorites": "Favoritos", "favorites_page_no_favorites": "Nenhum favorito encontrado", @@ -1023,6 +1060,9 @@ "haptic_feedback_switch": "Habilitar vibração", "haptic_feedback_title": "Vibração", "has_quota": "Tem quota", + "hash_asset": "Criptografar ficheiro", + "hashed_assets": "Ficheiros criptografados", + "hashing": "A criptografar", "header_settings_add_header_tip": "Adicionar cabeçalho", "header_settings_field_validator_msg": "Campo deve ser preenchido", "header_settings_header_name_input": "Nome do cabeçalho", @@ -1055,6 +1095,7 @@ "host": "Servidor", "hour": "Hora", "id": "ID", + "idle": "Em espera", "ignore_icloud_photos": "ignorar fotos no iCloud", "ignore_icloud_photos_description": "Fotos que estão armazenadas no iCloud não serão carregadas para o servidor do Immich", "image": "Imagem", @@ -1127,16 +1168,18 @@ "library_page_sort_created": "Data de criação", "library_page_sort_last_modified": "Última modificação", "library_page_sort_title": "Título do álbum", + "licenses": "Licenças", "light": "Claro", "like_deleted": "Gosto removido", "link_motion_video": "Relacionar video animado", - "link_options": "Opções do Link", "link_to_oauth": "Link do OAuth", "linked_oauth_account": "Conta OAuth Associada", "list": "Lista", "loading": "A Carregar", "loading_search_results_failed": "Ocorreu um erro ao carregar os resultados da pesquisa", + "local": "Local", "local_asset_cast_failed": "Não é possível transmitir um ficheiro que não tenha sido enviado antes para o servidor", + "local_assets": "Ficheiros Locais", "local_network": "Rede local", "local_network_sheet_info": "O aplicativo irá se conectar ao servidor através desta URL quando estiver na rede Wi-Fi especificada", "location_permission": "Permissão de localização", @@ -1193,8 +1236,7 @@ "manage_your_devices": "Gerir os seus dispositivos com sessão iniciada", "manage_your_oauth_connection": "Gerir a sua ligação ao OAuth", "map": "Mapa", - "map_assets_in_bound": "{count} foto", - "map_assets_in_bounds": "{count} fotos", + "map_assets_in_bounds": "{count, plural, one {# foto} other {# fotos}}", "map_cannot_get_user_location": "Impossível obter a sua localização", "map_location_dialog_yes": "Sim", "map_location_picker_page_use_location": "Utilizar esta localização", @@ -1246,6 +1288,7 @@ "more": "Mais", "move": "Mover", "move_off_locked_folder": "Mover para fora da pasta trancada", + "move_to_lock_folder_action_prompt": "{count} adicionados à pasta trancada", "move_to_locked_folder": "Mover para a pasta trancada", "move_to_locked_folder_confirmation": "Estas fotos e vídeos serão removidas de todos os álbuns, e só serão visíveis na pasta trancada", "moved_to_archive": "{count, plural, one {Foi movido # ficheiro} other {Foram movidos # ficheiros}} para o arquivo", @@ -1292,6 +1335,7 @@ "no_results": "Sem resultados", "no_results_description": "Tente um sinónimo ou uma palavra-chave mais comum", "no_shared_albums_message": "Crie um álbum para partilhar fotos e vídeos com pessoas na sua rede", + "no_uploads_in_progress": "Nenhum carregamento em curso", "not_in_any_album": "Não está em nenhum álbum", "not_selected": "Não selecionado", "note_apply_storage_label_to_previously_uploaded assets": "Nota: Para aplicar o Rótulo de Armazenamento a ficheiros carregados anteriormente, execute o", @@ -1329,6 +1373,7 @@ "original": "original", "other": "Outro", "other_devices": "Outros dispositivos", + "other_entities": "Outras entidades", "other_variables": "Outras variáveis", "owned": "Seu", "owner": "Dono", @@ -1460,6 +1505,7 @@ "purchase_server_description_2": "Status de apoiante", "purchase_server_title": "Servidor", "purchase_settings_server_activated": "A chave de produto do servidor é gerida pelo administrador", + "queue_status": "Em fila {count}/{total}", "rating": "Classificação por estrelas", "rating_clear": "Limpar classificação", "rating_count": "{count, plural, one {# estrela} other {# estrelas}}", @@ -1488,6 +1534,8 @@ "refreshing_faces": "A atualizar rostos", "refreshing_metadata": "A atualizar metadados", "regenerating_thumbnails": "A atualizar miniaturas", + "remote": "Remoto", + "remote_assets": "Ficheiros Remotos", "remove": "Remover", "remove_assets_album_confirmation": "Tem a certeza de que deseja remover {count, plural, one {# ficheiro} other {# ficheiros}} do álbum?", "remove_assets_shared_link_confirmation": "Tem certeza de que deseja remover {count, plural, one {# ficheiro} other {# ficheiros}} deste link partilhado?", @@ -1495,7 +1543,9 @@ "remove_custom_date_range": "Remover intervalo de datas personalizado", "remove_deleted_assets": "Remover ficheiros indisponíveis", "remove_from_album": "Remover do álbum", + "remove_from_album_action_prompt": "{count} removido(s) do álbum", "remove_from_favorites": "Remover dos favoritos", + "remove_from_lock_folder_action_prompt": "{count} removidos da pasta trancada", "remove_from_locked_folder": "Remover da pasta trancada", "remove_from_locked_folder_confirmation": "Tem a certeza de que quer mover estas fotos e vídeos para fora da pasta trancada? Passarão a ser visíveis na biblioteca.", "remove_from_shared_link": "Remover do link partilhado", @@ -1523,11 +1573,15 @@ "reset_password": "Redefinir palavra-passe", "reset_people_visibility": "Redefinir pessoas ocultas", "reset_pin_code": "Repor código PIN", + "reset_sqlite": "Reiniciar Base de Dados SQLite", + "reset_sqlite_confirmation": "Tem a certeza de que quer reiniciar a base de dados SQLite? Vai ter de terminar a sessão e entrar outra vez para sincronizar os dados de novo", + "reset_sqlite_success": "Base de dados SQLite reiniciada com sucesso", "reset_to_default": "Repor predefinições", "resolve_duplicates": "Resolver itens duplicados", "resolved_all_duplicates": "Todos os itens duplicados resolvidos", "restore": "Restaurar", "restore_all": "Restaurar tudo", + "restore_trash_action_prompt": "{count} restaurados da reciclagem", "restore_user": "Restaurar utilizador", "restored_asset": "Ficheiro restaurado", "resume": "Continuar", @@ -1536,6 +1590,7 @@ "role": "Função", "role_editor": "Editor", "role_viewer": "Visualizador", + "running": "A executar", "save": "Guardar", "save_to_gallery": "Salvar na galeria", "saved_api_key": "Chave de API guardada", @@ -1667,6 +1722,7 @@ "settings_saved": "Definições guardadas", "setup_pin_code": "Configurar um código PIN", "share": "Partilhar", + "share_action_prompt": "Partilhados {count} ficheiros", "share_add_photos": "Adicionar fotos", "share_assets_selected": "{count} selecionados", "share_dialog_preparing": "Preparando...", @@ -1768,6 +1824,7 @@ "sort_title": "Título", "source": "Fonte", "stack": "Empilhar", + "stack_action_prompt": "{count} empilhados", "stack_duplicates": "Empilhar itens duplicados", "stack_select_one_photo": "Selecione uma foto principal para a pilha", "stack_selected_photos": "Empilhar fotos selecionadas", @@ -1787,6 +1844,7 @@ "storage_quota": "Quota de armazenamento", "storage_usage": "Utilizado {used} de {available}", "submit": "Enviar", + "success": "Sucesso", "suggestions": "Sugestões", "sunrise_on_the_beach": "Nascer do sol na praia", "support": "Apoio", @@ -1796,6 +1854,8 @@ "sync": "Sincronizar", "sync_albums": "Sincronizar álbuns", "sync_albums_manual_subtitle": "Sincronizar todas as fotos e vídeos enviados para o álbum de backup selecionado", + "sync_local": "Sincronização Local", + "sync_remote": "Sincronização Remota", "sync_upload_album_setting_subtitle": "Crie e envie suas fotos e vídeos para o álbum selecionado no Immich", "tag": "Etiqueta", "tag_assets": "Etiquetar ficheiros", @@ -1806,6 +1866,7 @@ "tag_updated": "Atualizada a etiqueta: {tag}", "tagged_assets": "Etiquetado {count, plural, one {# ficheiros} other {# ficheiros}}", "tags": "Etiquetas", + "tap_to_run_job": "Tocar para executar tarefa", "template": "Modelo", "theme": "Tema", "theme_selection": "Selecionar tema", @@ -1838,6 +1899,7 @@ "total": "Total", "total_usage": "Total utilizado", "trash": "Reciclagem", + "trash_action_prompt": "{count} movidos para a reciclagem", "trash_all": "Mover todos para a reciclagem", "trash_count": "Reciclar {count, number}", "trash_delete_asset": "Eliminar ficheiro", @@ -1855,9 +1917,11 @@ "unable_to_change_pin_code": "Não foi possível alterar o código PIN", "unable_to_setup_pin_code": "Não foi possível configurar o código PIN", "unarchive": "Desarquivar", + "unarchive_action_prompt": "{count} removidos do Arquivo", "unarchived_count": "{count, plural, other {Não arquivado #}}", "undo": "Anular", "unfavorite": "Remover favorito", + "unfavorite_action_prompt": "{count} removidos dos Favoritos", "unhide_person": "Exibir pessoa", "unknown": "Desconhecido", "unknown_country": "País desconhecido", @@ -1875,15 +1939,20 @@ "unselect_all_duplicates": "Remover seleção de todos os itens duplicados", "unselect_all_in": "Remover seleção de {group}", "unstack": "Desempilhar", + "unstack_action_prompt": "{count} desempilhados", "unstacked_assets_count": "Desempilhados {count, plural, one {# ficheiro} other {# ficheiros}}", + "untagged": "Marcador removido", "up_next": "A seguir", "updated_at": "Atualizado a", "updated_password": "Palavra-passe atualizada", "upload": "Carregar", + "upload_action_prompt": "{count} à espera de carregar", "upload_concurrency": "Carregamentos em simultâneo", + "upload_details": "Detalhes do Carregamento", "upload_dialog_info": "Deseja fazer o backup dos arquivos selecionados no servidor?", "upload_dialog_title": "Enviar arquivo", "upload_errors": "Envio completo com {count, plural, one {# erro} other {# erros}}, atualize a página para ver os novos ficheiros enviados.", + "upload_finished": "Carregamento acabado", "upload_progress": "Restante(s) {remaining, number} - Processado(s) {processed, number}/{total, number}", "upload_skipped_duplicates": "{count, plural, one {# Ignorado ficheiro duplicado} other {# Ignorados ficheiros duplicados}}", "upload_status_duplicates": "Duplicados", @@ -1892,6 +1961,7 @@ "upload_success": "Carregamento realizado com sucesso, atualize a página para ver os novos ficheiros carregados.", "upload_to_immich": "Enviar para o Immich ({count})", "uploading": "Enviando", + "uploading_media": "A carregar media", "url": "URL", "usage": "Utilização", "use_biometric": "Utilizar dados biométricos", @@ -1912,6 +1982,7 @@ "user_usage_stats_description": "Ver estatísticas de utilização de conta", "username": "Nome de utilizador", "users": "Utilizadores", + "users_added_to_album_count": "{count, plural, one {Foi adicionado # utilizador} other {Foram adicionados # utilizadores}} ao álbum", "utilities": "Ferramentas", "validate": "Validar", "validate_endpoint_error": "Digite uma URL válida", @@ -1930,6 +2001,7 @@ "view_album": "Ver Álbum", "view_all": "Ver tudo", "view_all_users": "Ver todos os utilizadores", + "view_details": "Ver Detalhes", "view_in_timeline": "Ver na linha do tempo", "view_link": "Ver link", "view_links": "Ver links", @@ -1951,7 +2023,7 @@ "wifi_name": "Nome da rede Wi-Fi", "wrong_pin_code": "Código PIN errado", "year": "Ano", - "years_ago": "Há {years, plural, one {# ano} other {# anos}} atrás", + "years_ago": "Há {years, plural, one {# ano} other {# anos}}", "yes": "Sim", "you_dont_have_any_shared_links": "Não tem links partilhados", "your_wifi_name": "Nome da sua rede Wi-Fi", diff --git a/i18n/pt_BR.json b/i18n/pt_BR.json index 705180cafd..c8bf543731 100644 --- a/i18n/pt_BR.json +++ b/i18n/pt_BR.json @@ -105,7 +105,7 @@ "library_scanning_enable_description": "Habilitar verificação periódica da biblioteca", "library_settings": "Biblioteca Externa", "library_settings_description": "Gerenciar configurações de biblioteca externa", - "library_tasks_description": "Escanear bibliotecas externas para ativos novos ou modificados", + "library_tasks_description": "Verificar se há arquivos novos ou modificados nas bibliotecas externas", "library_watching_enable_description": "Observe bibliotecas externas para alterações de arquivos", "library_watching_settings": "Observação de biblioteca (EXPERIMENTAL)", "library_watching_settings_description": "Observe automaticamente os arquivos alterados", @@ -166,9 +166,23 @@ "metadata_settings_description": "Gerenciar configurações de metadados", "migration_job": "Migração", "migration_job_description": "Migrar miniaturas de arquivos e rostos para a estrutura de pastas mais recente", + "nightly_tasks_cluster_faces_setting_description": "Fazer o reconhecimento facial dos novos rostos detectados", + "nightly_tasks_cluster_new_faces_setting": "Agrupar novos rostos", + "nightly_tasks_database_cleanup_setting": "Tarefas de limpeza do banco de dados", + "nightly_tasks_database_cleanup_setting_description": "Limpe dados velhos e expirados do banco de dados", + "nightly_tasks_generate_memories_setting": "Gerar memórias", + "nightly_tasks_generate_memories_setting_description": "Criar novas memórias a partir dos arquivos", + "nightly_tasks_missing_thumbnails_setting": "Gerar miniaturas em falta", + "nightly_tasks_missing_thumbnails_setting_description": "Adiciona na fila de geração de miniaturas as fotos ainda sem miniaturas", + "nightly_tasks_settings": "Configurações de Tarefas Diárias", + "nightly_tasks_settings_description": "Gerenciar tarefas diárias", + "nightly_tasks_start_time_setting": "Hora de início", + "nightly_tasks_start_time_setting_description": "A hora que o servidor começa a executar as tarefas diárias", + "nightly_tasks_sync_quota_usage_setting": "Utilização da quota de sincronização", + "nightly_tasks_sync_quota_usage_setting_description": "Atualizar quotas de armazenamento dos usuários, com base na utilização atual", "no_paths_added": "Nenhum caminho adicionado", "no_pattern_added": "Nenhum padrão adicionado", - "note_apply_storage_label_previous_assets": "Observação: Para aplicar o rótulo de armazenamento a arquivos carregados anteriormente, execute o", + "note_apply_storage_label_previous_assets": "Observação: Para aplicar o rótulo de armazenamento a arquivos enviados anteriormente, execute o", "note_cannot_be_changed_later": "NOTA: Isto não pode ser alterado posteriormente!", "notification_email_from_address": "E-mail de origem", "notification_email_from_address_description": "Endereço de e-mail do remetente, por exemplo: \"Immich Photo Server \". Tenha certeza de ter permissão para enviar e-mails a partir do endereço selecionado.", @@ -196,15 +210,17 @@ "oauth_mobile_redirect_uri": "URI de redirecionamento móvel", "oauth_mobile_redirect_uri_override": "Substituição de URI de redirecionamento móvel", "oauth_mobile_redirect_uri_override_description": "Ative quando o provedor do OAuth não suportar uma URI de aplicativo, por exemplo ''{callback}''", + "oauth_role_claim": "Declaração de função", + "oauth_role_claim_description": "Dá permissões de administrador baseado no valor desta declaração. A declaração pode conter os valores 'user' ou 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Gerenciar configurações de login do OAuth", "oauth_settings_more_details": "Para mais detalhes sobre este recurso, consulte a documentação.", - "oauth_storage_label_claim": "Reivindicação de rótulo de armazenamento", + "oauth_storage_label_claim": "Declaração do rótulo de armazenamento", "oauth_storage_label_claim_description": "Defina automaticamente o rótulo de armazenamento do usuário para o valor desta declaração.", - "oauth_storage_quota_claim": "Cota de armazenamento", + "oauth_storage_quota_claim": "Declaração de cota de armazenamento", "oauth_storage_quota_claim_description": "Defina automaticamente a cota de armazenamento do usuário para o valor desta declaração.", "oauth_storage_quota_default": "Cota de armazenamento padrão (GiB)", - "oauth_storage_quota_default_description": "Cota em GiB a ser usada quando nenhuma outra reivindicação for fornecida.", + "oauth_storage_quota_default_description": "Cota em GiB que será usada caso esta declaração não seja fornecida.", "oauth_timeout": "Tempo Limite de Requisição", "oauth_timeout_description": "Tempo limite para requisições, em milissegundos", "password_enable_description": "Login com e-mail e senha", @@ -234,20 +250,20 @@ "sidecar_job_description": "Descubra ou sincronize metadados secundários do sistema de arquivos", "slideshow_duration_description": "Tempo em segundos para exibir cada imagem", "smart_search_job_description": "Execute aprendizado de máquina em arquivos para oferecer suporte à pesquisa inteligente", - "storage_template_date_time_description": "A data e hora da criação do ativo é usado para a informações de data e hora", + "storage_template_date_time_description": "A data e hora da criação do arquivo é usado para a informações de data e hora", "storage_template_date_time_sample": "Exemplo {date}", "storage_template_enable_description": "Habilitar mecanismo de modelo de armazenamento", "storage_template_hash_verification_enabled": "Verificação de hash ativada", "storage_template_hash_verification_enabled_description": "Ativa a verificação de hash, não desative a menos que você tenha certeza das implicações", "storage_template_migration": "Migração de modelo de armazenamento", - "storage_template_migration_description": "Aplique o {template} atual aos arquivos carregados anteriormente", - "storage_template_migration_info": "O modelo altera todas extensões para minúsculo. As mudanças no modelo serão aplicadas apenas em novos arquivos. Para aplicar retroativamente o modelo aos arquivos carregados anteriormente, execute o {job}.", + "storage_template_migration_description": "Aplicar o {template} atual aos arquivos enviados anteriormente", + "storage_template_migration_info": "O modelo altera todas extensões para minúsculo. As mudanças no modelo serão aplicadas apenas em novos arquivos; para aplicar o modelo aos arquivos enviados anteriormente, execute o {job}.", "storage_template_migration_job": "Tarefa de Migração de Modelo de Armazenamento", "storage_template_more_details": "Para mais detalhes sobre este recurso, consulte o Modelo de Armazenamento e suas implicações", "storage_template_onboarding_description_v2": "Ao ser ativado, este recurso irá organizar automaticamente os arquivos com base em um modelo definido pelo usuário. Para mais informações, consulte a documentação.", "storage_template_path_length": "Limite aproximado de comprimento do caminho: {length, number}/{limit, number}", "storage_template_settings": "Modelo de Armazenamento", - "storage_template_settings_description": "Gerencie a estrutura de pasta e o nome do arquivo carregado", + "storage_template_settings_description": "Gerencie a estrutura de pasta e o nome do arquivo enviado", "storage_template_user_label": "{label} é o Rótulo de Armazenamento do usuário", "system_settings": "Configurações do Sistema", "tag_cleanup_job": "Limpeza de marcadores", @@ -336,7 +352,7 @@ "user_delete_delay_settings": "Remover atraso", "user_delete_delay_settings_description": "Número de dias após a remoção para excluir permanentemente a conta e os arquivos de um usuário. A tarefa de exclusão de usuário é executada à meia-noite para verificar usuários que estão prontos para exclusão. As alterações nesta configuração serão avaliadas na próxima execução.", "user_delete_immediately": "A conta e os arquivos de {user} serão programados para exclusão permanente imediata.", - "user_delete_immediately_checkbox": "Adicionar o usuário e seus ativos na fila para serem deletados imediatamente", + "user_delete_immediately_checkbox": "Adicionar o usuário e seus arquivos na fila para serem deletados imediatamente", "user_details": "Detalhes do Usuário", "user_management": "Gerenciamento de usuários", "user_password_has_been_reset": "A senha do usuário foi redefinida:", @@ -357,6 +373,8 @@ "admin_password": "Senha do administrador", "administration": "Administração", "advanced": "Avançado", + "advanced_settings_beta_timeline_subtitle": "Teste a nova interface do aplicativo", + "advanced_settings_beta_timeline_title": "Linha do tempo Beta", "advanced_settings_enable_alternate_media_filter_subtitle": "Use esta opção para filtrar mídias durante a sincronização com base em critérios alternativos. Tente esta opção somente se o aplicativo estiver com problemas para detectar todos os álbuns.", "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTAL] Utilizar filtro alternativo de sincronização de álbum de dispositivo", "advanced_settings_log_level_title": "Nível de log: {level}", @@ -379,6 +397,7 @@ "album_cover_updated": "Capa do álbum atualizada", "album_delete_confirmation": "Tem certeza de que deseja excluir o álbum {album}?", "album_delete_confirmation_description": "Se este álbum é compartilhado, os outros usuários não conseguiram mais acessá-lo.", + "album_deleted": "Álbum deletado", "album_info_card_backup_album_excluded": "EXCLUÍDO", "album_info_card_backup_album_included": "INCLUÍDO", "album_info_updated": "Informações do álbum atualizadas", @@ -388,6 +407,7 @@ "album_options": "Opções de álbum", "album_remove_user": "Remover usuário?", "album_remove_user_confirmation": "Tem certeza de que deseja remover {user}?", + "album_search_not_found": "Não há álbum que corresponda à sua pesquisa", "album_share_no_users": "Parece que você já compartilhou este álbum com todos os usuários ou não há nenhum usuário para compartilhar.", "album_updated": "Álbum atualizado", "album_updated_setting_description": "Receba uma notificação por e-mail quando um álbum compartilhado tiver novos recursos", @@ -407,6 +427,7 @@ "albums_default_sort_order": "Ordem padrão do álbum", "albums_default_sort_order_description": "Ordem padrão dos arquivos ao criar novos álbuns.", "albums_feature_description": "Coleções de arquivos que podem ser compartilhados com outros usuários.", + "albums_on_device_count": "Álbuns no dispositivo ({count})", "all": "Todos", "all_albums": "Todos os álbuns", "all_people": "Todas as pessoas", @@ -426,7 +447,8 @@ "app_bar_signout_dialog_title": "Sair", "app_settings": "Configurações do Aplicativo", "appears_in": "Aparece em", - "archive": "Arquivados", + "archive": "Arquivar", + "archive_action_prompt": "{count} mídias arquivadas", "archive_or_unarchive_photo": "Arquivar ou desarquivar foto", "archive_page_no_archived_assets": "Nenhum arquivo encontrado", "archive_page_title": "Arquivados ({count})", @@ -440,7 +462,7 @@ "asset_action_share_err_offline": "Não foi possível obter os arquivos indisponíveis, ignorando", "asset_added_to_album": "Adicionado ao álbum", "asset_adding_to_album": "Adicionando ao álbum…", - "asset_description_updated": "A descrição do ativo foi atualizada", + "asset_description_updated": "A descrição do arquivo foi atualizada", "asset_filename_is_offline": "O arquivo {filename} não está disponível", "asset_has_unassigned_faces": "O arquivo tem rostos sem nomes", "asset_hashing": "Processando…", @@ -457,14 +479,13 @@ "asset_restored_successfully": "Arquivo restaurado", "asset_skipped": "Ignorado", "asset_skipped_in_trash": "Na lixeira", - "asset_uploaded": "Carregado", - "asset_uploading": "Carregando…", + "asset_uploaded": "Enviado", + "asset_uploading": "Enviando…", "asset_viewer_settings_subtitle": "Gerenciar as configurações do visualizador da galeria", "asset_viewer_settings_title": "Visualizador de Mídia", "assets": "Arquivos", "assets_added_count": "{count, plural, one {# arquivo adicionado} other {# arquivos adicionados}}", "assets_added_to_album_count": "{count, plural, one {# arquivo adicionado} other {# arquivos adicionados}} ao álbum", - "assets_added_to_name_count": "{count, plural, one {# arquivo adicionado} other {# arquivos adicionados}} {hasName, select, true {ao álbum {name}} other {em um novo álbum}}", "assets_cannot_be_added_to_album_count": "Não foi possível adicionar {count, plural, one {o arquivo} other {os arquivos}} ao álbum", "assets_count": "{count, plural, one {# arquivo} other {# arquivos}}", "assets_deleted_permanently": "{count} arquivo(s) deletado(s) permanentemente", @@ -489,7 +510,8 @@ "back": "Voltar", "back_close_deselect": "Voltar, fechar ou desmarcar", "background_location_permission": "Permissão de localização em segundo plano", - "background_location_permission_content": "Para que seja possível trocar a URL quando estiver executando em segundo plano, o Immich deve *sempre* ter a permissão de localização precisa para que o aplicativo consiga ler o nome da rede Wi-Fi", + "background_location_permission_content": "Para que seja possível trocar o endereço quando estiver executando em segundo plano, o Immich deve *sempre* ter a permissão de localização precisa para que o aplicativo consiga ler o nome da rede Wi-Fi", + "backup": "Backup", "backup_album_selection_page_albums_device": "Álbuns no dispositivo ({count})", "backup_album_selection_page_albums_tap": "Toque para incluir, toque duas vezes para excluir", "backup_album_selection_page_assets_scatter": "Os recursos podem se espalhar por vários álbuns. Assim, os álbuns podem ser incluídos ou excluídos durante o processo de backup.", @@ -502,9 +524,9 @@ "backup_background_service_current_upload_notification": "Enviando {filename}", "backup_background_service_default_notification": "Verificando se há novos arquivos…", "backup_background_service_error_title": "Erro no backup", - "backup_background_service_in_progress_notification": "Fazendo backup de seus ativos…", + "backup_background_service_in_progress_notification": "Fazendo backup de seus arquivos…", "backup_background_service_upload_failure_notification": "Falha ao enviar {filename}", - "backup_controller_page_albums": "Álbuns de backup", + "backup_controller_page_albums": "Backup de álbuns", "backup_controller_page_background_app_refresh_disabled_content": "Para utilizar o backup em segundo plano, ative a atualização da aplicação em segundo plano em Configurações > Geral > Atualização em 2º plano.", "backup_controller_page_background_app_refresh_disabled_title": "Atualização em 2º plano desativada", "backup_controller_page_background_app_refresh_enable_button_text": "Ir para as configurações", @@ -512,40 +534,40 @@ "backup_controller_page_background_battery_info_message": "Para uma melhor experiência de backup em segundo plano, desative todas as otimizações de bateria que restrinjam a atividade em segundo plano do Immich.\n\nComo isso é específico por dispositivo, consulte as informações de como fazer isso com o fabricante do seu dispositivo.", "backup_controller_page_background_battery_info_ok": "OK", "backup_controller_page_background_battery_info_title": "Otimizações de bateria", - "backup_controller_page_background_charging": "Apenas durante o carregamento", + "backup_controller_page_background_charging": "Apenas enquanto carrega a bateria", "backup_controller_page_background_configure_error": "Falha ao configurar o serviço em segundo plano", "backup_controller_page_background_delay": "Adiar backup de novos arquivos: {duration}", - "backup_controller_page_background_description": "Ative o serviço em segundo plano para fazer backup automático de novos ativos sem precisar abrir o aplicativo", + "backup_controller_page_background_description": "Ative o serviço em segundo plano para fazer backup automático de novos arquivos sem precisar abrir o aplicativo", "backup_controller_page_background_is_off": "O backup automático em segundo plano está desativado", "backup_controller_page_background_is_on": "O backup automático em segundo plano está ativado", "backup_controller_page_background_turn_off": "Desativar o serviço em segundo plano", "backup_controller_page_background_turn_on": "Ativar o serviço em segundo plano", "backup_controller_page_background_wifi": "Apenas no Wi-Fi", "backup_controller_page_backup": "Backup", - "backup_controller_page_backup_selected": "Selecionado: ", - "backup_controller_page_backup_sub": "Backup de fotos e vídeos", - "backup_controller_page_created": "Criado em: {date}", - "backup_controller_page_desc_backup": "Ative o backup para carregar automaticamente novos ativos no servidor.", - "backup_controller_page_excluded": "Excluído: ", + "backup_controller_page_backup_selected": "Selecionados: ", + "backup_controller_page_backup_sub": "Total de mídias com backup", + "backup_controller_page_created": "Data: {date}", + "backup_controller_page_desc_backup": "Ative para fazer backup automático dos novos arquivos ao abrir este aplicativo.", + "backup_controller_page_excluded": "Ignorados: ", "backup_controller_page_failed": "Falhou ({count})", - "backup_controller_page_filename": "Nome do arquivo: {filename} [{size}]", + "backup_controller_page_filename": "Arquivo: {filename} [{size}]", "backup_controller_page_id": "ID: {id}", - "backup_controller_page_info": "Informações de backup", - "backup_controller_page_none_selected": "Nenhum selecionado", + "backup_controller_page_info": "Informações do backup", + "backup_controller_page_none_selected": "Nenhum álbum selecionado", "backup_controller_page_remainder": "Restante", - "backup_controller_page_remainder_sub": "Fotos e vídeos restantes para fazer backup da seleção", + "backup_controller_page_remainder_sub": "Mídias nos álbuns selecionados que ainda não tem backup", "backup_controller_page_server_storage": "Armazenamento do servidor", - "backup_controller_page_start_backup": "Iniciar backup", - "backup_controller_page_status_off": "O backup está desativado", - "backup_controller_page_status_on": "O backup está ativado", + "backup_controller_page_start_backup": "Iniciar backup manual", + "backup_controller_page_status_off": "O backup automático está desativado", + "backup_controller_page_status_on": "O backup automático está ativado", "backup_controller_page_storage_format": "{used} de {total} usados", - "backup_controller_page_to_backup": "Álbuns para backup", - "backup_controller_page_total_sub": "Todas as fotos e vídeos únicos dos álbuns selecionados", - "backup_controller_page_turn_off": "Desativar o backup", - "backup_controller_page_turn_on": "Ativar Backup", - "backup_controller_page_uploading_file_info": "Carregando informações do arquivo", + "backup_controller_page_to_backup": "Escolha os álbuns para fazer backup", + "backup_controller_page_total_sub": "Total de mídias nos álbuns selecionados", + "backup_controller_page_turn_off": "Desativar backup automático", + "backup_controller_page_turn_on": "Ativar backup automático", + "backup_controller_page_uploading_file_info": "Informações do arquivo", "backup_err_only_album": "Não é possível remover o único álbum", - "backup_info_card_assets": "ativos", + "backup_info_card_assets": "arquivos", "backup_manual_cancelled": "Cancelado", "backup_manual_in_progress": "Envio já está em progresso. Tente novamente mais tarde", "backup_manual_success": "Sucesso", @@ -553,6 +575,8 @@ "backup_options_page_title": "Opções de backup", "backup_setting_subtitle": "Gerenciar as configurações de envio em primeiro e segundo plano", "backward": "Para trás", + "beta_sync": "Status da sincronização Beta", + "beta_sync_subtitle": "Configurar o novo sistema de sincronização", "biometric_auth_enabled": "Autenticação por biometria ativada", "biometric_locked_out": "Sua autenticação por biometria está bloqueada", "biometric_no_options": "Não há opções de biometria disponíveis", @@ -570,7 +594,7 @@ "cache_settings_clear_cache_button": "Limpar o cache", "cache_settings_clear_cache_button_title": "Limpa o cache do aplicativo. Isso afetará significativamente o desempenho do aplicativo até que o cache seja reconstruído.", "cache_settings_duplicated_assets_clear_button": "LIMPAR", - "cache_settings_duplicated_assets_subtitle": "Fotos e vídeos que são bloqueados pelo app", + "cache_settings_duplicated_assets_subtitle": "Mídias ignoradas pelo app", "cache_settings_duplicated_assets_title": "Arquivos duplicados ({count})", "cache_settings_statistics_album": "Miniaturas da biblioteca", "cache_settings_statistics_full": "Imagens completas", @@ -587,6 +611,7 @@ "cancel": "Cancelar", "cancel_search": "Cancelar pesquisa", "canceled": "Cancelado", + "canceling": "Cancelando", "cannot_merge_people": "Não é possível mesclar pessoas", "cannot_undo_this_action": "Você não pode desfazer esta ação!", "cannot_update_the_description": "Não é possível atualizar a descrição", @@ -640,7 +665,7 @@ "comments_are_disabled": "Comentários estão desativados", "common_create_new_album": "Criar novo álbum", "common_server_error": "Verifique a sua conexão de rede, certifique-se de que o servidor está acessível e de que as versões do aplicativo e servidor são compatíveis.", - "completed": "Sucesso", + "completed": "Completado", "confirm": "Confirmar", "confirm_admin_password": "Confirmar senha de administrador", "confirm_delete_face": "Tem certeza que deseja remover a rosto de {name} deste arquivo?", @@ -658,9 +683,9 @@ "control_bottom_app_bar_create_new_album": "Criar novo álbum", "control_bottom_app_bar_delete_from_immich": "Excluir do Immich", "control_bottom_app_bar_delete_from_local": "Excluir do dispositivo", - "control_bottom_app_bar_edit_location": "Editar Localização", + "control_bottom_app_bar_edit_location": "Alterar Local", "control_bottom_app_bar_edit_time": "Editar data e hora", - "control_bottom_app_bar_share_link": "Compartilhar Link", + "control_bottom_app_bar_share_link": "Link", "control_bottom_app_bar_share_to": "Compartilhar", "control_bottom_app_bar_trash_from_immich": "Mover para a Lixeira", "copied_image_to_clipboard": "Imagem copiada para a área de transferência.", @@ -680,7 +705,7 @@ "create_album_page_untitled": "Sem título", "create_library": "Criar biblioteca", "create_link": "Criar link", - "create_link_to_share": "Criar link para partilhar", + "create_link_to_share": "Criar link e compartilhar", "create_link_to_share_description": "Permitir que qualquer pessoa com o link veja a(s) foto(s) selecionada(s)", "create_new": "CRIAR NOVO", "create_new_person": "Criar nova pessoa", @@ -700,10 +725,11 @@ "current_server_address": "Endereço atual do servidor", "custom_locale": "Localização Customizada", "custom_locale_description": "Formatar datas e números baseados na linguagem e região", + "custom_url": "URL personalizada", "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Escuro", - "darkTheme": "trocar para tema escuro", + "dark_theme": "Usar tema escuro", "date_after": "Data após", "date_and_time": "Data e Hora", "date_before": "Data antes", @@ -719,6 +745,8 @@ "default_locale": "Localização Padrão", "default_locale_description": "Formatar datas e números baseados na linguagem do seu navegador", "delete": "Excluir", + "delete_action_confirmation_message": "Confirma deletar este arquivo? O arquivo será enviado para a lixeira do servidor e depois perguntará se deseja deletar do seu dispositivo local", + "delete_action_prompt": "{count} deletados", "delete_album": "Excluir álbum", "delete_api_key_prompt": "Tem certeza de que deseja excluir esta chave de API?", "delete_dialog_alert": "Esses itens serão excluídos permanentemente do Immich e do seu dispositivo", @@ -732,9 +760,12 @@ "delete_key": "Excluir chave", "delete_library": "Excluir biblioteca", "delete_link": "Excluir link", + "delete_local_action_prompt": "{count} deletados do dispositivo local", "delete_local_dialog_ok_backed_up_only": "Excluir apenas arquivos com backup feito", "delete_local_dialog_ok_force": "Excluir mesmo assim", "delete_others": "Excluir restante", + "delete_permanently": "Deletar permanentemente", + "delete_permanently_action_prompt": "{count} Deletado permanentemente", "delete_shared_link": "Excluir link de compartilhamento", "delete_shared_link_dialog_title": "Excluir link compartilhado", "delete_tag": "Excluir marcador", @@ -745,6 +776,7 @@ "description": "Descrição", "description_input_hint_text": "Adicionar descrição...", "description_input_submit_error": "Erro ao atualizar a descrição, verifique o log para mais detalhes", + "deselect_all": "Desselecionar tudo", "details": "Detalhes", "direction": "Direção", "disabled": "Desativado", @@ -762,6 +794,7 @@ "documentation": "Documentação", "done": "Feito", "download": "Baixar", + "download_action_prompt": "Baixando {count} arquivos", "download_canceled": "Cancelado", "download_complete": "Sucesso", "download_enqueue": "Na fila", @@ -781,7 +814,7 @@ "downloading": "Baixando", "downloading_asset_filename": "Baixando arquivo {filename}", "downloading_media": "Baixando mídia", - "drop_files_to_upload": "Solte arquivos em qualquer lugar para carregar", + "drop_files_to_upload": "Solte os arquivos em qualquer lugar para enviar", "duplicates": "Duplicados", "duplicates_description": "Marque cada grupo indicando quais arquivos, se algum, são duplicados", "duration": "Duração", @@ -799,6 +832,7 @@ "edit_key": "Editar chave", "edit_link": "Editar link", "edit_location": "Editar Localização", + "edit_location_action_prompt": "{count} locais alterados", "edit_location_dialog_title": "Localização", "edit_name": "Editar nome", "edit_people": "Editar pessoas", @@ -814,9 +848,10 @@ "email": "E-mail", "email_notifications": "Notificações por e-mail", "empty_folder": "A pasta está vazia", - "empty_trash": "Esvaziar lixo", + "empty_trash": "Esvaziar lixeira", "empty_trash_confirmation": "Tem certeza de que deseja esvaziar a lixeira? Isso removerá permanentemente do Immich todos os arquivos que estão na lixeira.\nVocê não pode desfazer esta ação!", "enable": "Habilitar", + "enable_backup": "Ativar Backup", "enable_biometric_auth_description": "Insira seu código PIN para ativar a autenticação por biometria", "enabled": "Habilitado", "end_date": "Data final", @@ -855,8 +890,8 @@ "failed_to_edit_shared_link": "Falha ao editar o link compartilhado", "failed_to_get_people": "Falha na obtenção de pessoas", "failed_to_keep_this_delete_others": "Falha ao manter este arquivo e excluir os outros", - "failed_to_load_asset": "Não foi possível carregar o ativo", - "failed_to_load_assets": "Não foi possível carregar os ativos", + "failed_to_load_asset": "Não foi possível carregar o arquivo", + "failed_to_load_assets": "Não foi possível carregar os arquivos", "failed_to_load_notifications": "Falha ao carregar notificações", "failed_to_load_people": "Falha ao carregar pessoas", "failed_to_remove_product_key": "Falha ao remover a chave do produto", @@ -949,7 +984,7 @@ "unable_to_update_settings": "Não foi possível atualizar as configurações", "unable_to_update_timeline_display_status": "Não foi possível atualizar o modo de visualização da linha do tempo", "unable_to_update_user": "Não foi possível atualizar o usuário", - "unable_to_upload_file": "Não foi possível carregar o arquivo" + "unable_to_upload_file": "Não foi possível enviar o arquivo" }, "exif": "Exif", "exif_bottom_sheet_description": "Adicionar descrição...", @@ -973,17 +1008,20 @@ "explorer": "Explorar", "export": "Exportar", "export_as_json": "Exportar como JSON", + "export_database": "Exportar Banco de Dados", + "export_database_description": "Exportar o Banco de Dados SQLite", "extension": "Extensão", "external": "Externo", "external_libraries": "Bibliotecas externas", "external_network": "Rede externa", - "external_network_sheet_info": "Quando não estiver na rede Wi-Fi especificada, o aplicativo irá se conectar usando a primeira URL abaixo que obtiver sucesso, começando do topo da lista para baixo", + "external_network_sheet_info": "Quando não estiver na rede Wi-Fi especificada, o aplicativo irá se conectar usando o primeiro endereço abaixo que obtiver sucesso, começando do topo da lista para baixo", "face_unassigned": "Sem nome", "failed": "Falhou", "failed_to_authenticate": "Não foi possível autenticar", "failed_to_load_assets": "Falha ao carregar arquivos", "failed_to_load_folder": "Falha ao carregar a pasta", "favorite": "Favorito", + "favorite_action_prompt": "{count} marcados como favorito", "favorite_or_unfavorite_photo": "Marque ou desmarque a foto como favorita", "favorites": "Favoritos", "favorites_page_no_favorites": "Nenhuma mídia favorita encontrada", @@ -1023,6 +1061,9 @@ "haptic_feedback_switch": "Ativar vibração", "haptic_feedback_title": "Vibração", "has_quota": "Cota", + "hash_asset": "Calcular hash dos arquivos", + "hashed_assets": "Com hash", + "hashing": "Calculando", "header_settings_add_header_tip": "Adicionar Cabeçalho", "header_settings_field_validator_msg": "O valor não pode estar vazio", "header_settings_header_name_input": "Nome do cabeçalho", @@ -1055,6 +1096,7 @@ "host": "Servidor", "hour": "Hora", "id": "ID", + "idle": "Inativo", "ignore_icloud_photos": "Ignorar fotos do iCloud", "ignore_icloud_photos_description": "Fotos que estão armazenadas no iCloud não serão enviadas para o servidor do Immich", "image": "Imagem", @@ -1127,18 +1169,20 @@ "library_page_sort_created": "Data de criação", "library_page_sort_last_modified": "Última modificação", "library_page_sort_title": "Título do álbum", + "licenses": "Licenças", "light": "Claro", "like_deleted": "Curtida excluída", "link_motion_video": "Relacionar video animado", - "link_options": "Opções do Link", "link_to_oauth": "Link do OAuth", "linked_oauth_account": "Conta OAuth Vinculada", "list": "Lista", "loading": "Carregando", "loading_search_results_failed": "Falha ao carregar os resultados da pesquisa", + "local": "Local", "local_asset_cast_failed": "Não é possível transmitir um arquivo que não foi enviado ao servidor", + "local_assets": "Arquivos no dispositivo", "local_network": "Rede local", - "local_network_sheet_info": "O aplicativo irá se conectar ao servidor através desta URL quando estiver na rede Wi-Fi especificada", + "local_network_sheet_info": "O aplicativo irá se conectar ao servidor através deste endereço quando estiver na rede Wi-Fi especificada", "location_permission": "Permissão de localização", "location_permission_content": "Para utilizar a função de troca automática de URL é necessário a permissão de localização precisa, para que seja possível ler o nome da rede Wi-Fi", "location_picker_choose_on_map": "Escolha no mapa", @@ -1147,7 +1191,7 @@ "location_picker_longitude_error": "Digite uma longitude válida", "location_picker_longitude_hint": "Digite a longitude", "lock": "Trancar", - "locked_folder": "Pasta Trancada", + "locked_folder": "Pasta com senha", "log_out": "Sair", "log_out_all_devices": "Sair de todos dispositivos", "logged_in_as": "Usuário atual: {user}", @@ -1167,7 +1211,7 @@ "login_form_err_trailing_whitespace": "Há um espaço em branco no fim", "login_form_failed_get_oauth_server_config": "Erro de login com OAuth, verifique a URL do servidor", "login_form_failed_get_oauth_server_disable": "O recurso OAuth não está disponível neste servidor", - "login_form_failed_login": "Erro ao fazer login, verifique a url do servidor, e-mail e senha", + "login_form_failed_login": "Erro ao fazer login, verifique a URL do servidor, e-mail e senha", "login_form_handshake_exception": "Houve um erro de autorização com o servidor. Se estiver utilizando um certificado auto assinado, ative o suporte a isso nas configurações.", "login_form_password_hint": "senha", "login_form_save_login": "Permaneçer conectado", @@ -1193,8 +1237,7 @@ "manage_your_devices": "Gerenciar seus dispositivos logados", "manage_your_oauth_connection": "Gerenciar sua conexão OAuth", "map": "Mapa", - "map_assets_in_bound": "{count} foto", - "map_assets_in_bounds": "{count} fotos", + "map_assets_in_bounds": "{count, plural, one {# foto} other {# fotos}}", "map_cannot_get_user_location": "Não foi possível obter a sua localização", "map_location_dialog_yes": "Sim", "map_location_picker_page_use_location": "Use esta localização", @@ -1246,6 +1289,7 @@ "more": "Mais", "move": "Mover", "move_off_locked_folder": "Mover para fora da pasta com senha", + "move_to_lock_folder_action_prompt": "{count} adicionados à pasta com senha", "move_to_locked_folder": "Mover para a pasta com senha", "move_to_locked_folder_confirmation": "Estas fotos e vídeos serão removidos de todos os álbuns e somente poderão ser visualizados de dentro da pasta com senha", "moved_to_archive": "{count, plural, one {# mídia foi arquivada} other {# mídias foram arquivadas}}", @@ -1276,12 +1320,12 @@ "no_albums_with_name_yet": "Parece que você ainda não tem nenhum álbum com esse nome.", "no_albums_yet": "Parece que você ainda não tem nenhum álbum.", "no_archived_assets_message": "Arquive fotos e vídeos para os ocultar da sua visualização de fotos", - "no_assets_message": "CLIQUE PARA CARREGAR SUA PRIMEIRA FOTO", + "no_assets_message": "CLIQUE PARA ENVIAR SUA PRIMEIRA FOTO", "no_assets_to_show": "Não há arquivos para exibir", "no_cast_devices_found": "Nenhum dispositivo encontrado", "no_duplicates_found": "Nenhuma duplicidade foi encontrada.", "no_exif_info_available": "Sem informações exif disponíveis", - "no_explore_results_message": "Carregue mais fotos para explorar sua coleção.", + "no_explore_results_message": "Envie mais fotos para explorar sua coleção.", "no_favorites_message": "Adicione aos favoritos para encontrar suas melhores fotos e vídeos rapidamente", "no_libraries_message": "Crie uma biblioteca externa para ver suas fotos e vídeos", "no_locked_photos_message": "Fotos e vídeos na pasta com senha são ocultos e não serão exibidos enquanto explora ou pesquisa na biblioteca.", @@ -1292,9 +1336,10 @@ "no_results": "Sem resultados", "no_results_description": "Tente um sinônimo ou uma palavra-chave mais geral", "no_shared_albums_message": "Crie um álbum para compartilhar fotos e vídeos com pessoas em sua rede", + "no_uploads_in_progress": "Nenhum envio em progresso", "not_in_any_album": "Fora de álbum", "not_selected": "Não selecionado", - "note_apply_storage_label_to_previously_uploaded assets": "Nota: Para aplicar o rótulo de armazenamento a arquivos carregados anteriormente, execute o", + "note_apply_storage_label_to_previously_uploaded assets": "Nota: Para aplicar o rótulo de armazenamento a arquivos enviados anteriormente, execute o", "notes": "Notas", "nothing_here_yet": "Ainda não existe nada aqui", "notification_permission_dialog_content": "Para ativar as notificações, vá em Configurações e selecione permitir.", @@ -1329,6 +1374,7 @@ "original": "original", "other": "Outro", "other_devices": "Outros dispositivos", + "other_entities": "Outras entidades", "other_variables": "Outras variáveis", "owned": "Seu", "owner": "Dono", @@ -1369,7 +1415,7 @@ "permanent_deletion_warning_setting_description": "Exibe um aviso ao deletar arquivos de forma permanente", "permanently_delete": "Deletar permanentemente", "permanently_delete_assets_count": "Excluir permanentemente {count, plural, one {asset} other {assets}}", - "permanently_delete_assets_prompt": "Você tem certeza de que deseja excluir permanentemente {count, plural, one {este ativo?} other {estes # ativos?}} Esta ação também removerá {count, plural, one {o ativo} other {os ativos}} de um ou mais álbuns.", + "permanently_delete_assets_prompt": "Você tem certeza de que deseja excluir permanentemente {count, plural, one {este arquivo?} other {estes # arquivos?}} Esta ação também removerá {count, plural, one {o arquivo} other {os arquivos}} de um ou mais álbuns.", "permanently_deleted_asset": "Arquivo deletado permanentemente", "permanently_deleted_assets_count": "{count, plural, one {# arquivo permanentemente excluído} other {# arquivos permanentemente excluídos}}", "permission": "Permissão", @@ -1460,6 +1506,7 @@ "purchase_server_description_2": "Status de Contribuidor", "purchase_server_title": "Servidor", "purchase_settings_server_activated": "A chave do produto para servidor é gerenciada pelo administrador", + "queue_status": "Na fila {count} de {total}", "rating": "Estrelas", "rating_clear": "Limpar classificação", "rating_count": "{count, plural, one {# estrela} other {# estrelas}}", @@ -1488,6 +1535,8 @@ "refreshing_faces": "Atualizando rostos", "refreshing_metadata": "Atualizando metadados", "regenerating_thumbnails": "Regenerando miniaturas", + "remote": "Remoto", + "remote_assets": "Arquivos Remotos", "remove": "Remover", "remove_assets_album_confirmation": "Tem certeza de que deseja remover {count, plural, one {# arquivo} other {# arquivos}} do álbum?", "remove_assets_shared_link_confirmation": "Tem certeza de que deseja remover {count, plural, one {# arquivo} other {# arquivos}} desse link compartilhado?", @@ -1495,7 +1544,9 @@ "remove_custom_date_range": "Remover intervalo de datas personalizado", "remove_deleted_assets": "Remover arquivos excluídos", "remove_from_album": "Remover do álbum", + "remove_from_album_action_prompt": "{count} removido do álbum", "remove_from_favorites": "Remover dos favoritos", + "remove_from_lock_folder_action_prompt": "{count} removidos da pasta com senha", "remove_from_locked_folder": "Remover da pasta com senha", "remove_from_locked_folder_confirmation": "Tem a certeza de que deseja mover estes arquivos para fora da pasta com senha? Eles ficarão visíveis na biblioteca principal.", "remove_from_shared_link": "Remover do link compartilhado", @@ -1523,19 +1574,24 @@ "reset_password": "Resetar senha", "reset_people_visibility": "Resetar pessoas ocultas", "reset_pin_code": "Redefinir código PIN", + "reset_sqlite": "Redefinir o Banco de Dados SQLite", + "reset_sqlite_confirmation": "Realmente deseja redefinir o banco de dados SQLite? Será necessário sair e entrar em sua conta novamente para ressincronizar os dados", + "reset_sqlite_success": "Banco de dados SQLite redefinido com sucesso", "reset_to_default": "Redefinir para a configuração padrão", "resolve_duplicates": "Resolver duplicatas", "resolved_all_duplicates": "Todas duplicidades resolvidas", "restore": "Restaurar", "restore_all": "Restaurar tudo", + "restore_trash_action_prompt": "{count} restaurados da lixeira", "restore_user": "Restaurar usuário", "restored_asset": "Arquivo restaurado", "resume": "Continuar", - "retry_upload": "Tentar carregar novamente", + "retry_upload": "Tentar enviar novamente", "review_duplicates": "Revisar duplicidade", "role": "Função", "role_editor": "Editor", "role_viewer": "Visualizador", + "running": "Executando", "save": "Salvar", "save_to_gallery": "Salvar na galeria", "saved_api_key": "Chave de API salva", @@ -1626,7 +1682,7 @@ "send_welcome_email": "Enviar E-mail de boas vindas", "server_endpoint": "URL do servidor", "server_info_box_app_version": "Versão do aplicativo", - "server_info_box_server_url": "URL do servidor", + "server_info_box_server_url": "Endereço", "server_offline": "Servidor Indisponível", "server_online": "Servidor Disponível", "server_privacy": "Privacidade do servidor", @@ -1667,10 +1723,11 @@ "settings_saved": "Configurações salvas", "setup_pin_code": "Criar um código PIN", "share": "Compartilhar", + "share_action_prompt": "{count} arquivos compartilhados", "share_add_photos": "Adicionar fotos", "share_assets_selected": "{count} selecionado", "share_dialog_preparing": "Preparando...", - "share_link": "Compartilhar Link", + "share_link": "Criar Link", "shared": "Compartilhado", "shared_album_activities_input_disable": "Comentários desativados", "shared_album_activity_remove_content": "Deseja excluir esta atividade?", @@ -1688,6 +1745,7 @@ "shared_link_clipboard_copied_massage": "Copiado para a área de transferência", "shared_link_clipboard_text": "Link: {link}\nSenha: {password}", "shared_link_create_error": "Erro ao criar o link compartilhado", + "shared_link_custom_url_description": "Acessar este link com uma URL personalizada", "shared_link_edit_description_hint": "Digite a descrição do compartilhamento", "shared_link_edit_expire_after_option_day": "1 dia", "shared_link_edit_expire_after_option_days": "{count} dias", @@ -1713,7 +1771,8 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Gerenciar links compartilhados", "shared_link_options": "Opções de link compartilhado", - "shared_links": "Links compartilhados", + "shared_link_password_description": "Exija uma senha para acessar este link compartilhado", + "shared_links": "Links", "shared_links_description": "Compartilhar fotos e videos com um link", "shared_photos_and_videos_count": "{assetCount, plural, one {# Foto & vídeo compartilhado.} other {# Fotos & vídeos compartilhados.}}", "shared_with_me": "Compartilhado comigo", @@ -1768,6 +1827,7 @@ "sort_title": "Título", "source": "Fonte", "stack": "Agrupar", + "stack_action_prompt": "{count} agrupados", "stack_duplicates": "Agrupar duplicados", "stack_select_one_photo": "Selecione uma foto principal para o grupo", "stack_selected_photos": "Agrupar fotos selecionadas", @@ -1787,6 +1847,7 @@ "storage_quota": "Quota de armazenamento", "storage_usage": "Utilizado {used} de {available}", "submit": "Enviar", + "success": "Sucesso", "suggestions": "Sugestões", "sunrise_on_the_beach": "Nascer do sol na praia", "support": "Ajuda", @@ -1796,6 +1857,8 @@ "sync": "Sincronizar", "sync_albums": "Sincronizar álbuns", "sync_albums_manual_subtitle": "Sincronize todos as fotos e vídeos enviados para os álbuns de backup selecionados", + "sync_local": "Sincronização Local", + "sync_remote": "Sincronização Remota", "sync_upload_album_setting_subtitle": "Crie e envie suas fotos e vídeos para o álbum selecionado no Immich", "tag": "Marcador", "tag_assets": "Marcar arquivos", @@ -1806,6 +1869,7 @@ "tag_updated": "Marcador foi atualizado: {tag}", "tagged_assets": "{count, plural, one {# Arquivo marcado} other {# Arquivos marcados}}", "tags": "Marcadores", + "tap_to_run_job": "Toque para executar", "template": "Modelo", "theme": "Tema", "theme_selection": "Selecionar tema", @@ -1838,6 +1902,7 @@ "total": "Total", "total_usage": "Utilização total", "trash": "Lixeira", + "trash_action_prompt": "{count} enviados à lixeira", "trash_all": "Mover todos para o lixo", "trash_count": "Lixo {count, number}", "trash_delete_asset": "Jogar na lixeira/Excluir Arquivo", @@ -1855,9 +1920,11 @@ "unable_to_change_pin_code": "Não foi possível alterar o código PIN", "unable_to_setup_pin_code": "Não foi possível criar o código PIN", "unarchive": "Desarquivar", + "unarchive_action_prompt": "{count} desarquivado", "unarchived_count": "{count, plural, one {# Desarquivado} other {# Desarquivados}}", "undo": "Desfazer", "unfavorite": "Remover favorito", + "unfavorite_action_prompt": "{count} removido dos favoritos", "unhide_person": "Exibir pessoa", "unknown": "Desconhecido", "unknown_country": "País desconhecido", @@ -1875,23 +1942,29 @@ "unselect_all_duplicates": "Desselecionar todas as duplicatas", "unselect_all_in": "Remover seleção de {group}", "unstack": "Retirar do grupo", + "unstack_action_prompt": "{count} desagrupados", "unstacked_assets_count": "{count, plural, one {# arquivo retirado} other {# arquivos retirados}} do grupo", + "untagged": "Marcador removido", "up_next": "A seguir", "updated_at": "Atualizado em", "updated_password": "Senha atualizada", - "upload": "Carregar", + "upload": "Enviar", + "upload_action_prompt": "{count} na fila de envio", "upload_concurrency": "Envios simultâneos", + "upload_details": "Detalhes do envio", "upload_dialog_info": "Deseja fazer o backup dos arquivos selecionados no servidor?", "upload_dialog_title": "Enviar arquivo", - "upload_errors": "Envio concluído com {count, plural, one {# erro} other {# erros}}, atualize a página para ver os novos arquivos carregados.", + "upload_errors": "Envio concluído com {count, plural, one {# erro} other {# erros}}, atualize a página para ver os novos arquivos.", + "upload_finished": "Envio finalizado", "upload_progress": "{remaining, number} restantes - {processed, number}/{total, number} já processados", "upload_skipped_duplicates": "{count, plural, one {# Arquivo duplicado foi ignorado} other {# Arquivos duplicados foram ignorados}}", "upload_status_duplicates": "Duplicados", "upload_status_errors": "Erros", - "upload_status_uploaded": "Carregado", - "upload_success": "Carregado com sucesso, atualize a página para ver os novos arquivos.", + "upload_status_uploaded": "Enviado", + "upload_success": "Enviado com sucesso, atualize a página para ver os novos arquivos.", "upload_to_immich": "Enviar para o Immich ({count})", "uploading": "Enviando", + "uploading_media": "Enviando mídia", "url": "URL", "usage": "Uso", "use_biometric": "Usar biometria", @@ -1912,6 +1985,7 @@ "user_usage_stats_description": "Ver estatísticas de utilização da conta", "username": "Nome do usuário", "users": "Usuários", + "users_added_to_album_count": "{count, plural, one {# usuário adicionado} other {# usuários adicionados}} ao álbum", "utilities": "Ferramentas", "validate": "Validar", "validate_endpoint_error": "Digite uma URL válida", @@ -1930,6 +2004,7 @@ "view_album": "Ver álbum", "view_all": "Ver tudo", "view_all_users": "Ver todos os usuários", + "view_details": "Ver Detalhes", "view_in_timeline": "Ver na linha do tempo", "view_link": "Ver link", "view_links": "Ver links", diff --git a/i18n/ro.json b/i18n/ro.json index 0b2ef61a36..40c50a317d 100644 --- a/i18n/ro.json +++ b/i18n/ro.json @@ -14,6 +14,7 @@ "add_a_location": "Adaugă o locație", "add_a_name": "Adaugă un nume", "add_a_title": "Adaugă un titlu", + "add_endpoint": "Adaugă punct final", "add_exclusion_pattern": "Adăugă un model de excludere", "add_import_path": "Adaugă o cale de import", "add_location": "Adaugă locație", @@ -21,6 +22,7 @@ "add_partner": "Adaugă partener", "add_path": "Adaugă o cale", "add_photos": "Adaugă fotografii", + "add_tag": "Adaugă etichetă", "add_to": "Adaugă la…", "add_to_album": "Adaugă în album", "add_to_album_bottom_sheet_added": "Adăugat în {album}", @@ -32,6 +34,7 @@ "added_to_favorites_count": "Adăugat {count, number} la favorite", "admin": { "add_exclusion_pattern_description": "Adăugați modele de excludere. Globing folosind *, ** și ? este suportat. Pentru a ignora toate fișierele din orice director numit „Raw”, utilizați „**/Raw/**”. Pentru a ignora toate fișierele care se termină în „.tif”, utilizați „**/*.tif”. Pentru a ignora o cale absolută, utilizați „/path/to/ignore/**”.", + "admin_user": "Utilizator admin", "asset_offline_description": "Acest material din biblioteca externă nu se mai găsește pe disc și a fost mutat în coșul de gunoi. Dacă fișierul a fost mutat în bibliotecă, verificați cronologia pentru noul material corespunzător. Pentru a restabili acest material, asigurați-vă că calea fișierului de mai jos poate fi accesată de Immich și scanați biblioteca.", "authentication_settings": "Setări de Autentificare", "authentication_settings_description": "Gestionează parola, OAuth și alte setări de autentificare", @@ -39,8 +42,8 @@ "authentication_settings_reenable": "Pentru a reactiva, folosește Comandă Server.", "background_task_job": "Activități de Fundal", "backup_database": "Salvare Bază de Date", - "backup_database_enable_description": "Activare salvare bază de date", - "backup_keep_last_amount": "Cantitatea de copii de rezervă anterioare de păstrat", + "backup_database_enable_description": "Activare salvarea bazei de date", + "backup_keep_last_amount": "Număr de copii de rezervă anterioare de păstrat", "backup_settings": "Setări Copii de Rezervă", "backup_settings_description": "Gestionați setările de salvare a bazei de date", "cleared_jobs": "Activități eliminate pentru: {job}", @@ -50,6 +53,7 @@ "confirm_email_below": "Pentru a confirma, tastați „{email}” mai jos", "confirm_reprocess_all_faces": "Sigur doriți să reprocesați toate fețele? Acest lucru va șterge și persoanele cu nume.", "confirm_user_password_reset": "Sigur doriți să resetați parola utilizatorului {user}?", + "confirm_user_pin_code_reset": "Ești sigur că vrei să resetezi codul PIN al {user}?", "create_job": "Creează sarcină", "cron_expression": "Expresia cron", "cron_expression_description": "Setați intervalul de scanare folosind formatul cron. Pentru mai multe informații, consultați de ex. Crontab Guru", @@ -162,12 +166,25 @@ "metadata_settings_description": "Gestionează setările pentru metadate", "migration_job": "Migrare", "migration_job_description": "Migrați miniaturile pentru elemente și fețe la cea mai recentă structură de foldere", + "nightly_tasks_cluster_faces_setting_description": "Rulează recunoașterea facială pe fețele noi recunoscute", + "nightly_tasks_cluster_new_faces_setting": "Grupează fetele noi", + "nightly_tasks_database_cleanup_setting": "Sarcini curățare baze de date", + "nightly_tasks_database_cleanup_setting_description": "Curată date vechi, expirate din baza de date", + "nightly_tasks_generate_memories_setting": "Generare memorii", + "nightly_tasks_generate_memories_setting_description": "Creează amintiri noi din resurse", + "nightly_tasks_missing_thumbnails_setting": "Generează miniaturi lipsă", + "nightly_tasks_settings": "Setări pentru sarcinile nocturne", + "nightly_tasks_settings_description": "Gestionați sarcinile nocturne", + "nightly_tasks_start_time_setting": "Ora de începere", + "nightly_tasks_start_time_setting_description": "Ora la care serverul începe să execute sarcinile nocturne", + "nightly_tasks_sync_quota_usage_setting": "Utilizarea cotei de sincronizare", + "nightly_tasks_sync_quota_usage_setting_description": "Actualizați cota de stocare a utilizatorului, în funcție de utilizarea actuală", "no_paths_added": "Nicio cale adăugată", "no_pattern_added": "Niciun tipar adăugat", "note_apply_storage_label_previous_assets": "Notă: Pentru a aplica Eticheta de Stocare la elementele încărcate anterior, executați", "note_cannot_be_changed_later": "NOTĂ: Nu se va mai putea modifica ulterior!", "notification_email_from_address": "De la adresa", - "notification_email_from_address_description": "Adresa expeditorului, spre exemplu: „Immich Photo Server ”", + "notification_email_from_address_description": "Adresa expeditorului, spre exemplu: „Immich Photo Server ”. Asigură-te că folosești o adresă de la care ai permisiunea de a trimite e-mailuri.", "notification_email_host_description": "Adresa serverului de email (ex. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Ingnoră erorile de certificat", "notification_email_ignore_certificate_errors_description": "Ignoră erorile de validare a certificatului TLS (nerecomandat)", @@ -187,10 +204,13 @@ "oauth_auto_register": "Auto înregistrare", "oauth_auto_register_description": "Înregistrează automat utilizatori noi după autentificarea cu OAuth", "oauth_button_text": "Text buton", + "oauth_client_secret_description": "Necesar dacă PKCE (Proof Key for Code Exchange) nu este suportat de furnizorul OAuth", "oauth_enable_description": "Autentifică-te cu OAuth", "oauth_mobile_redirect_uri": "URI de redirecționare mobilă", "oauth_mobile_redirect_uri_override": "Înlocuire URI de redirecționare mobilă", "oauth_mobile_redirect_uri_override_description": "Activați atunci când furnizorul OAuth nu permite un URI mobil, precum ''{callback}''", + "oauth_role_claim": "Revendicare de rol", + "oauth_role_claim_description": "Acordă automat acces de administrator în funcție de prezența acestei revendicări. Revendicarea poate avea fie 'utilizator', fie 'administrator'.", "oauth_settings": "OAuth", "oauth_settings_description": "Gestionați setările de conectare OAuth", "oauth_settings_more_details": "Pentru mai multe detalii despre aceastǎ funcționalitate, verificǎ documentația.", @@ -199,7 +219,9 @@ "oauth_storage_quota_claim": "Revendicare spațiu de stocare", "oauth_storage_quota_claim_description": "Setează automat spațiul de stocare al utilizatorului la valoarea acestei cereri.", "oauth_storage_quota_default": "Cota implicită a spațiului de stocare (GiB)", - "oauth_storage_quota_default_description": "Spațiul în GiB ce urmează a fi utilizat atunci când nu este furnizată nicio solicitare (introduceți 0 pentru spațiu nelimitat).", + "oauth_storage_quota_default_description": "Spațiul în GiB ce urmează a fi utilizat atunci când nu este furnizată nicio solicitare.", + "oauth_timeout": "Solicitarea a expirat", + "oauth_timeout_description": "Timp de expirare pentru solicitări în milisecunde", "password_enable_description": "Autentificare cu email și parolǎ", "password_settings": "Autentificare cu Parolǎ", "password_settings_description": "Gestioneazǎ setǎrile de autentificare cu parola", @@ -237,6 +259,7 @@ "storage_template_migration_info": "Șablonul de stocare va converti extensiile in litere mici. Modificările șablonului se vor aplica doar materialelor noi. Pentru a aplica retroactiv șablonul la materialele încărcate anterior, rulați {job}.", "storage_template_migration_job": "Sarcină Migrare Șablon Stocare", "storage_template_more_details": "Pentru mai multe detalii despre aceasta caracteristică, accesați Șablon stocare si implicațiile", + "storage_template_onboarding_description_v2": "Când este activată, această funcție va organiza automat fișierele pe baza șablonului definit de către utilizator. Pentru mai multe informații, accesează documentația.", "storage_template_path_length": "Limita de lungime pentru calea aproximativă: {length, number}/{limit, number}", "storage_template_settings": "Șablon Stocare", "storage_template_settings_description": "Gestionează structura folderelor și numele fișierelor pentru elementele încărcate", @@ -251,7 +274,7 @@ "template_email_update_album": "Actualizați Șablonul de Album", "template_email_welcome": "Șablon de e-mail de bun venit", "template_settings": "Șabloane de Notificare", - "template_settings_description": "Gestionați șabloanele personalizate pentru notificări.", + "template_settings_description": "Gestionați șabloanele personalizate pentru notificări", "theme_custom_css_settings": "CSS personalizat", "theme_custom_css_settings_description": "Foile de stil în cascadă (CSS) permit personalizarea designului Immich.", "theme_settings": "Setări Temă", @@ -283,7 +306,7 @@ "transcoding_encoding_options": "Opțiuni codificare", "transcoding_encoding_options_description": "Setează codecuri , calitatea, rezoluția și alte opțiuni pentru videoclipuri codificare", "transcoding_hardware_acceleration": "Accelerare Hardware", - "transcoding_hardware_acceleration_description": "Experimental; mult mai rapid, dar va avea o calitate mai scăzută la același bitrate", + "transcoding_hardware_acceleration_description": "Experimental: transcodare mai rapidă, dar poate reduce calitatea la aceeași rată de biți", "transcoding_hardware_decoding": "Decodare hardware", "transcoding_hardware_decoding_setting_description": "Se aplică doar pentru NVENC, QSV și RKMPP. Activează accelerarea completă în loc de doar accelerarea codificării. S-ar putea să nu funcționeze pentru toate videoclipurile.", "transcoding_max_b_frames": "Număr maxim de cadre B", @@ -329,6 +352,7 @@ "user_delete_delay_settings_description": "Numărul de zile după eliminare până la ștergerea permanentă a contului și a resurselor unui utilizator. Procesul de ștergere a utilizatorului rulează la miezul nopții pentru a verifica utilizatorii care sunt pregătiți pentru ștergere. Modificările aduse acestei setări vor fi evaluate la următoarea execuție.", "user_delete_immediately": "Contul și resursele utilizatorului {user} vor fi puse în coadă pentru ștergere permanentă imediat.", "user_delete_immediately_checkbox": "Pune utilizatorul și resursele în coadă pentru ștergere imediată", + "user_details": "Detalii utilizator", "user_management": "Gestionarea Utilizatorilor", "user_password_has_been_reset": "Parola utilizatorului a fost resetată:", "user_password_reset_description": "Vă rugăm să furnizați utilizatorului parola temporară și să îi informați că va trebui să o schimbe la următoarea autentificare.", @@ -348,11 +372,15 @@ "admin_password": "Parolă Administrator", "administration": "Administrare", "advanced": "Avansat", + "advanced_settings_beta_timeline_subtitle": "Încearcă noua experiență în aplicație", + "advanced_settings_beta_timeline_title": "Cronologie beta", "advanced_settings_enable_alternate_media_filter_subtitle": "Utilizați această opțiune pentru a filtra conținutul media în timpul sincronizării pe baza unor criterii alternative. Încercați numai dacă întâmpinați probleme cu aplicația la detectarea tuturor albumelor.", "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTAL] Utilizați filtrul alternativ de sincronizare a albumelor de pe dispozitiv", "advanced_settings_log_level_title": "Nivel log: {level}", - "advanced_settings_prefer_remote_subtitle": "Unele dispozitive întâmpină dificultăți în încărcarea miniaturilor pentru resursele de pe dispozitiv. Activează această setare pentru a încărca imaginile de la distanță în schimb.", + "advanced_settings_prefer_remote_subtitle": "Unele dispozitive încarcă extrem de lent miniaturile din resursele locale. Activați această setare pentru a încărca imagini la distanță.", "advanced_settings_prefer_remote_title": "Preferă fotografii la distanță", + "advanced_settings_proxy_headers_subtitle": "Definește antetele proxy pe care Immich ar trebui să le trimită cu fiecare solicitare de rețea", + "advanced_settings_proxy_headers_title": "Antete Proxy", "advanced_settings_self_signed_ssl_subtitle": "Omite verificare certificate SSL pentru distinația server-ului, necesar pentru certificate auto-semnate.", "advanced_settings_self_signed_ssl_title": "Permite certificate SSL auto-semnate", "advanced_settings_sync_remote_deletions_subtitle": "Ștergeți sau restaurați automat un element de pe acest dispozitiv atunci când acțiunea este efectuată pe web", @@ -368,6 +396,7 @@ "album_cover_updated": "Coperta albumului a fost actualizată", "album_delete_confirmation": "Ești sigur că vrei să ștergi albumul {album}?", "album_delete_confirmation_description": "Dacă acest album este partajat, alți utilizatori nu vor mai putea accesa.", + "album_deleted": "Album șters", "album_info_card_backup_album_excluded": "EXCLUSE", "album_info_card_backup_album_included": "INCLUSE", "album_info_updated": "Informații album actualizate", @@ -377,11 +406,13 @@ "album_options": "Opțiuni album", "album_remove_user": "Eliminare utilizator?", "album_remove_user_confirmation": "Ești sigur că dorești eliminarea {user}?", + "album_search_not_found": "Nu s-au găsit albume care să corespundă căutării dumneavoastră", "album_share_no_users": "Se pare că ai partajat acest album cu toți utilizatorii sau nu ai niciun utilizator cu care să-l partajezi.", "album_updated": "Album actualizat", "album_updated_setting_description": "Primiți o notificare prin e-mail când un album partajat are elemente noi", "album_user_left": "A părăsit {album}", "album_user_removed": "{user} eliminat", + "album_viewer_appbar_delete_confirm": "Ești sigur că vrei să ștergi acest album din contul tău?", "album_viewer_appbar_share_err_delete": "Ștergere album eșuată", "album_viewer_appbar_share_err_leave": "Părăsire album eșuată", "album_viewer_appbar_share_err_remove": "Probleme la ștergerea resurselor din album", @@ -392,6 +423,10 @@ "album_with_link_access": "Permite oricui cu link-ul să vadă fotografiile și persoanele din acest album.", "albums": "Albume", "albums_count": "{count, plural, one {{count, number} Album} other {{count, number} Albume}}", + "albums_default_sort_order": "Ordinea implicită de sortare a albumelor", + "albums_default_sort_order_description": "Ordinea inițială de sortare a pozelor la crearea de albume noi.", + "albums_feature_description": "Colecții de date care pot fi partajate cu alți utilizatori.", + "albums_on_device_count": "{count} albume pe dispozitiv", "all": "Toate", "all_albums": "Toate albumele", "all_people": "Toți oamenii", @@ -412,11 +447,13 @@ "app_settings": "Setări Aplicație", "appears_in": "Apare în", "archive": "Arhivă", + "archive_action_prompt": "{count} adăugate la Arhivă", "archive_or_unarchive_photo": "Arhiveazǎ sau dezarhiveazǎ fotografia", "archive_page_no_archived_assets": "Nu au fost găsite resurse favorite", "archive_page_title": "Arhivă ({count})", "archive_size": "Mărime arhivă", "archive_size_description": "Configurează dimensiunea arhivei pentru descărcări (în GiB)", + "archived": "Arhivat", "archived_count": "{count, plural, other {Arhivat/e#}}", "are_these_the_same_person": "Sunt aceștia aceeași persoană?", "are_you_sure_to_do_this": "Sunteți sigur că doriți să faceți acest lucru?", @@ -427,33 +464,53 @@ "asset_description_updated": "Descrierea resursei a fost actualizată", "asset_filename_is_offline": "Resursa {filename} este offline", "asset_has_unassigned_faces": "Resursa are fețe neatribuite", + "asset_hashing": "Calculare amprentă digitală", + "asset_list_group_by_sub_title": "Grupare după", "asset_list_layout_settings_dynamic_layout_title": "Aspect dinamic", "asset_list_layout_settings_group_automatically": "Automat", "asset_list_layout_settings_group_by": "Grupează resurse după", "asset_list_layout_settings_group_by_month_day": "Lună + zi", + "asset_list_layout_sub_title": "Aspect", "asset_list_settings_subtitle": "Setări format grilă fotografii", "asset_list_settings_title": "Grilă fotografii", "asset_offline": "Resursă Offline", "asset_offline_description": "Această resursă externă nu mai este găsită pe disc. Contactează te rog administratorul tău Immich pentru ajutor.", + "asset_restored_successfully": "Date restaurate cu succes", "asset_skipped": "Sărit", "asset_skipped_in_trash": "În coșul de gunoi", "asset_uploaded": "Încărcat", "asset_uploading": "Se incarcă…", + "asset_viewer_settings_subtitle": "Gestionați setările de vizualizare a galeriei", + "asset_viewer_settings_title": "Vizualizator resurse", "assets": "Resurse", "assets_added_count": "Adăugat {count, plural, one {# resursă} other {# resurse}}", "assets_added_to_album_count": "Am adăugat {count, plural, one {# resursă} other {# resurse}} în album", - "assets_added_to_name_count": "Am adăugat {count, plural, one {# resursă} other {# resurse}} în {hasName, select, true {{name}} other {albumul nou}}", + "assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} nu pot fi adăugate în album", "assets_count": "{count, plural, one {# resursă} other {# resurse}}", + "assets_deleted_permanently": "{count} poză/poze ștearsă/șterse permanent", + "assets_deleted_permanently_from_server": "{count} poză/poze ștearsă/șterse permanent din serverul Immich", + "assets_downloaded_failed": "{count, plural, one {S-a descărcat # fișier – {error} fișier eșuat} other {S-au descărcat # fișiere – {error} fișiere eșuate}}", + "assets_downloaded_successfully": "{count, plural, one {S-a descărcat cu succes # fișier} other {S-au descărcat cu succes # fișiere}}", "assets_moved_to_trash_count": "Am mutat {count, plural, one {# resursă} other {# resurse}} în coșul de gunoi", "assets_permanently_deleted_count": "Șters permanent {count, plural, one {# resursă} other {# resurse}}", "assets_removed_count": "Eliminat {count, plural, one {# resursă} other {# resurse}}", + "assets_removed_permanently_from_device": "{count} resursă(e) eliminate permanent din dispozitivul dvs.", "assets_restore_confirmation": "Ești sigur că vrei să restaurezi toate resursele tale din coșul de gunoi? Nu poți anula această acțiune! Ține minte că resursele offline nu se restaurează astfel.", "assets_restored_count": "Restaurat {count, plural, one {# resursă} other {# resurse}}", + "assets_restored_successfully": "{count} resursă(e) restaurate cu succes", + "assets_trashed": "{count} resursă(e) eliminate", "assets_trashed_count": "Mutat în coșul de gunoi {count, plural, one {# resursă} other {# resurse}}", + "assets_trashed_from_server": "{count} resursă(e) eliminate de pe serverul Immich", "assets_were_part_of_album_count": "{count, plural, one {Resursa era} other {Resursele erau}} deja parte din album", "authorized_devices": "Dispozitive Autorizate", + "automatic_endpoint_switching_subtitle": "Conectează-te local prin rețeaua Wi‐Fi configurată când este valabilă și prin rețele alternative în caz contrar", + "automatic_endpoint_switching_title": "Alternare URL automată", + "autoplay_slideshow": "Derulare slideshow automat", "back": "Înapoi", "back_close_deselect": "Înapoi, închidere sau deselectare", + "background_location_permission": "Permisiune locație în fundal", + "background_location_permission_content": "Pentru a putea schimba rețeaua activă în fundal, Immich are nevoie de acces *permanent* la locația precisă pentru a citi numele rețelei Wi-Fi", + "backup": "Backup", "backup_album_selection_page_albums_device": "Albume în dispozitiv ({count})", "backup_album_selection_page_albums_tap": "Apasă odata pentru a include, de două ori pentru a exclude", "backup_album_selection_page_assets_scatter": "Resursele pot fi împrăștiate în mai multe albume. Prin urmare, albumele pot fi incluse sau excluse în timpul procesului de backup.", @@ -474,6 +531,7 @@ "backup_controller_page_background_app_refresh_enable_button_text": "Mergi la setări", "backup_controller_page_background_battery_info_link": "Arată-mi cum", "backup_controller_page_background_battery_info_message": "Pentru cea mai bună experiență a backup-ului în fundal, te rugăm să dezactivezi orice optimizare pentru baterie care restricționează activitatea în fundal pentru Immich.\n\nDeoarece aceasta este specifică fiecărui dispozitiv, te rugăm verifică informațiile necesare tipului tău de dispozitiv.", + "backup_controller_page_background_battery_info_ok": "OK", "backup_controller_page_background_battery_info_title": "Optimizări baterie", "backup_controller_page_background_charging": "Doar în timpul încărcării", "backup_controller_page_background_configure_error": "Configurare serviciu în fundal eșuată", @@ -483,7 +541,8 @@ "backup_controller_page_background_is_on": "Backup-ul automat în fundal este activat", "backup_controller_page_background_turn_off": "Dezactivează serviciul în fundal", "backup_controller_page_background_turn_on": "Activează serviciul în fundal", - "backup_controller_page_background_wifi": "Doar conectat la WiFi", + "backup_controller_page_background_wifi": "Numai prin Wi-Fi", + "backup_controller_page_backup": "Backup", "backup_controller_page_backup_selected": "Selectat(e): ", "backup_controller_page_backup_sub": "S-a făcut backup pentru fotografii și videoclipuri", "backup_controller_page_created": "Creat la: {date}", @@ -491,6 +550,7 @@ "backup_controller_page_excluded": "Exclus(e): ", "backup_controller_page_failed": "Eșuate ({count})", "backup_controller_page_filename": "Nume fișier: {filename} [{size}]", + "backup_controller_page_id": "ID: {id}", "backup_controller_page_info": "Informații backup", "backup_controller_page_none_selected": "Nici o selecție", "backup_controller_page_remainder": "Rămas(e)", @@ -504,14 +564,22 @@ "backup_controller_page_total_sub": "Toate fotografiile și videoclipurile unice din albumele selectate", "backup_controller_page_turn_off": "Dezactivează backup-ul în prim-plan", "backup_controller_page_turn_on": "Activează backup-ul în prim-plan", - "backup_controller_page_uploading_file_info": "Încărcare informații fișier", + "backup_controller_page_uploading_file_info": "Informații încărcare fișier", "backup_err_only_album": "Nu poți șterge singurul album", "backup_info_card_assets": "resurse", "backup_manual_cancelled": "Anulat", "backup_manual_in_progress": "Încărcarea este deja în curs. Încearcă din nou mai târziu", "backup_manual_success": "Succes", "backup_manual_title": "Status încărcare", + "backup_options_page_title": "Opțiuni Backup", + "backup_setting_subtitle": "Schimbă opțiuni pentru backup în prim-plan și în fundal", "backward": "În sens invers", + "beta_sync": "Starea sincronizării Beta", + "beta_sync_subtitle": "Gestionați noul sistem de sincronizare", + "biometric_auth_enabled": "Autentificare biometrică activată", + "biometric_locked_out": "Sunteți blocați de la autentificare biometrică", + "biometric_no_options": "Nu sunt disponibile opțiuni biometrice", + "biometric_not_available": "Autentificarea biometrică nu este disponibilă pe acest dispozitiv", "birthdate_saved": "Data nașterii salvată cu succes", "birthdate_set_description": "Data nașterii este utilizată pentru a calcula vârsta acestei persoane la momentul realizării fotografiei.", "blurred_background": "Fundal neclar", @@ -525,7 +593,7 @@ "cache_settings_clear_cache_button": "Șterge cache", "cache_settings_clear_cache_button_title": "Șterge memoria cache a aplicatiei. Performanța aplicației va fi semnificativ afectată până când va fi reconstruită.", "cache_settings_duplicated_assets_clear_button": "ȘTERGE", - "cache_settings_duplicated_assets_subtitle": "Fotografii și videoclipuri care sunt pe lista neagră a aplicației", + "cache_settings_duplicated_assets_subtitle": "Fotografii și videoclipuri ignorate în lista aplicației", "cache_settings_duplicated_assets_title": "Resurse duplicate ({count})", "cache_settings_statistics_album": "Miniaturi pentru librării", "cache_settings_statistics_full": "Fotografii complete", @@ -541,14 +609,20 @@ "camera_model": "Model cameră", "cancel": "Anulați", "cancel_search": "Anulați căutarea", + "canceled": "Anulat", + "canceling": "Se anulează", "cannot_merge_people": "Nu se pot îmbina persoanele", "cannot_undo_this_action": "Nu puteți anula această acțiune!", "cannot_update_the_description": "Nu se poate actualiza descrierea", + "cast": "Partajare", + "cast_description": "Configurați destinațiile de difuzare disponibile", "change_date": "Schimbați data", + "change_description": "Schimbă descrierea", + "change_display_order": "Schimbați ordinea de afișare", "change_expiration_time": "Schimbați data expirare", "change_location": "Schimbați locația", "change_name": "Schimbați nume", - "change_name_successfully": "Schimbare nume cu succes", + "change_name_successfully": "Schimbare a numelui făcută cu succes", "change_password": "Schimbați parolă", "change_password_description": "Aceasta este fie prima dată când te conectezi în sistem, fie s-a făcut o solicitare pentru a schimba parola ta. Te rog să introduci noua parolă mai jos.", "change_password_form_confirm_password": "Confirmă parola", @@ -556,8 +630,12 @@ "change_password_form_new_password": "Parolă nouă", "change_password_form_password_mismatch": "Parolele nu se potrivesc", "change_password_form_reenter_new_password": "Reintrodu noua parolă", + "change_pin_code": "Schimbă codul PIN", "change_your_password": "Schimbă-ți parola", "changed_visibility_successfully": "Schimbare vizibilitate cu succes", + "check_corrupt_asset_backup": "Verifică copii de rezervă a resurselor corupte", + "check_corrupt_asset_backup_button": "Efectuează verificarea", + "check_corrupt_asset_backup_description": "Rulează această verificare doar prin Wi-Fi și doar după ce toate resursele au fost salvate în copia de rezerva. Procedura poate dura câteva minute.", "check_logs": "Verificați Jurnale", "choose_matching_people_to_merge": "Alegeți persoanele care se potrivesc pentru a le fuziona", "city": "Oraș", @@ -566,6 +644,14 @@ "clear_all_recent_searches": "Curățați toate căutările recente", "clear_message": "Ștergeți mesajul", "clear_value": "Ștergeți valoarea", + "client_cert_dialog_msg_confirm": "OK", + "client_cert_enter_password": "Introdu Parola", + "client_cert_import": "Importă", + "client_cert_import_success_msg": "Certificatul de client este importat", + "client_cert_invalid_msg": "Fisier cu certificat invalid sau parola este greșită", + "client_cert_remove_msg": "Certificatul de client este șters", + "client_cert_subtitle": "Acceptă doar formatul PKCS12 (.p12, .pfx). Importul/ștergerea certificatului este disponibil(ă) doar înainte de autentificare", + "client_cert_title": "Certificat SSL pentru client", "clockwise": "În sensul acelor de ceas", "close": "Închideți", "collapse": "Restrângeți", @@ -578,19 +664,27 @@ "comments_are_disabled": "Comentariile sunt dezactivate", "common_create_new_album": "Creează album nou", "common_server_error": "Te rugăm să verifici conexiunea la rețea, asigura-te că server-ul este accesibil și că versiunile aplicației/server-ului sunt compatibile.", + "completed": "Finalizat", "confirm": "Confirmați", "confirm_admin_password": "Confirmați Parola de Administrator", "confirm_delete_face": "Ești sigur ca vrei sa ștergi {name} din activ?", "confirm_delete_shared_link": "Sunteți sigur că doriți să ștergeți acest link partajat?", "confirm_keep_this_delete_others": "Toate celelalte active din stivă vor fi șterse, cu excepția acestui material. Sunteți sigur că doriți să continuați?", + "confirm_new_pin_code": "Confirmă noul cod PIN", "confirm_password": "Confirmați parola", + "confirm_tag_face": "Vrei să etichetezi această față ca {name}?", + "confirm_tag_face_unnamed": "Vrei să etichetezi această față?", + "connected_device": "Dispozitiv conectat", + "connected_to": "Conectat la", "contain": "Încadrează", + "context": "Context", "continue": "Continuați", "control_bottom_app_bar_create_new_album": "Creează album nou", "control_bottom_app_bar_delete_from_immich": "Șterge din Immich", "control_bottom_app_bar_delete_from_local": "Șterge din dispozitiv", "control_bottom_app_bar_edit_location": "Editează locație", "control_bottom_app_bar_edit_time": "Editează Data și Ora", + "control_bottom_app_bar_share_link": "Partajează linkul", "control_bottom_app_bar_share_to": "Distribuire către", "control_bottom_app_bar_trash_from_immich": "Mută în coș", "copied_image_to_clipboard": "Imagine copiată în clipboard.", @@ -612,6 +706,7 @@ "create_link": "Creează link", "create_link_to_share": "Creează link pentru a distribui", "create_link_to_share_description": "Permiteți oricui are link-ul să vadă fotografia (fotografiile) selectată(e)", + "create_new": "CREARE NOUĂ", "create_new_person": "Creați o persoană nouă", "create_new_person_hint": "Atribuiți resursele selectate unei persoane noi", "create_new_user": "Creează utilizator nou", @@ -621,14 +716,22 @@ "create_tag_description": "Creează o etichetă nouă. Pentru etichete imbricate, te rog să introduci calea completă a etichetei, inclusiv bare oblice (/).", "create_user": "Creează utilizator", "created": "Creat", + "created_at": "Creat", + "crop": "Decupează", "curated_object_page_title": "Obiecte", "current_device": "Dispozitiv curent", + "current_pin_code": "Codul PIN actual", + "current_server_address": "Adresa actuală a serverului", "custom_locale": "Setare Regională Personalizată", "custom_locale_description": "Formatați datele și numerele în funcție de limbă și regiune", + "daily_title_text_date": "E, MMM dd", + "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "Întunecat", + "dark_theme": "Comută tema întunecată", "date_after": "După data", "date_and_time": "Dată și oră", "date_before": "Anterior datei", + "date_format": "E, LLL d, y • h:mm a", "date_of_birth_saved": "Data nașterii salvată cu succes", "date_range": "Interval de date", "day": "Zi", @@ -640,6 +743,8 @@ "default_locale": "Setare Regională Implicită", "default_locale_description": "Formatați datele și numerele în funcție de regiunea browserului dvs", "delete": "Ștergere", + "delete_action_confirmation_message": "Sigur vrei să ștergi acest element? Această acțiune va muta elementul în coșul de gunoi al serverului și te va întreba dacă vrei să-l ștergi local", + "delete_action_prompt": "{count} șterse", "delete_album": "Ștergere album", "delete_api_key_prompt": "Sunteți sigur că doriți să ștergeți această cheie API?", "delete_dialog_alert": "Aceste elemente vor fi șterse permanent de pe server-ul Immich și din dispozitivul tău", @@ -653,9 +758,12 @@ "delete_key": "Ștergere cheie", "delete_library": "Ștergere biblioteca", "delete_link": "Ștergere link", + "delete_local_action_prompt": "{count} șterse local", "delete_local_dialog_ok_backed_up_only": "Șterge doar fișierele pentru care s-a făcut backup", "delete_local_dialog_ok_force": "Șterge oricum", "delete_others": "Ștergeți celelalte", + "delete_permanently": "Șterge permanent", + "delete_permanently_action_prompt": "{count} șterse permanent", "delete_shared_link": "Ștergere link partajat", "delete_shared_link_dialog_title": "Șterge link distribuire", "delete_tag": "Ștergere etichetă", @@ -666,12 +774,14 @@ "description": "Descriere", "description_input_hint_text": "Adaugă descriere...", "description_input_submit_error": "Eroare actualizare descriere, verifică log-urile pentru mai multe detalii", + "deselect_all": "Deselectează toate", "details": "Detalii", "direction": "Direcție", "disabled": "Dezactivat", "disallow_edits": "Interzice modificările", "discord": "Server Discord", "discover": "Descoperiți", + "discovered_devices": "Dispozititve descoperite", "dismiss_all_errors": "Ignorați toate erorile", "dismiss_error": "Ignorați eroarea", "display_options": "Opțiuni de afișare", @@ -682,12 +792,26 @@ "documentation": "Documentație", "done": "Gata", "download": "Descărcați", + "download_action_prompt": "Se descarcă {count} elemente", + "download_canceled": "Descărcare anulată", + "download_complete": "Descărcare completă", + "download_enqueue": "Descărcare în coadă", + "download_error": "Eroare de descărcare", + "download_failed": "Descărcare eșuată", + "download_finished": "Descărcare finalizată", "download_include_embedded_motion_videos": "Videoclipuri încorporate", "download_include_embedded_motion_videos_description": "Include videoclipurile încorporate în fotografiile în mișcare ca fișier separat", + "download_notfound": "Descărcare negăsită", + "download_paused": "Descărcarea a fost întreruptă", "download_settings": "Descărcați", "download_settings_description": "Gestionați setările legate de descărcarea resurselor", + "download_started": "Descărcarea a început", + "download_sucess": "Descărcare reușită", + "download_sucess_android": "Fișierul media a fost descărcat în DCIM/Immich", + "download_waiting_to_retry": "Se așteaptă o nouă încercare", "downloading": "Se descarcă", "downloading_asset_filename": "Se descarcă resursa {filename}", + "downloading_media": "Se descarcă fișierele media", "drop_files_to_upload": "Trageți fișierele aici pentru a le încărca", "duplicates": "Duplicate", "duplicates_description": "Rezolvați fiecare grup indicând care sunt duplicate, dacă există", @@ -697,6 +821,8 @@ "edit_avatar": "Editare avatar", "edit_date": "Editare dată", "edit_date_and_time": "Editare dată și oră", + "edit_description": "Editează descrierea", + "edit_description_prompt": "Vă rugăm să selectați o descriere nouă:", "edit_exclusion_pattern": "Editarea modelului de excludere", "edit_faces": "Editare fețe", "edit_import_path": "Editare cale de import", @@ -704,6 +830,7 @@ "edit_key": "Tastă de editare", "edit_link": "Editare link", "edit_location": "Editare locație", + "edit_location_action_prompt": "{count} locație(i) editată(e)", "edit_location_dialog_title": "Locație", "edit_name": "Editare nume", "edit_people": "Editare persoane", @@ -711,19 +838,31 @@ "edit_title": "Editare Titlu", "edit_user": "Editare utilizator", "edited": "Editat", + "editor": "Editor", "editor_close_without_save_prompt": "Schimbările nu vor fi salvate", "editor_close_without_save_title": "Închideți editorul?", "editor_crop_tool_h2_aspect_ratios": "Raporturi de aspect", "editor_crop_tool_h2_rotation": "Rotire", + "email": "Adresă de mail", + "email_notifications": "Notificări e-mail", + "empty_folder": "Acest dosar este gol", "empty_trash": "Goliți coșul de gunoi", "empty_trash_confirmation": "Sunteți sigur că doriți să goliți coșul de gunoi? Acest lucru va elimina definitiv din Immich toate resursele din coșul de gunoi.\nNu puteți anula această acțiune!", "enable": "Permite", + "enable_backup": "Activează backup", + "enable_biometric_auth_description": "Introduceți codul PIN pentru a activa autentificarea biometrică", "enabled": "Activat", "end_date": "Data de încheiere", - "enter_wifi_name": "Enter WiFi name", + "enqueued": "Pus în coadă", + "enter_wifi_name": "Introduceți numele rețelei Wi-Fi", + "enter_your_pin_code": "Introduceți codul PIN", + "enter_your_pin_code_subtitle": "Introduceți codul PIN pentru a accesa folderul blocat", "error": "Eroare", + "error_change_sort_album": "Nu s-a putut modifica ordinea de sortare a albumului", "error_delete_face": "Eroare la ștergerea feței din activ", "error_loading_image": "Eroare la încărcarea imaginii", + "error_saving_image": "Eroare: {error}", + "error_tag_face_bounding_box": "Eroare la etichetarea feței - nu se pot obține coordonatele casetei de delimitare", "error_title": "Eroare - ceva nu a mers", "errors": { "cannot_navigate_next_asset": "Nu se poate naviga către următoarea resursă", @@ -751,10 +890,12 @@ "failed_to_keep_this_delete_others": "Nu s-a putut păstra acest material respectiv nu s-au putut șterge celelalte materiale", "failed_to_load_asset": "Eșec la încărcarea resursei", "failed_to_load_assets": "Eșec la încărcarea resurselor", + "failed_to_load_notifications": "Nu s-au putut încărca notificările", "failed_to_load_people": "Eșec la încărcarea persoanelor", "failed_to_remove_product_key": "Eșec la eliminarea cheii de produs", "failed_to_stack_assets": "Eșec la combinarea resurselor", "failed_to_unstack_assets": "Eșec la desfășurarea resurselor", + "failed_to_update_notification_status": "Nu s-a putut actualiza starea notificării", "import_path_already_exists": "Această cale de import există deja.", "incorrect_email_or_password": "E-mail sau parolă incorect/ă", "paths_validation_failed": "{paths, plural, one {# cale} other {# căi}} nu a trecut validarea", @@ -771,6 +912,7 @@ "unable_to_archive_unarchive": "Nu se poate {archived, select, true {arhiva} other {dezarhiva}}", "unable_to_change_album_user_role": "Nu se poate schimba rolul utilizatorului de album", "unable_to_change_date": "Imposibil de schimbat data", + "unable_to_change_description": "Nu se poate schimba descrierea", "unable_to_change_favorite": "Nu se pot modifica favoritele pentru resursa", "unable_to_change_location": "Imposibil de schimbat locația", "unable_to_change_password": "Imposibil de schimbat parola", @@ -814,6 +956,7 @@ "unable_to_remove_partner": "Imposibil de eliminat partenerul", "unable_to_remove_reaction": "Nu se poate elimina reacția", "unable_to_reset_password": "Imposibil de resetat parola", + "unable_to_reset_pin_code": "Nu se poate reseta codul PIN", "unable_to_resolve_duplicate": "Nu se poate rezolva duplicatul", "unable_to_restore_assets": "Nu se pot restaura resursele", "unable_to_restore_trash": "Nu se poate restaura coșul de gunoi", @@ -846,11 +989,16 @@ "exif_bottom_sheet_details": "DETALII", "exif_bottom_sheet_location": "LOCAȚIE", "exif_bottom_sheet_people": "PERSOANE", + "exif_bottom_sheet_person_add_person": "Adăugați nume", + "exif_bottom_sheet_person_age_months": "Vârstă {months} luni", + "exif_bottom_sheet_person_age_year_months": "Vârstă 1 an, {months} luni", + "exif_bottom_sheet_person_age_years": "Vârstă {years}", "exit_slideshow": "Ieșire din Prezentare", "expand_all": "Extindeți-le pe toate", "experimental_settings_new_asset_list_subtitle": "Acțiune în desfășurare", "experimental_settings_new_asset_list_title": "Activează grila experimentală de fotografii", "experimental_settings_subtitle": "Folosește pe propria răspundere!", + "experimental_settings_title": "Experimental", "expire_after": "Expiră după", "expired": "Expirat", "expires_date": "Expiră la {date}", @@ -858,13 +1006,20 @@ "explorer": "Explorator", "export": "Exportare", "export_as_json": "Exportare ca JSON", + "export_database": "Exportați baza de date", + "export_database_description": "Exportați baza de date SQLite", "extension": "Extensie", "external": "Extern", "external_libraries": "Biblioteci Externe", - "external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom", + "external_network": "Rețea externă", + "external_network_sheet_info": "Când nu se află în rețeaua Wi-Fi preferată, aplicația se va conecta la server prin prima dintre adresele URL de mai jos pe care o poate accesa, începând de sus în jos", "face_unassigned": "Nealocat", + "failed": "Eșuat", + "failed_to_authenticate": "Autentificarea nu a reușit", "failed_to_load_assets": "Nu s-au încărcat activele", + "failed_to_load_folder": "Nu s-a putut încărca folderul", "favorite": "Favorit", + "favorite_action_prompt": "{count} adăugate la Favorite", "favorite_or_unfavorite_photo": "Fotografie preferată sau nepreferată", "favorites": "Favorite", "favorites_page_no_favorites": "Nu au fost găsite resurse favorite", @@ -875,25 +1030,41 @@ "file_name_or_extension": "Numele sau extensia fișierului", "filename": "Numele fișierului", "filetype": "Tipul fișierului", + "filter": "Filtre", "filter_people": "Filtrați persoanele", "filter_places": "Filtrează locurile", "find_them_fast": "Găsiți-le rapid prin căutare după nume", "fix_incorrect_match": "Remediați potrivirea incorectă", + "folder": "Dosar", + "folder_not_found": "Dosar negăsit", "folders": "Foldere", "folders_feature_description": "Răsfoire în conținutul folderului pentru fotografiile și videoclipurile din sistemul de fișiere", "forward": "Redirecționare", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "Această funcție încarcă resurse externe de la Google pentru a funcționa.", + "general": "General", "get_help": "Obțineți Ajutor", + "get_wifiname_error": "Nu s-a putut obține numele rețelei Wi-Fi. Asigurați-vă că ați acordat permisiunile necesare și că sunteți conectat la o rețea Wi-Fi", "getting_started": "Noțiuni de Bază", "go_back": "Întoarcere", "go_to_folder": "Accesați folderul", "go_to_search": "Spre căutare", + "grant_permission": "Acordați permisiunea", "group_albums_by": "Grupați albume de...", "group_country": "Grupare după țară", "group_no": "Fără grupare", "group_owner": "Grupați după proprietar", "group_places_by": "Grupare locuri după...", "group_year": "Grupați după an", + "haptic_feedback_switch": "Activează feedback-ul haptic", + "haptic_feedback_title": "Feedback haptic", "has_quota": "Are spațiu de stocare", + "header_settings_add_header_tip": "Adăugați antet", + "header_settings_field_validator_msg": "Valoarea nu poate fi goală", + "header_settings_header_name_input": "Numele antetului", + "header_settings_header_value_input": "Valoarea antetului", + "headers_settings_tile_subtitle": "Definiți header-urile proxy pe care aplicația ar trebui să le trimită cu fiecare solicitare de rețea", + "headers_settings_tile_title": "Header-uri proxy personalizate", "hi_user": "Bună {name} ({email})", "hide_all_people": "Ascundeți toate persoanele", "hide_gallery": "Ascundeți galeria", @@ -913,10 +1084,16 @@ "home_page_favorite_err_local": "Resursele locale nu pot fi adăugate la favorite încă, omitere", "home_page_favorite_err_partner": "Momentan nu se pot adăuga fișierele partenerului la favorite, omitere", "home_page_first_time_notice": "Dacă este prima dată când utilizezi aplicația, te rugăm să te asiguri că alegi unul sau mai multe albume de backup, astfel încât cronologia să poată fi populată cu fotografiile și videoclipurile din aceste albume", + "home_page_locked_error_local": "Nu se pot muta resursele locale în folderul blocat, se omit", + "home_page_locked_error_partner": "Nu se pot muta materialele partenerului în folderul blocat, se omit.", "home_page_share_err_local": "Nu se pot distribui fișiere locale prin link, omitere", "home_page_upload_err_limit": "Se pot încărca maxim 30 de resurse odată, omitere", "host": "Gazdă", "hour": "Oră", + "id": "ID", + "idle": "Inactiv", + "ignore_icloud_photos": "Ignoră fotografiile din iCloud", + "ignore_icloud_photos_description": "Fotografiile stocate pe iCloud nu vor fi încărcate pe serverul Immich", "image": "Imagine", "image_alt_text_date": "{isVideo, select, true {Video} other {imagine}} preluată în {date}", "image_alt_text_date_1_person": "{isVideo, select, true {Video} other {imagine}} preluată cu {person1} în {date}", @@ -928,6 +1105,8 @@ "image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {imagine}} preluată în {city}, {country} cu {person1} și {person2} în {date}", "image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {imagine}} preluată în {city}, {country} cu {person1}, {person2}, și {person3} în {date}", "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {imagine}} preluată în {city}, {country} cu {person1}, {person2}, și {additionalCount, number} alții în {date}", + "image_saved_successfully": "Imaginea a fost salvată", + "image_viewer_page_state_provider_download_started": "Descărcare începută", "image_viewer_page_state_provider_download_success": "Descărcare cu succes", "image_viewer_page_state_provider_share_error": "Eroare distribuire", "immich_logo": "Logo Immich", @@ -948,8 +1127,15 @@ "night_at_midnight": "În fiecare noapte la miezul nopții", "night_at_twoam": "În fiecare noapte la 2 dimineața" }, + "invalid_date": "Dată invalidă", + "invalid_date_format": "Format de dată invalid", "invite_people": "Invitați Persoane", "invite_to_album": "Invitați în album", + "ios_debug_info_fetch_ran_at": "Fetch a funcționat la {dateTime}", + "ios_debug_info_last_sync_at": "Ultima sincronizare {dateTime}", + "ios_debug_info_no_processes_queued": "Niciun proces în fundal pus în coadă", + "ios_debug_info_no_sync_yet": "Nicio sarcină de sincronizare în fundal nu a fost încă executată", + "ios_debug_info_processes_queued": "{count, plural, one {{count} proces în fundal pus în coadă} other {{count} procese în fundal puse în coadă}}", "ios_debug_info_processing_ran_at": "Procesarea a rulat {dateTime}", "items_count": "{count, plural, one {# element} other{# elemente}}", "jobs": "Sarcini", @@ -959,6 +1145,9 @@ "kept_this_deleted_others": "S-a păstrat acest material și s-au șters {count, plural, one {# material} other {# materiale}}", "keyboard_shortcuts": "Comenzi rapide de tastatură", "language": "Limbă", + "language_no_results_subtitle": "Încercați să ajustați termenul de căutare", + "language_no_results_title": "Nu au fost găsite limbi", + "language_search_hint": "Căutați limbi...", "language_setting_description": "Selectați limba preferată", "last_seen": "Văzut ultima dată", "latest_version": "Ultima Versiune", @@ -975,23 +1164,32 @@ "library_page_sort_created": "Data creării", "library_page_sort_last_modified": "Ultima dată modificat", "library_page_sort_title": "Titlu album", + "licenses": "Licențe", "light": "Lumină", "like_deleted": "Preferat șters", "link_motion_video": "Link video în mișcare", - "link_options": "Opțiuni de link", "link_to_oauth": "Link către OAuth", "linked_oauth_account": "Cont OAuth conectat", "list": "Listă", "loading": "Încărcare", "loading_search_results_failed": "Încărcarea rezultatelor căutării nu a reușit", - "location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current WiFi network's name", + "local": "Local", + "local_asset_cast_failed": "Nu se poate converti un element care nu este încărcat pe server", + "local_assets": "Asset-uri locale", + "local_network": "Rețea locală", + "local_network_sheet_info": "Aplicația se va conecta la server prin intermediul acestei adrese URL atunci când utilizează rețeaua Wi-Fi specificată", + "location_permission": "Permisiunea de locație", + "location_permission_content": "Pentru a utiliza funcția de comutare automată, Immich are nevoie de permisiune pentru locația precisă, astfel încât să poată citi numele rețelei Wi-Fi curente", "location_picker_choose_on_map": "Alege pe hartă", "location_picker_latitude_error": "Introdu o latitudine validă", "location_picker_latitude_hint": "Introdu latitudinea aici", "location_picker_longitude_error": "Introdu o longitudine validă", "location_picker_longitude_hint": "Introdu longitudinea aici", + "lock": "Blocare", + "locked_folder": "Dosar blocat", "log_out": "Deconectare", "log_out_all_devices": "Deconectați-vă de la toate dispozitivele", + "logged_in_as": "Conectat ca {user}", "logged_out_all_devices": "S-au deconectat toate dispozitivele", "logged_out_device": "Dispozitiv deconectat", "login": "Conectare", @@ -1034,8 +1232,7 @@ "manage_your_devices": "Gestionați-vă dispozitivele conectate", "manage_your_oauth_connection": "Gestionați-vă conexiunea OAuth", "map": "Hartă", - "map_assets_in_bound": "{count} fotografie", - "map_assets_in_bounds": "{count} fotografii", + "map_assets_in_bounds": "{count, plural, one {# poză} other {# poze}}", "map_cannot_get_user_location": "Nu se poate obține locația utilizatorului", "map_location_dialog_yes": "Da", "map_location_picker_page_use_location": "Folosește această locație", @@ -1054,13 +1251,21 @@ "map_settings_date_range_option_years": "Ultimii {years} ani", "map_settings_dialog_title": "Setările hărții", "map_settings_include_show_archived": "Include resursele arhivate", + "map_settings_include_show_partners": "Includeți partenerii", "map_settings_only_show_favorites": "Arată doar favorite", "map_settings_theme_settings": "Stilul hărții", "map_zoom_to_see_photos": "Zoom out pentru a vedea fotografii", + "mark_all_as_read": "Marchează toate ca citite", + "mark_as_read": "Marchează ca citit", + "marked_all_as_read": "Marcate toate ca citite", "matches": "Corespunde", "media_type": "Tip media", "memories": "Amintiri", + "memories_all_caught_up": "Sunteți la zi", + "memories_check_back_tomorrow": "Reveniți mâine pentru mai multe amintiri", "memories_setting_description": "Administrați ce vedeți în amintiri", + "memories_start_over": "Începeți de la început", + "memories_swipe_to_close": "Glisează în sus pentru a închide", "memory": "Amintire", "memory_lane_title": "Banda Memoriei {title}", "menu": "Meniu", @@ -1071,9 +1276,19 @@ "merge_people_successfully": "Persoane îmbinate cu succes", "merged_people_count": "Imbinate {count, plural, one {# persoană} other {# persoane}}", "minimize": "Minimizare", + "minute": "Minute", "missing": "Lipsă", + "model": "Model", "month": "Lună", + "monthly_title_text_date_format": "MMMM y", "more": "Mai mult", + "move": "Mută", + "move_off_locked_folder": "Mutați din folderul blocat", + "move_to_lock_folder_action_prompt": "{count} adăugate în dosarul blocat", + "move_to_locked_folder": "Mută în dosarul blocat", + "move_to_locked_folder_confirmation": "Aceste fotografii și videoclipuri vor fi eliminate din toate albumele și vor putea fi vizualizate doar din dosarul blocat", + "moved_to_archive": "Au fost mutate {count, plural, one {# element} other {# elemente}} în arhivă", + "moved_to_library": "Au fost mutate {count, plural, one {# element} other {# elemente}} la bibliotecă", "moved_to_trash": "Mutat în coșul de gunoi", "multiselect_grid_edit_date_time_err_read_only": "Nu se poate edita data fișierului(lor) cu permisiuni doar pentru citire, omitere", "multiselect_grid_edit_gps_err_read_only": "Nu se poate edita locația fișierului(lor) cu permisiuni doar pentru citire, omitere", @@ -1081,11 +1296,15 @@ "my_albums": "Albumele mele", "name": "Nume", "name_or_nickname": "Nume sau poreclǎ", + "networking_settings": "Rețele", + "networking_subtitle": "Gestionați setările endpoint-ului serverului", "never": "Niciodată", "new_album": "Album Nou", "new_api_key": "Cheie API nouǎ", "new_password": "Parolă nouă", "new_person": "Persoanǎ nouǎ", + "new_pin_code": "Cod PIN nou", + "new_pin_code_subtitle": "Aceasta este prima dată când accesați folderul blocat. Creați un cod PIN pentru a accesa în siguranță această pagină", "new_user_created": "Utilizator nou creat", "new_version_available": "VERSIUNE NOUĂ DISPONIBILĂ", "newest_first": "Cel mai nou primul", @@ -1097,19 +1316,27 @@ "no_albums_yet": "Se pare că nu aveți încă niciun album.", "no_archived_assets_message": "Arhivați fotografii și videoclipuri pentru a le ascunde din vizualizarea fotografii", "no_assets_message": "CLICK PENTRU A ÎNCĂRCA PRIMA TA FOTOGRAFIE", + "no_assets_to_show": "Nicio resursă de afișat", + "no_cast_devices_found": "Nu s-au găsit dispozitive de difuzare", "no_duplicates_found": "Nu au fost găsite duplicate.", "no_exif_info_available": "Nu există informații exif disponibile", "no_explore_results_message": "Încarcați mai multe fotografii pentru a vă explora colecția.", "no_favorites_message": "Adăugați favorite pentru a găsi rapid cele mai bune fotografii și videoclipuri", "no_libraries_message": "Creați o bibliotecă externă pentru a vă vizualiza fotografiile și videoclipurile", + "no_locked_photos_message": "Fotografiile și videoclipurile din folderul blocat sunt ascunse și nu vor apărea atunci când răsfoiți sau căutați în bibliotecă.", "no_name": "Fără Nume", + "no_notifications": "Nicio notificare", + "no_people_found": "Nu au fost găsite persoane potrivite căutării", "no_places": "Nu există locuri", "no_results": "Fără rezultate", "no_results_description": "Încercați un sinonim sau un cuvânt cheie mai general", "no_shared_albums_message": "Creați un album pentru a partaja fotografii și videoclipuri cu persoanele din rețeaua dvs", + "no_uploads_in_progress": "Nicio încărcare în curs", "not_in_any_album": "Nu există în niciun album", + "not_selected": "Neselectat", "note_apply_storage_label_to_previously_uploaded assets": "Notă: Pentru a aplica eticheta de stocare la resursele încărcate anterior, rulați", "notes": "Note", + "nothing_here_yet": "Nimic aici încă", "notification_permission_dialog_content": "Pentru a activa notificările, mergi în Setări > Immich și selectează permite.", "notification_permission_list_tile_content": "Acordă permisiunea pentru a activa notificările.", "notification_permission_list_tile_enable_button": "Activează notificările", @@ -1117,13 +1344,20 @@ "notification_toggle_setting_description": "Activați notificările prin email", "notifications": "Notificări", "notifications_setting_description": "Gestionați notificările", + "oauth": "OAuth", "official_immich_resources": "Resurse Oficiale Immich", + "offline": "Offline", "ok": "Bine", "oldest_first": "Cel mai vechi mai întâi", + "on_this_device": "Pe acest dispozitiv", "onboarding": "Integrare", - "onboarding_privacy_description": "Următoarele caracteristici (opționale) se bazează pe servicii externe și pot fi dezactivate în orice moment din setările de administrare.", + "onboarding_locale_description": "Selectați limba preferată. Puteți schimba această opțiune ulterior în setări.", + "onboarding_privacy_description": "Următoarele caracteristici (opționale) se bazează pe servicii externe și pot fi dezactivate în orice moment din setări.", + "onboarding_server_welcome_description": "Hai să configurăm instanța cu câteva setări comune.", "onboarding_theme_description": "Alegeți o temă de culoare pentru exemplul dvs. Puteți modifica acest lucru mai târziu în setări.", + "onboarding_user_welcome_description": "Hai să începem!", "onboarding_welcome_user": "Bun venit, {user}", + "online": "Online", "only_favorites": "Doar favorite", "open": "Deschide", "open_in_map_view": "Deschideți în vizualizarea hărții", @@ -1132,8 +1366,10 @@ "options": "Opțiuni", "or": "sau", "organize_your_library": "Organizează-ți biblioteca", + "original": "original", "other": "Alte", "other_devices": "Alte dispozitive", + "other_entities": "Alte entități", "other_variables": "Alte variabile", "owned": "Deținut", "owner": "Proprietar", @@ -1141,6 +1377,8 @@ "partner_can_access": "{partner} poate accesa", "partner_can_access_assets": "Toate fotografiile și videoclipurile tale, cu excepția celor din arhivate și sterse", "partner_can_access_location": "Locația în care au fost făcute fotografiile dvs", + "partner_list_user_photos": "Fotografiile lui {user}", + "partner_list_view_all": "Vezi toate", "partner_page_empty_message": "Fotografiile tale nu sunt încă distribuite cu nici un partener.", "partner_page_no_more_users": "Nu mai sunt utilizatori de adăugat", "partner_page_partner_add_failed": "Eșuare adăugare partener", @@ -1175,6 +1413,8 @@ "permanently_delete_assets_prompt": "Sigur doriți să ștergeți definitiv {count, plural, one {această resursă?} other {aceste # resurse?}} Acest lucru va elimina și {count, plural, one {din ea} other {din ele}} album(e).", "permanently_deleted_asset": "Resursă ștearsă definitiv", "permanently_deleted_assets_count": "S-au șters definitiv {count, plural, one {# resursă} other {# resurse}}", + "permission": "Permisiune", + "permission_empty": "Permisiunea dvs. nu trebuie să fie goală", "permission_onboarding_back": "Înapoi", "permission_onboarding_continue_anyway": "Continuă oricum", "permission_onboarding_get_started": "Începe", @@ -1192,6 +1432,10 @@ "photos_count": "{count, plural, one {{count, number} imagine} other{{count, number} imagini}}", "photos_from_previous_years": "Fotografii din anii anteriori", "pick_a_location": "Alegeți o locație", + "pin_code_changed_successfully": "Codul PIN a fost modificat cu succes", + "pin_code_reset_successfully": "Codul PIN a fost resetat cu succes", + "pin_code_setup_successfully": "Configurarea cu succes a unui cod PIN", + "pin_verification": "Verificarea codului PIN", "place": "Loc", "places": "Locații", "places_count": "{count, plural, one {{count, number} Loc} other {{count, number} Locuri}}", @@ -1199,18 +1443,27 @@ "play_memories": "Redare amintiri", "play_motion_photo": "Redare Fotografie în Mișcare", "play_or_pause_video": "Redați sau întrerupeți videoclipul", + "please_auth_to_access": "Vă rugăm să vă autentificați pentru a accesa", + "port": "Port", + "preferences_settings_subtitle": "Gestionați preferințele aplicației", + "preferences_settings_title": "Preferințe", "preset": "Presetat", "preview": "Previzualizare", "previous": "Anterior", "previous_memory": "Memoria anterioară", + "previous_or_next_day": "Zi înainte/înapoi", + "previous_or_next_month": "Lună înainte/înapoi", "previous_or_next_photo": "Fotografie înainte/înapoi", + "previous_or_next_year": "An înainte/înapoi", "primary": "Primar", "privacy": "Confidențialitate", + "profile": "Profil", "profile_drawer_app_logs": "Log-uri", - "profile_drawer_client_out_of_date_major": "Aplicația nu folosește ultima versiune. Te rugăm să actulizezi la ultima versiune majoră.", - "profile_drawer_client_out_of_date_minor": "Aplicația nu folosește ultima versiune. Te rugăm să actulizezi la ultima versiune minoră.", + "profile_drawer_client_out_of_date_major": "Aplicația nu folosește ultima versiune. Te rugăm să actualizezi la ultima versiune majoră.", + "profile_drawer_client_out_of_date_minor": "Aplicația nu folosește ultima versiune. Te rugăm să actualizezi la ultima versiune minoră.", "profile_drawer_client_server_up_to_date": "Aplicația client și server-ul sunt actualizate", - "profile_drawer_server_out_of_date_major": "Server-ul nu folosește ultima versiune. Te rugăm să actulizezi la ultima versiune majoră.", + "profile_drawer_github": "GitHub", + "profile_drawer_server_out_of_date_major": "Server-ul nu folosește ultima versiune. Te rugăm să actualizezi la ultima versiune majoră.", "profile_drawer_server_out_of_date_minor": "Server-ul nu folosește ultima versiune. Te rugăm să actulizezi la ultima versiune minoră.", "profile_image_of_user": "Imagine de profil a lui {user}", "profile_picture_set": "Poză de profil setată.", @@ -1230,22 +1483,27 @@ "purchase_failed_activation": "Activare eșuată! Vă rugăm să vă verificați e-mailul pentru cheia de produs corectă!", "purchase_individual_description_1": "Pentru un individ", "purchase_individual_description_2": "Statutul de suporter", + "purchase_individual_title": "Individual", "purchase_input_suggestion": "Aveți o cheie de produs? Introduceți cheia mai jos", "purchase_license_subtitle": "Cumpărați Immich pentru a sprijini dezvoltarea continuă a serviciului", "purchase_lifetime_description": "Achiziție pe viață", "purchase_option_title": "OPȚIUNI DE CUMPĂRARE", - "purchase_panel_info_1": "Dezvoltarea Immich necesită mult timp și efort și avem ingineri cu normă întreagă care lucrează la ea pentru a o face cât se poate de bună. Misiunea noastră este ca software-ul open-source și practicile de afaceri etice să devină o sursă de venit durabilă pentru dezvoltatori și să se creeze un ecosistem care să respecte confidențialitatea, cu alternative reale la serviciile cloud care exploatează.", - "purchase_panel_info_2": "Deoarece ne-am angajat să nu adăugăm planuri de plată, această achiziție nu vă va oferi nicio funcție suplimentară în Immich. Ne bazăm pe utilizatori ca dvs. pentru a sprijini dezvoltarea continuă a lui Immich.", + "purchase_panel_info_1": "Dezvoltarea programului Immich necesită mult timp și efort și avem ingineri cu normă întreagă care lucrează la el pentru a-l face cât se poate de bun. Misiunea noastră este ca software-ul open-source și practicile de afaceri etice să devină o sursă de venit durabilă pentru dezvoltatori și să se creeze un ecosistem care să respecte confidențialitatea utilizatorilor, cu alternative reale la serviciile cloud care exploatează utilizatorii.", + "purchase_panel_info_2": "Deoarece ne-am angajat să nu adăugăm planuri de plată, această achiziție nu vă va oferi nicio funcție suplimentară în Immich. Ne bazăm pe utilizatori ca dvs. pentru a sprijini dezvoltarea continuă a Immich.", "purchase_panel_title": "Susțineți proiectul", + "purchase_per_server": "Per server", + "purchase_per_user": "Per utilizator", "purchase_remove_product_key": "Eliminați Cheia Produsului", "purchase_remove_product_key_prompt": "Sigur doriți să eliminați cheia de produs?", "purchase_remove_server_product_key": "Eliminați cheia de produs a Serverului", "purchase_remove_server_product_key_prompt": "Sigur doriți să eliminați cheia de produs a Serverului?", "purchase_server_description_1": "Pentru tot serverul", "purchase_server_description_2": "Statutul de suporter", + "purchase_server_title": "Server", "purchase_settings_server_activated": "Cheia de produs a serverului este gestionată de administrator", + "queue_status": "Se pun în coadă {count}/{total}", "rating": "Evaluare cu stele", - "rating_clear": "Anulați evaluare", + "rating_clear": "Anulați evaluarea", "rating_count": "{count, plural, one {# stea} other {# stele}}", "rating_description": "Afișați evaluarea EXIF în panoul de informații", "reaction_options": "Opțiuni de reacție", @@ -1254,20 +1512,26 @@ "reassigned_assets_to_existing_person": "Re-alocat {count, plural, one {# resursă} other {# resurse}} to {name, select, null {unei persoane existente} other {{name}}}", "reassigned_assets_to_new_person": "Re-alocat {count, plural, one {# resursă} other {# resurse}} unei noi persoane", "reassing_hint": "Atribuiți resursele selectate unei persoane existente", + "recent": "Recent", "recent-albums": "Albume recente", "recent_searches": "Căutări recente", + "recently_added": "Adăugate recent", "recently_added_page_title": "Adăugate recent", + "recently_taken": "Recent realizate", + "recently_taken_page_title": "Recent realizate", "refresh": "Reîmprospătare", - "refresh_encoded_videos": "Actualizează videoclipurile codificate", + "refresh_encoded_videos": "Actualizează videoclipurile encodate", "refresh_faces": "Reîmprospătați fețele", "refresh_metadata": "Actualizați metadatele", "refresh_thumbnails": "Reîmprospătați miniaturile", "refreshed": "Reîmprospătat", "refreshes_every_file": "Recitește toate fișierele existente și noi", - "refreshing_encoded_video": "Se reîmprospătează videoclipul codificat", + "refreshing_encoded_video": "Se reîmprospătează videoclipul encodat", "refreshing_faces": "Se reîmprospătează fețele", "refreshing_metadata": "Se reîmprospătează metadatele", "regenerating_thumbnails": "Se regenerează miniaturile", + "remote": "De la distanță", + "remote_assets": "Elemente la distanță", "remove": "Eliminați", "remove_assets_album_confirmation": "Sigur doriți să eliminați {count, plural, one {# resursă} other {# resurse}} din album?", "remove_assets_shared_link_confirmation": "Sigur doriți să eliminați {count, plural, one {# resursă} other {# resurse}} din acest link comun?", @@ -1275,10 +1539,15 @@ "remove_custom_date_range": "Eliminați intervalul de date personalizat", "remove_deleted_assets": "Eliminați Resursele Șterse", "remove_from_album": "Ștergeți din album", + "remove_from_album_action_prompt": "{count} șters(e) din album", "remove_from_favorites": "Eliminați din favorite", + "remove_from_lock_folder_action_prompt": "{count} șters(e) din dosarul blocat", + "remove_from_locked_folder": "Eliminați din folderul securizat", + "remove_from_locked_folder_confirmation": "Sunteți sigur că doriți să mutați aceste poze și videoclipuri afară din folderul securizat? Vor deveni vizibile în biblioteca dvs.", "remove_from_shared_link": "Eliminați din linkul partajat", "remove_memory": "Șterge amintirea", "remove_photo_from_memory": "Șterge fotografia din această amintire", + "remove_tag": "Eliminați ticheta", "remove_url": "Eliminați adresa URL", "remove_user": "Eliminați utilizatorul", "removed_api_key": "Cheie API eliminată: {name}", @@ -1299,19 +1568,27 @@ "reset": "Resetare", "reset_password": "Resetare parolă", "reset_people_visibility": "Resetați vizibilitatea persoanelor", + "reset_pin_code": "Resetare cod PIN", + "reset_sqlite": "Resetare bază de date SQLite", + "reset_sqlite_confirmation": "Sigur doriți să resetați baza de date SQLite? Va trebui să vă deconectați și să vă conectați din nou pentru a resincroniza datele", + "reset_sqlite_success": "Resetarea cu succes a bazei de date SQLite", "reset_to_default": "Resetați la valoarea implicită", "resolve_duplicates": "Rezolvați duplicatele", "resolved_all_duplicates": "Rezolvați toate duplicatele", "restore": "Restaurați", "restore_all": "Restaurați toate", + "restore_trash_action_prompt": "{count} restaurate din gunoi", "restore_user": "Restabiliți utilizatorul", "restored_asset": "Resursă restaurată", "resume": "Reluare", "retry_upload": "Reîncercați încărcarea", "review_duplicates": "Examinați duplicatele", "role": "Rol", + "role_editor": "Editor", "role_viewer": "Vizualizator", + "running": "Rulează", "save": "Salvați", + "save_to_gallery": "Salvați în galerie", "saved_api_key": "Cheie API salvată", "saved_profile": "Profil salvat", "saved_settings": "Setări salvate", @@ -1332,16 +1609,32 @@ "search_camera_model": "Se caută modelul camerei...", "search_city": "Se caută orașul...", "search_country": "Se caută țara...", + "search_filter_apply": "Aplicați filtrul", + "search_filter_camera_title": "Selectați tipul de cameră", + "search_filter_date": "Dată", + "search_filter_date_interval": "{start} la {end}", + "search_filter_date_title": "Selectați un interval de dată", + "search_filter_display_option_not_in_album": "Nu este în album", + "search_filter_display_options": "Opțiuni de afișare", + "search_filter_filename": "Căutare după numele fișierului", + "search_filter_location": "Locaţie", + "search_filter_location_title": "Selectați locația", + "search_filter_media_type": "Tip media", + "search_filter_media_type_title": "Selectați tipul media", + "search_filter_people_title": "Selectați persoane", "search_for": "Căutare după", "search_for_existing_person": "Se caută o persoană existentă", + "search_no_more_result": "Nu mai există rezultate", "search_no_people": "Fără persoane", "search_no_people_named": "Nicio persoană numită \"{name}\"", + "search_no_result": "Nu s-au găsit rezultate, încercați un alt termen sau o altă combinație de termeni de căutare", "search_options": "Opțiuni de căutare", "search_page_categories": "Categorii", "search_page_motion_photos": "Fotografii în mișcare", "search_page_no_objects": "Nu sunt informații disponibile despre obiecte", "search_page_no_places": "Nici o informație disponibilă despre locuri", "search_page_screenshots": "Capturi de ecran", + "search_page_search_photos_videos": "Caută fotografiile și videoclipurile tale", "search_page_selfies": "Selfie-uri", "search_page_things": "Obiecte", "search_page_view_all_button": "Vezi toate", @@ -1366,6 +1659,7 @@ "select_album_cover": "Selectați coperta albumului", "select_all": "Selectați tot", "select_all_duplicates": "Selectați toate duplicatele", + "select_all_in": "Selectați tot în {group}", "select_avatar_color": "Selectați culoarea avatarului", "select_face": "Selectați fața", "select_featured_photo": "Selectați fotografia recomandată", @@ -1373,6 +1667,7 @@ "select_keep_all": "Selectați tot pentru păstrare", "select_library_owner": "Selectați proprietarul bibliotecii", "select_new_face": "Selectați o nouǎ fațǎ", + "select_person_to_tag": "Selectați o persoană pentru a o eticheta", "select_photos": "Selectați fotografii", "select_trash_all": "Selectați tot pentru ștergere", "select_user_for_sharing_page_err_album": "Creare album eșuată", @@ -1380,8 +1675,12 @@ "selected_count": "{count, plural, other {# selectat}}", "send_message": "Trimiteți mesaj", "send_welcome_email": "Trimiteți email de bun venit", + "server_endpoint": "Endpoint server", "server_info_box_app_version": "Versiune Aplicatie", "server_info_box_server_url": "URL-ul server-ului", + "server_offline": "Serverul este offline", + "server_online": "Server online", + "server_privacy": "Confidențialitatea serverului", "server_stats": "Statistici Server", "server_version": "Versiune Server", "set": "Setați", @@ -1391,11 +1690,15 @@ "set_date_of_birth": "Setați data nașterii", "set_profile_picture": "Setați poza de profil", "set_slideshow_to_fullscreen": "Setați Prezentare de Diapozitive la ecran complet", + "set_stack_primary_asset": "Setați ca element principal", "setting_image_viewer_help": "Vizualizatorul detaliilor încarcă mai întâi miniatura mică, apoi încarcă previzualizarea de dimensiune medie (dacă este activată), în cele din urmă încarcă originalul (dacă este activat).", "setting_image_viewer_original_subtitle": "Activează pentru a încărca imaginea originală în rezoluție completă (mare!). Dezactivează pentru a reduce consumul de date (atat pe rețea, cât și în memoria cache a dispozitivului).", "setting_image_viewer_original_title": "Încarcă fotografia originală", "setting_image_viewer_preview_subtitle": "Activează pentru a încărca o imagine în rezoluție medie. Dezactivează pentru a încărca direct imaginea originală sau doar a utiliza miniatura.", "setting_image_viewer_preview_title": "Încarcă imaginea de previzualizare", + "setting_image_viewer_title": "Imagini", + "setting_languages_apply": "Aplică", + "setting_languages_subtitle": "Schimbați limba aplicației", "setting_notifications_notify_failures_grace_period": "Notificare eșuări backup în fundal: {duration}", "setting_notifications_notify_hours": "{count} ore", "setting_notifications_notify_immediately": "imediat", @@ -1407,12 +1710,19 @@ "setting_notifications_subtitle": "Ajustează preferințele pentru notificări", "setting_notifications_total_progress_subtitle": "Progresul general al încărcării (resurse finalizate/total)", "setting_notifications_total_progress_title": "Afișează progresul total al copiilor de siguranță în fundal", + "setting_video_viewer_looping_title": "Buclă", + "setting_video_viewer_original_video_subtitle": "Când redați în flux un videoclip de pe server, redați originalul chiar și atunci când este disponibilă o transcodare. Poate duce la încărcare temporară. Videoclipurile disponibile local sunt redate la calitatea originală indiferent de această setare.", + "setting_video_viewer_original_video_title": "Forțează videoclipul original", "settings": "Setări", "settings_require_restart": "Te rugăm să repornești Immich pentru a aplica această setare", "settings_saved": "Setările au fost salvate", + "setup_pin_code": "Configurați un cod PIN", "share": "Distribuiți", + "share_action_prompt": "{count} elemente partajate", "share_add_photos": "Adaugă fotografii", + "share_assets_selected": "{count} selectat(e)", "share_dialog_preparing": "Se pregătește...", + "share_link": "Partajați linkul", "shared": "Partajat", "shared_album_activities_input_disable": "Cometariile sunt dezactivate", "shared_album_activity_remove_content": "Dorești să ștergi această activitate?", @@ -1425,6 +1735,7 @@ "shared_by_user": "Partajat de {user}", "shared_by_you": "Partajat de tine", "shared_from_partner": "Fotografii de la {partner}", + "shared_intent_upload_button_progress_text": "{current} / {total} încărcate", "shared_link_app_bar_title": "Link-uri distribuite", "shared_link_clipboard_copied_massage": "Copiat în clipboard", "shared_link_clipboard_text": "Link: {link}\nParolă: {password}", @@ -1436,6 +1747,8 @@ "shared_link_edit_expire_after_option_hours": "{count} ore", "shared_link_edit_expire_after_option_minute": "1 minut", "shared_link_edit_expire_after_option_minutes": "{count} minute", + "shared_link_edit_expire_after_option_months": "{count} luni", + "shared_link_edit_expire_after_option_year": "{count} an", "shared_link_edit_password_hint": "Introdu parola de distribuire", "shared_link_edit_submit_button": "Actualizează link", "shared_link_error_server_url_fetch": "Nu se poate accesa URL-ul serverului", @@ -1448,11 +1761,14 @@ "shared_link_expires_never": "Expiră ∞", "shared_link_expires_second": "Expiră în {count} secunde", "shared_link_expires_seconds": "Expiră în {count} secunde", + "shared_link_individual_shared": "Partajat individual", + "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Administrează link-urile distribuite", "shared_link_options": "Opțiuni de link partajat", "shared_links": "Link-uri distribuite", "shared_links_description": "Partajare imagini și clipuri printr-un link", "shared_photos_and_videos_count": "{assetCount, plural, other {# fotografii și videoclipuri partajate.}}", + "shared_with_me": "Distribuit cu mine", "shared_with_partner": "Partajat cu {partner}", "sharing": "Distribuire", "sharing_enter_password": "Vă rugăm să introduceți parola pentru a vizualiza această pagină.", @@ -1504,6 +1820,7 @@ "sort_title": "Titlu", "source": "Sursă", "stack": "Stivă", + "stack_action_prompt": "{count} suprapuse", "stack_duplicates": "Duplicate stive", "stack_select_one_photo": "Selectați o fotografie principală pentru stivă", "stack_selected_photos": "Fotografie stivă selectată", @@ -1513,14 +1830,17 @@ "start_date": "Data de începere", "state": "Situaţie", "status": "Stare", + "stop_casting": "Opriți difuzarea", "stop_motion_photo": "Opriți Fotografia in Mișcare", "stop_photo_sharing": "Încetați distribuirea fotografiilor?", "stop_photo_sharing_description": "{partner} nu va mai putea accesa fotografiile dvs.", "stop_sharing_photos_with_user": "Nu mai partajați fotografiile cu acest utilizator", "storage": "Spațiu de stocare", "storage_label": "Eticheta de depozitare", + "storage_quota": "Cotă de stocare", "storage_usage": "{used} din {available} utilizați", "submit": "Trimiteți", + "success": "Succes", "suggestions": "Sugestii", "sunrise_on_the_beach": "Rǎsǎrit pe plajǎ", "support": "Suport tehnic", @@ -1528,6 +1848,11 @@ "support_third_party_description": "Instalarea dvs. Immich a fost pregătită de o terță parte. Problemele pe care le întâmpinați pot fi cauzate de acel pachet, așa că vă rugăm să ridicați probleme cu ei în primă instanță utilizând linkurile de mai jos.", "swap_merge_direction": "Schimbați direcția de îmbinare", "sync": "Sincronizare", + "sync_albums": "Sincronizează albumele", + "sync_albums_manual_subtitle": "Sincronizează toate videoclipurile și fotografiile încărcate cu albumele de rezervă selectate", + "sync_local": "Sincronizare locală", + "sync_remote": "Sincronizare la distanță", + "sync_upload_album_setting_subtitle": "Creează și încarcă fotografiile și videoclipurile tale în albumele selectate de pe Immich", "tag": "Etichetă", "tag_assets": "Eticheta resurselor", "tag_created": "Etichetă creată: {tag}", @@ -1537,14 +1862,20 @@ "tag_updated": "Etichetă actualizată: {tag}", "tagged_assets": "Etichetat {count, plural, one {# resursă} other {# resurse}}", "tags": "Etichete", + "tap_to_run_job": "Atingeți pentru a rula job-ul", "template": "Șablon", "theme": "Temă", "theme_selection": "Selectarea temei", "theme_selection_description": "Setați automat tema la mod luminos sau întunecată, în funcție de preferințele de sistem ale browserului dvs", "theme_setting_asset_list_storage_indicator_title": "Arată indicator stocare", "theme_setting_asset_list_tiles_per_row_title": "Număr de resurse pe rând ({count})", + "theme_setting_colorful_interface_subtitle": "Aplicați culoarea primară pe suprafețele de fundal.", + "theme_setting_colorful_interface_title": "Interfață colorată", "theme_setting_image_viewer_quality_subtitle": "Ajustează calitatea detaliilor vizualizatorului de imagine", "theme_setting_image_viewer_quality_title": "Calitate vizualizator de imagine", + "theme_setting_primary_color_subtitle": "Alege o culoare pentru acțiunile și accentele principale.", + "theme_setting_primary_color_title": "Culoare primară", + "theme_setting_system_primary_color_title": "Folosește culoarea sistemului", "theme_setting_system_theme_switch": "Automat (La fel ca setarea sistemului)", "theme_setting_theme_subtitle": "Alege tema aplicației", "theme_setting_three_stage_loading_subtitle": "Încărcarea în trei etape are putea crește performanța încărcării dar generează un volum semnificativ mai mare de trafic pe rețea", @@ -1561,11 +1892,14 @@ "to_parent": "Du-te la părinte", "to_trash": "Coș de gunoi", "toggle_settings": "Activați setările", + "total": "Total", "total_usage": "Utilizare totală", "trash": "Coș de gunoi", + "trash_action_prompt": "{count} mutat(e) la coșul de gunoi", "trash_all": "Ștergeți Tot", "trash_count": "Ștergeți {count, number}", "trash_delete_asset": "Coș de gunoi/Ștergeți resursa", + "trash_emptied": "Coș de gunoi golit", "trash_no_results_message": "Fotografiile și videoclipurile mutate în coșul de gunoi vor apărea aici.", "trash_page_delete_all": "Șterge tot", "trash_page_empty_trash_dialog_content": "Dorești să golești coșul? Aceste fișiere vor fi șterse permanent din Immich", @@ -1576,9 +1910,14 @@ "trash_page_title": "Coș ({count})", "trashed_items_will_be_permanently_deleted_after": "Elementele din coșul de gunoi vor fi șterse definitiv după {days, plural, one {# zi} other {# zile}}.", "type": "Tip", + "unable_to_change_pin_code": "Nu se poate schimba codul PIN", + "unable_to_setup_pin_code": "Nu se poate configura codul PIN", "unarchive": "Dezarhivați", + "unarchive_action_prompt": "{count} șters(e) din Arhivă", "unarchived_count": "{count, plural, other {dezarhivat #}}", + "undo": "Anulează", "unfavorite": "Ștergeți din favorite", + "unfavorite_action_prompt": "{count} șters(e) de la Favorite", "unhide_person": "Dezvăluie persoana", "unknown": "Necunoscut", "unknown_country": "Țară necunoscută", @@ -1594,26 +1933,43 @@ "unsaved_change": "Modificare nesalvată", "unselect_all": "Deselectați toate", "unselect_all_duplicates": "Deselectați toate duplicatele", + "unselect_all_in": "Deselectați toate din {group}", "unstack": "Dezasamblați", + "unstack_action_prompt": "{count} unstacked", "unstacked_assets_count": "Nestivuit {count, plural, one {# resursă} other {# resurse}}", + "untagged": "Neetichetat", "up_next": "Mai departe", + "updated_at": "Actualizat", "updated_password": "Parolă actualizată", "upload": "Încărcați", + "upload_action_prompt": "{count} în coadă pentru încărcare", "upload_concurrency": "Încărcați simultan", + "upload_details": "Detalii încărcare", "upload_dialog_info": "Vrei să backup resursele selectate pe server?", "upload_dialog_title": "Încarcă resursă", "upload_errors": "Încărcare finalizată cu {count, plural, one {# eroare} other {# erori}}, reîmprospătați pagina pentru a reîncărca noile resurse.", + "upload_finished": "Încărcarea s-a finalizat", "upload_progress": "Rămas {remaining, number} - Procesat {processed, number}/{total, number}", "upload_skipped_duplicates": "Sărit {count, plural, one {# duplicat resursă} other {# duplicate resurse}}", "upload_status_duplicates": "Duplicate", "upload_status_errors": "Erori", "upload_status_uploaded": "Încărcat", "upload_success": "Încărcare reușită, reîmprospătați pagina pentru a vedea resursele noi încărcate.", + "upload_to_immich": "Încărcați pe Immich ({count})", + "uploading": "Se încarcă", + "uploading_media": "Se încarcă fișierele media", + "url": "URL", "usage": "Utilizare", + "use_biometric": "Folosește biometrice", + "use_current_connection": "folosește conexiunea curentă", "use_custom_date_range": "Utilizați în schimb un interval de date personalizat", "user": "Utilizator", + "user_has_been_deleted": "Acest utilizator a fost șters.", "user_id": "ID utilizator", "user_liked": "{user} a apreciat {type, select, photo {această imagine} video {acest video} asset {această resursă} other {it}}", + "user_pin_code_settings": "Cod PIN", + "user_pin_code_settings_description": "Gestionați-vă codul PIN", + "user_privacy": "Confidențialitatea utilizatorilor", "user_purchase_settings": "Cumpărare", "user_purchase_settings_description": "Gestionați-vă achiziția", "user_role_set": "Setați {user} ca {role}", @@ -1622,8 +1978,10 @@ "user_usage_stats_description": "Vedeți statisticile de utilizare a contului", "username": "Nume de utilizator", "users": "Utilizatori", + "users_added_to_album_count": "{count, plural, one {# utilizator a fost adăugat} other {# utilizatori au fost adăugați}} în album", "utilities": "Utilitǎți", "validate": "Validați", + "validate_endpoint_error": "Vă rugăm să introduceți o adresă URL validă", "variables": "Variabile", "version": "Versiune", "version_announcement_closing": "Prietenul tǎu, Alex", @@ -1639,6 +1997,7 @@ "view_album": "Vizualizați Album", "view_all": "Vizualizați Tot", "view_all_users": "Vizulizați toți utilizatorii", + "view_details": "Vedeți detaliile", "view_in_timeline": "Vizualizați în cronologie", "view_link": "Vezi link", "view_links": "Vizualizați scurtǎturi", @@ -1647,6 +2006,7 @@ "view_previous_asset": "Vizualizați resursa anterioară", "view_qr_code": "Vezi cod QR", "view_stack": "Vizualizați Stiva", + "view_user": "Vizualizare utilizator", "viewer_remove_from_stack": "Șterge din grup", "viewer_stack_use_as_main_asset": "Folosește ca resursă principală", "viewer_unstack": "Anulează grup", @@ -1656,11 +2016,12 @@ "week": "Sǎptǎmânǎ", "welcome": "Bun venit", "welcome_to_immich": "Bun venit la Immich", - "wifi_name": "WiFi Name", + "wifi_name": "Nume Wi-Fi", + "wrong_pin_code": "Cod PIN greșit", "year": "An", "years_ago": "acum {years, plural, one {# an} other {# ani}} în urmă", "yes": "Da", "you_dont_have_any_shared_links": "Nu aveți linkuri partajate", - "your_wifi_name": "Your WiFi name", + "your_wifi_name": "Numele rețelei tale WiFi", "zoom_image": "Măriți Imaginea" } diff --git a/i18n/ru.json b/i18n/ru.json index 5810a31053..921666fb54 100644 --- a/i18n/ru.json +++ b/i18n/ru.json @@ -14,6 +14,7 @@ "add_a_location": "Добавить местоположение", "add_a_name": "Добавить имя", "add_a_title": "Добавить название", + "add_birthday": "Указать дату рождения", "add_endpoint": "Добавить адрес", "add_exclusion_pattern": "Добавить шаблон исключения", "add_import_path": "Добавить путь импорта", @@ -44,6 +45,13 @@ "backup_database": "Создать резервную копию базы данных", "backup_database_enable_description": "Включить создание дампов базы данных", "backup_keep_last_amount": "Количество хранимых резервных копий базы данных", + "backup_onboarding_1_description": "хранение дополнительной внешней копии в облаке или другом физическом месте.", + "backup_onboarding_2_description": "хранение основных файлов и их локальной копии на двух разных типах носителей.", + "backup_onboarding_3_description": "создание трёх копий данных, включая исходные файлы. 2 локальных копии и 1 внешнюю.", + "backup_onboarding_description": "Для надёжной защиты рекомендуется использовать стратегию резервирования данных 3-2-1. Делайте копии как загруженных фотографий и видео, так и базы данных Immich.", + "backup_onboarding_footer": "Дополнительная информация по резервному копированию Immich доступна в документации.", + "backup_onboarding_parts_title": "Стратегия 3-2-1 подразумевает:", + "backup_onboarding_title": "Резервное копирование", "backup_settings": "Настройки резервного копирования базы данных", "backup_settings_description": "Настройки создания резервных копий базы данных.", "cleared_jobs": "Очищены задачи для: {job}", @@ -166,6 +174,20 @@ "metadata_settings_description": "Управление настройками метаданных", "migration_job": "Миграция", "migration_job_description": "Перенос миниатюр объектов и лиц в последнюю структуру папок", + "nightly_tasks_cluster_faces_setting_description": "Запустить распознавание людей по новым обнаруженным лицам", + "nightly_tasks_cluster_new_faces_setting": "Распознавание новых лиц", + "nightly_tasks_database_cleanup_setting": "Задачи очистки базы данных", + "nightly_tasks_database_cleanup_setting_description": "Удаление старых и более ненужных записей из базы данных", + "nightly_tasks_generate_memories_setting": "Создание воспоминаний", + "nightly_tasks_generate_memories_setting_description": "Создание новых воспоминаний из существующих объектов", + "nightly_tasks_missing_thumbnails_setting": "Создание отсутствующих миниатюр", + "nightly_tasks_missing_thumbnails_setting_description": "Добавление объектов без миниатюр в очередь для их создания", + "nightly_tasks_settings": "Настройки ночных задач", + "nightly_tasks_settings_description": "Управление ночными регламентными задачами", + "nightly_tasks_start_time_setting": "Время начала", + "nightly_tasks_start_time_setting_description": "Время, когда сервер начинает выполнять ночные задачи", + "nightly_tasks_sync_quota_usage_setting": "Синхронизация квот хранилища", + "nightly_tasks_sync_quota_usage_setting_description": "Обновление квоты хранилища пользователя на основе актуальных данных", "no_paths_added": "Пути не добавлены", "no_pattern_added": "Шаблон не добавлен", "note_apply_storage_label_previous_assets": "Примечание: Чтобы применить метку хранилища к ранее загруженным объектам, запустите", @@ -196,6 +218,8 @@ "oauth_mobile_redirect_uri": "URI редиректа для мобильных", "oauth_mobile_redirect_uri_override": "Перенаправление URI для мобильных устройств", "oauth_mobile_redirect_uri_override_description": "Включите, если поставщик OAuth не разрешает использование мобильного URI, например, ''{callback}''", + "oauth_role_claim": "Утверждение роли", + "oauth_role_claim_description": "Автоматическое предоставление доступа администратора на основе наличия этого утверждения. Утверждение может иметь значение 'user' или 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Настройки входа через OAuth", "oauth_settings_more_details": "Для получения дополнительной информаций об этой функции обратитесь к документации.", @@ -212,7 +236,7 @@ "password_settings_description": "Управление настройками входа по паролю", "paths_validated_successfully": "Все пути успешно прошли проверку", "person_cleanup_job": "Очистка персоны", - "quota_size_gib": "Размер квоты (ГБ)", + "quota_size_gib": "Размер квоты (GiB)", "refreshing_all_libraries": "Обновление всех библиотек", "registration": "Регистрация администратора", "registration_description": "Первый зарегистрированный пользователь будет назначен администратором. В дальнейшем этой учетной записи будет доступно создание дополнительных пользователей и управление сервером.", @@ -225,7 +249,7 @@ "server_external_domain_settings": "Внешний домен", "server_external_domain_settings_description": "Домен для публичных ссылок, включая http(s)://", "server_public_users": "Публичные пользователи", - "server_public_users_description": "Отображать всех пользователей (имена и email) для добавления в общие альбомы. Когда отключено, список пользователей будет доступен только администраторам.", + "server_public_users_description": "Выводить список пользователей (имена и email) в общих альбомах. Когда отключено, список доступен только администраторам, пользователи смогут делиться только ссылкой.", "server_settings": "Настройки сервера", "server_settings_description": "Управление настройками сервера", "server_welcome_message": "Приветственное сообщение", @@ -357,10 +381,12 @@ "admin_password": "Пароль администратора", "administration": "Управление сервером", "advanced": "Расширенные", + "advanced_settings_beta_timeline_subtitle": "Попробуйте новый функционал приложения", + "advanced_settings_beta_timeline_title": "Бета-версия временной шкалы", "advanced_settings_enable_alternate_media_filter_subtitle": "Используйте этот параметр для фильтрации медиафайлов во время синхронизации на основе альтернативных критериев. Пробуйте только в том случае, если у вас есть проблемы с обнаружением приложением всех альбомов.", "advanced_settings_enable_alternate_media_filter_title": "[ЭКСПЕРИМЕНТАЛЬНО] Использование фильтра синхронизации альбомов альтернативных устройств", "advanced_settings_log_level_title": "Уровень логирования: {level}", - "advanced_settings_prefer_remote_subtitle": "Некоторые устройства очень медленно загружают локальные изображения. Активируйте эту настройку, чтобы изображения всегда загружались с сервера.", + "advanced_settings_prefer_remote_subtitle": "Некоторые устройства очень медленно загружают локальные миниатюры. Активируйте эту настройку, чтобы изображения всегда загружались с сервера.", "advanced_settings_prefer_remote_title": "Предпочитать фото на сервере", "advanced_settings_proxy_headers_subtitle": "Определите заголовки прокси-сервера, которые Immich должен отправлять с каждым сетевым запросом", "advanced_settings_proxy_headers_title": "Заголовки прокси", @@ -379,6 +405,7 @@ "album_cover_updated": "Обложка альбома обновлена", "album_delete_confirmation": "Вы уверены, что хотите удалить альбом {album}?", "album_delete_confirmation_description": "Если альбом был общим, другие пользователи больше не смогут получить к нему доступ.", + "album_deleted": "Альбом удалён", "album_info_card_backup_album_excluded": "ИСКЛЮЧЕН", "album_info_card_backup_album_included": "ВКЛЮЧЕН", "album_info_updated": "Информация об альбоме обновлена", @@ -388,7 +415,8 @@ "album_options": "Параметры альбома", "album_remove_user": "Удалить пользователя?", "album_remove_user_confirmation": "Вы уверены, что хотите удалить пользователя {user}?", - "album_share_no_users": "Похоже, вы поделились этим альбомом со всеми пользователями или у вас нет пользователей, с которыми можно поделиться.", + "album_search_not_found": "Не найдено альбомов по вашему запросу", + "album_share_no_users": "Нет доступных пользователей, с которыми можно поделиться альбомом.", "album_updated": "Альбом обновлён", "album_updated_setting_description": "Получать уведомление по электронной почте при добавлении новых ресурсов в общий альбом", "album_user_left": "Вы покинули {album}", @@ -407,14 +435,15 @@ "albums_default_sort_order": "Порядок сортировки в альбомах по умолчанию", "albums_default_sort_order_description": "Первоначальный порядок сортировки, устанавливаемый в новых альбомах.", "albums_feature_description": "Коллекции фото и видео, которыми можно делиться с другими пользователями.", + "albums_on_device_count": "Альбомы на устройстве ({count})", "all": "Все", "all_albums": "Все альбомы", "all_people": "Все люди", "all_videos": "Все видео", "allow_dark_mode": "Разрешить темный режим", "allow_edits": "Разрешить редактирование", - "allow_public_user_to_download": "Разрешить скачивание публичным пользователям", - "allow_public_user_to_upload": "Разрешить публичным пользователям загружать файлы", + "allow_public_user_to_download": "Разрешить скачивание", + "allow_public_user_to_upload": "Разрешить добавление файлов", "alt_text_qr_code": "QR-код", "anti_clockwise": "Против часовой", "api_key": "API ключ", @@ -427,6 +456,7 @@ "app_settings": "Параметры приложения", "appears_in": "Добавлено в", "archive": "Архив", + "archive_action_prompt": "{count} добавлено в Архив", "archive_or_unarchive_photo": "Архивировать или разархивировать фото", "archive_page_no_archived_assets": "В архиве сейчас пусто", "archive_page_title": "Архив ({count})", @@ -464,7 +494,6 @@ "assets": "Объекты", "assets_added_count": "{count, plural, one {Добавлен # объект} many {Добавлено # объектов} other {Добавлено # объекта}}", "assets_added_to_album_count": "В альбом {count, plural, one {добавлен # объект} many {добавлено # объектов} other {добавлено # объекта}}", - "assets_added_to_name_count": "{count, plural, one {# объект добавлен} many {# объектов добавлено} other {# объекта добавлено}} в {hasName, select, true {альбом {name}} other {новый альбом}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Объект не может быть добавлен} other {Объекты не могут быть добавлены}} в альбом", "assets_count": "{count, plural, one {# объект} many {# объектов} other {# объекта}}", "assets_deleted_permanently": "{count} объект(ов) удалено навсегда", @@ -490,6 +519,7 @@ "back_close_deselect": "Назад, закрыть или отменить выбор", "background_location_permission": "Доступ к местоположению в фоне", "background_location_permission_content": "Чтобы считывать имя Wi-Fi сети в фоне, приложению *всегда* необходим доступ к точному местоположению устройства", + "backup": "Резервное копирование", "backup_album_selection_page_albums_device": "Альбомы на устройстве ({count})", "backup_album_selection_page_albums_tap": "Нажмите, чтобы включить, дважды, чтобы исключить", "backup_album_selection_page_assets_scatter": "Ваши изображения и видео могут находиться в разных альбомах. Вы можете выбрать, какие альбомы включить, а какие исключить из резервного копирования.", @@ -553,6 +583,8 @@ "backup_options_page_title": "Резервное копирование", "backup_setting_subtitle": "Настройка активного и фонового резервного копирования", "backward": "Назад", + "beta_sync": "Статус бета-синхронизации", + "beta_sync_subtitle": "Управление новой системой синхронизации", "biometric_auth_enabled": "Биометрическая аутентификация включена", "biometric_locked_out": "Вам закрыт доступ к биометрической аутентификации", "biometric_no_options": "Биометрическая аутентификация недоступна", @@ -560,7 +592,7 @@ "birthdate_saved": "Дата рождения успешно сохранена", "birthdate_set_description": "Дата рождения используется для определения возраста человека на момент фотографии.", "blurred_background": "Размытый фон", - "bugs_and_feature_requests": "Ошибки и предложения", + "bugs_and_feature_requests": "Ошибки и запросы", "build": "Сборка", "build_image": "Версия сборки", "bulk_delete_duplicates_confirmation": "Вы уверены, что хотите удалить {count, plural, one {# дублирующийся объект} many {# дублирующихся объектов} other {# дублирующихся объекта}}? Будет сохранён самый большой файл в каждой группе, а его дубликаты навсегда удалены. Это действие нельзя отменить!", @@ -570,7 +602,7 @@ "cache_settings_clear_cache_button": "Очистить кэш", "cache_settings_clear_cache_button_title": "Очищает кэш приложения. Это негативно повлияет на производительность, пока кэш не будет создан заново.", "cache_settings_duplicated_assets_clear_button": "ОЧИСТИТЬ", - "cache_settings_duplicated_assets_subtitle": "Фото и видео, занесенные приложением в черный список", + "cache_settings_duplicated_assets_subtitle": "Фото и видео, пропускаемые приложением", "cache_settings_duplicated_assets_title": "Дублирующиеся объекты ({count})", "cache_settings_statistics_album": "Миниатюры библиотеки", "cache_settings_statistics_full": "Полные изображения", @@ -587,6 +619,7 @@ "cancel": "Отменить", "cancel_search": "Отменить поиск", "canceled": "Отменено", + "canceling": "Отмена", "cannot_merge_people": "Невозможно объединить людей", "cannot_undo_this_action": "Это действие нельзя отменить!", "cannot_update_the_description": "Невозможно обновить описание", @@ -595,7 +628,7 @@ "change_date": "Изменить дату", "change_description": "Изменить описание", "change_display_order": "Изменить порядок отображения", - "change_expiration_time": "Изменить время окончания", + "change_expiration_time": "Изменить срок действия", "change_location": "Изменить местоположение", "change_name": "Изменить имя", "change_name_successfully": "Имя успешно изменено", @@ -671,7 +704,7 @@ "copy_link": "Копировать ссылку", "copy_link_to_clipboard": "Скопировать ссылку в буфер обмена", "copy_password": "Скопировать пароль", - "copy_to_clipboard": "Скопировать в буфер обмена", + "copy_to_clipboard": "Скопировать настройки в буфер обмена", "country": "Страна", "cover": "Обложка", "covers": "Обложки", @@ -700,10 +733,11 @@ "current_server_address": "Текущий адрес сервера", "custom_locale": "Пользовательский регион", "custom_locale_description": "Форматирование дат и чисел в зависимости от языка и региона", + "custom_url": "Свой URL", "daily_title_text_date": "E, MMM dd", "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "Тёмная", - "darkTheme": "Переключение темной темы", + "dark_theme": "Тёмная тема", "date_after": "Дата после", "date_and_time": "Дата и Время", "date_before": "Дата до", @@ -713,12 +747,14 @@ "day": "День", "deduplicate_all": "Убрать все дубликаты", "deduplication_criteria_1": "Размер изображения в байтах", - "deduplication_criteria_2": "Подсчет данных EXIF", + "deduplication_criteria_2": "Количество EXIF данных", "deduplication_info": "Информация о дедупликации", - "deduplication_info_description": "Для автоматического предварительного выбора объектов и массового удаления дубликатов мы рассмотрим:", + "deduplication_info_description": "Для автоматического выбора лучших объектов среди дубликатов анализируется следующая информация:", "default_locale": "Дата и время по умолчанию", "default_locale_description": "Использовать формат даты и времени в соответствии с языковым стандартом вашего браузера", "delete": "Удалить", + "delete_action_confirmation_message": "Вы действительно хотите удалить этот объект? Это действие переместит объект в корзину сервера и предложит удалить его локально.", + "delete_action_prompt": "{count} удалено", "delete_album": "Удалить альбом", "delete_api_key_prompt": "Вы действительно хотите удалить этот API ключ?", "delete_dialog_alert": "Эти элементы будут безвозвратно удалены с сервера, а также с вашего устройства", @@ -732,9 +768,12 @@ "delete_key": "Удалить ключ", "delete_library": "Удалить библиотеку", "delete_link": "Удалить ссылку", + "delete_local_action_prompt": "{count} удалено локально", "delete_local_dialog_ok_backed_up_only": "Удалить только резервные копии", "delete_local_dialog_ok_force": "Все равно удалить", "delete_others": "Удалить остальные", + "delete_permanently": "Удалить навсегда", + "delete_permanently_action_prompt": "{count} удалено навсегда", "delete_shared_link": "Удалить публичную ссылку", "delete_shared_link_dialog_title": "Удалить публичную ссылку", "delete_tag": "Удалить тег", @@ -745,6 +784,7 @@ "description": "Описание", "description_input_hint_text": "Добавить описание...", "description_input_submit_error": "Не удалось обновить описание, проверьте логи, чтобы узнать причину", + "deselect_all": "Снять выделение", "details": "Подробности", "direction": "Направление", "disabled": "Отключено", @@ -762,6 +802,7 @@ "documentation": "Документация", "done": "Готово", "download": "Скачать", + "download_action_prompt": "Загружаются {count} объектов", "download_canceled": "Загрузка отменена", "download_complete": "Загрузка окончена", "download_enqueue": "Загрузка в очереди", @@ -783,11 +824,12 @@ "downloading_media": "Загрузка медиа", "drop_files_to_upload": "Перенесите файлы в любое место для загрузки", "duplicates": "Дубликаты", - "duplicates_description": "Разберитесь с каждой группой, указав, какие из них являются дубликатами, если таковые имеются", + "duplicates_description": "Просмотрите найденные дубликаты и в каждой группе укажите, какие объекты оставить, а какие удалить", "duration": "Продолжительность", "edit": "Редактировать", "edit_album": "Редактировать альбом", "edit_avatar": "Изменить аватар", + "edit_birthday": "Изменить дату рождения", "edit_date": "редактировать дату", "edit_date_and_time": "редактировать дату и время", "edit_description": "Изменить описание", @@ -799,12 +841,13 @@ "edit_key": "Изменить ключ", "edit_link": "Редактировать ссылку", "edit_location": "Редактировать местоположение", + "edit_location_action_prompt": "{count} мест изменено", "edit_location_dialog_title": "Местоположение", "edit_name": "Редактировать имя", "edit_people": "Редактировать людей", "edit_tag": "Изменить тег", "edit_title": "Редактировать Заголовок", - "edit_user": "Редактирование пользователя", + "edit_user": "Изменить пользователя", "edited": "Отредактировано", "editor": "Редактор", "editor_close_without_save_prompt": "Изменения не будут сохранены", @@ -817,6 +860,7 @@ "empty_trash": "Очистить корзину", "empty_trash_confirmation": "Вы уверены, что хотите очистить корзину? Все объекты в корзине будут навсегда удалены из Immich.\nВы не сможете отменить это действие!", "enable": "Включить", + "enable_backup": "Включить резервное копирование", "enable_biometric_auth_description": "Введите свой PIN-код для включения биометрической аутентификации", "enabled": "Включено", "end_date": "Дата окончания", @@ -953,6 +997,7 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "Добавить описание...", + "exif_bottom_sheet_description_error": "Не удалось обновить описание", "exif_bottom_sheet_details": "ПОДРОБНОСТИ", "exif_bottom_sheet_location": "МЕСТО", "exif_bottom_sheet_people": "ЛЮДИ", @@ -967,12 +1012,14 @@ "experimental_settings_subtitle": "Используйте на свой страх и риск!", "experimental_settings_title": "Экспериментальные функции", "expire_after": "Истекает через", - "expired": "Срок действия истек", + "expired": "Срок действия истёк", "expires_date": "Срок действия до {date}", "explore": "Поиск", "explorer": "Проводник", "export": "Экспортировать", "export_as_json": "Экспорт в JSON", + "export_database": "Экспорт базы данных", + "export_database_description": "Экспорт базы данных SQLite", "extension": "Расширение", "external": "Внешний", "external_libraries": "Внешние библиотеки", @@ -984,6 +1031,7 @@ "failed_to_load_assets": "Не удалось загрузить объекты", "failed_to_load_folder": "Ошибка при загрузке папки", "favorite": "Избранное", + "favorite_action_prompt": "{count} добавлено в Избранное", "favorite_or_unfavorite_photo": "Добавить или удалить фотографию из избранного", "favorites": "Избранное", "favorites_page_no_favorites": "В избранном сейчас пусто", @@ -1023,6 +1071,9 @@ "haptic_feedback_switch": "Включить тактильную отдачу", "haptic_feedback_title": "Тактильная отдача", "has_quota": "Квота", + "hash_asset": "Хешированный объект", + "hashed_assets": "Хешированные объекты", + "hashing": "Хеширование", "header_settings_add_header_tip": "Добавить заголовок", "header_settings_field_validator_msg": "Значение не может быть пустым", "header_settings_header_name_input": "Имя заголовка", @@ -1055,6 +1106,7 @@ "host": "Хост", "hour": "Час", "id": "ID", + "idle": "В ожидании", "ignore_icloud_photos": "Пропускать файлы из iCloud", "ignore_icloud_photos_description": "Не загружать файлы в Immich, если они хранятся в iCloud", "image": "Изображения", @@ -1081,8 +1133,8 @@ "include_archived": "Отображать архив", "include_shared_albums": "Включать общие альбомы", "include_shared_partner_assets": "Включать общие ресурсы партнера", - "individual_share": "Персональный доступ", - "individual_shares": "Индивидуальный доступ", + "individual_share": "Индивидуальная подборка", + "individual_shares": "Подборки", "info": "Информация", "interval": { "day_at_onepm": "Каждый день в 13:00", @@ -1103,7 +1155,7 @@ "items_count": "{count, plural, one {# элемент} many {# элементов} other {# элемента}}", "jobs": "Задачи", "keep": "Оставить", - "keep_all": "Сохранить всё", + "keep_all": "Сохранить все", "keep_this_delete_others": "Оставить этот, удалить остальные", "kept_this_deleted_others": "Сохранён этот объект и {count, plural, one {# объект удалён} many {# объектов удалено} other {# объекта удалено}}", "keyboard_shortcuts": "Сочетания клавиш", @@ -1112,6 +1164,7 @@ "language_no_results_title": "Языков не найдено", "language_search_hint": "Поиск языков...", "language_setting_description": "Выберите предпочитаемый вами язык", + "large_files": "Файлы наибольшего размера", "last_seen": "Последний доступ", "latest_version": "Последняя версия", "latitude": "Широта", @@ -1127,16 +1180,18 @@ "library_page_sort_created": "Недавно созданные", "library_page_sort_last_modified": "Последнее изменение", "library_page_sort_title": "Название альбома", + "licenses": "Лицензии", "light": "Светлая", "like_deleted": "Лайк удален", "link_motion_video": "Ссылка на движущееся видео", - "link_options": "Настройки ссылки", "link_to_oauth": "Присоединение к OAuth", "linked_oauth_account": "Присоединённый аккаунт OAuth", "list": "Список", "loading": "Загрузка", "loading_search_results_failed": "Загрузка результатов поиска не удалась", + "local": "На устройстве", "local_asset_cast_failed": "Невозможно транслировать объект, который ещё не загружен на сервер", + "local_assets": "Объекты на устройстве", "local_network": "Локальная сеть", "local_network_sheet_info": "Приложение будет подключаться к серверу по этому адресу, когда устройство подключено к выбранной Wi-Fi сети", "location_permission": "Доступ к местоположению", @@ -1193,8 +1248,7 @@ "manage_your_devices": "Управление устройствами, на которых вы входили в свой аккаунт", "manage_your_oauth_connection": "Настройки подключённого OAuth", "map": "Карта", - "map_assets_in_bound": "{count} фото", - "map_assets_in_bounds": "{count} фото", + "map_assets_in_bounds": "{count, plural, one {# фото} other {# фото}}", "map_cannot_get_user_location": "Невозможно получить местоположение пользователя", "map_location_dialog_yes": "Да", "map_location_picker_page_use_location": "Это местоположение", @@ -1246,6 +1300,7 @@ "more": "Больше", "move": "Переместить", "move_off_locked_folder": "Переместить из личной папки", + "move_to_lock_folder_action_prompt": "{count} добавлено в Личную папку", "move_to_locked_folder": "Переместить в личную папку", "move_to_locked_folder_confirmation": "Эти фото и видео будут удалены из всех альбомов и будут доступны только в личной папке", "moved_to_archive": "{count, plural, one {# объект перемещён} many {# объектов перемещено} other {# объекта перемещено}} в архив", @@ -1257,6 +1312,8 @@ "my_albums": "Мои альбомы", "name": "Имя", "name_or_nickname": "Имя или ник", + "network_requirement_photos_upload": "Использовать мобильный интернет для загрузки фото", + "network_requirement_videos_upload": "Использовать мобильный интернет для загрузки видео", "networking_settings": "Сеть", "networking_subtitle": "Настройка подключения к серверу", "never": "никогда", @@ -1292,6 +1349,7 @@ "no_results": "Нет результатов", "no_results_description": "Попробуйте использовать синоним или более общее ключевое слово", "no_shared_albums_message": "Создайте альбом для обмена фотографиями и видеозаписями с людьми в вашей сети", + "no_uploads_in_progress": "Нет активных загрузок", "not_in_any_album": "Ни в одном альбоме", "not_selected": "Не выбрано", "note_apply_storage_label_to_previously_uploaded assets": "Примечание: Чтобы применить метку хранилища к ранее загруженным ресурсам, запустите", @@ -1329,6 +1387,7 @@ "original": "оригинал", "other": "Другое", "other_devices": "Другие устройства", + "other_entities": "Другие объекты", "other_variables": "Другие переменные", "owned": "Мои", "owner": "Владелец", @@ -1426,7 +1485,7 @@ "profile_drawer_server_out_of_date_minor": "Версия сервера устарела. Пожалуйста, обновите его.", "profile_image_of_user": "Изображение профиля {user}", "profile_picture_set": "Фото профиля установлено.", - "public_album": "Публичный альбом", + "public_album": "Общий альбом", "public_share": "Публичный доступ", "purchase_account_info": "Поддержка", "purchase_activated_subtitle": "Благодарим вас за поддержку Immich и программного обеспечения с открытым исходным кодом", @@ -1460,6 +1519,7 @@ "purchase_server_description_2": "Состояние поддержки", "purchase_server_title": "Сервер", "purchase_settings_server_activated": "Ключом продукта управляет администратор сервера", + "queue_status": "В очереди {count}/{total}", "rating": "Рейтинг звёзд", "rating_clear": "Очистить рейтинг", "rating_count": "{count, plural, one {# звезда} many {# звезд} other {# звезды}}", @@ -1488,6 +1548,8 @@ "refreshing_faces": "Обновление лиц", "refreshing_metadata": "Обновление метаданных", "regenerating_thumbnails": "Восстановление миниатюр", + "remote": "На сервере", + "remote_assets": "Объекты на сервере", "remove": "Удалить", "remove_assets_album_confirmation": "Вы действительно хотите удалить {count, plural, one {# объект} many {# объектов} other {# объекта}} из альбома?", "remove_assets_shared_link_confirmation": "Вы действительно хотите удалить {count, plural, one {# объект} many {# объектов} other {# объекта}} из публичного доступа по этой ссылке?", @@ -1495,7 +1557,9 @@ "remove_custom_date_range": "Удалить пользовательский диапазон дат", "remove_deleted_assets": "Удаление автономных файлов", "remove_from_album": "Удалить из альбома", + "remove_from_album_action_prompt": "{count} удалено из альбома", "remove_from_favorites": "Удалить из избранного", + "remove_from_lock_folder_action_prompt": "{count} удалено из Личной папки", "remove_from_locked_folder": "Удалить из личной папки", "remove_from_locked_folder_confirmation": "Вы действительно хотите переместить эти фото и видео из личной папки? Они станут доступны в вашей библиотеке.", "remove_from_shared_link": "Удалить из публичной ссылки", @@ -1520,22 +1584,28 @@ "require_user_to_change_password_on_first_login": "Требовать у пользователя сменить пароль при первом входе", "rescan": "Повторное сканирование", "reset": "Сброс", - "reset_password": "Сброс пароля", + "reset_password": "Сбросить пароль", "reset_people_visibility": "Восстановить видимость людей", "reset_pin_code": "Сбросить PIN-код", + "reset_sqlite": "Очистить базу данных SQLite", + "reset_sqlite_confirmation": "Вы уверены, что хотите очистить базу данных SQLite? Вам потребуется выйти из системы и снова войти для повторной синхронизации данных.", + "reset_sqlite_success": "База данных SQLite успешно очищена", "reset_to_default": "Восстановление значений по умолчанию", "resolve_duplicates": "Устранить дубликаты", "resolved_all_duplicates": "Все дубликаты устранены", "restore": "Восстановить", "restore_all": "Восстановить все", + "restore_trash_action_prompt": "{count} восстановлено из корзины", "restore_user": "Восстановить пользователя", "restored_asset": "Восстановленный объект", "resume": "Продолжить", "retry_upload": "Повторить загрузку", - "review_duplicates": "Посмотреть дубликаты", + "review_duplicates": "Разбор дубликатов", + "review_large_files": "Обзор больших файлов", "role": "Роль", "role_editor": "Редактор", "role_viewer": "Зритель", + "running": "Выполняется", "save": "Сохранить", "save_to_gallery": "Сохранить в галерею", "saved_api_key": "API ключ изменён", @@ -1607,18 +1677,18 @@ "select": "Выбрать", "select_album_cover": "Выбрать обложку альбома", "select_all": "Выбрать все", - "select_all_duplicates": "Выбрать все дубликаты", + "select_all_duplicates": "Выбрать все для сохранения", "select_all_in": "Выбрать все в {group}", "select_avatar_color": "Выберите цвет аватара", "select_face": "Выбрать лицо", "select_featured_photo": "Выбрать избранное фото", - "select_from_computer": "Выберите с компьютера", - "select_keep_all": "Оставить всё выбранное", + "select_from_computer": "Выбрать с компьютера", + "select_keep_all": "Выбрать все для сохранения", "select_library_owner": "Выберите владельца библиотеки", "select_new_face": "Выбрать другое лицо", "select_person_to_tag": "Выделите лицо человека, которого хотите отметить", "select_photos": "Выберите фотографии", - "select_trash_all": "Удалить всё выбранное", + "select_trash_all": "Выбрать все для удаления", "select_user_for_sharing_page_err_album": "Не удалось создать альбом", "selected": "Выбрано", "selected_count": "{count, plural, one {Выбран # объект} many {Выбрано # объектов} other {Выбрано # объекта}}", @@ -1667,6 +1737,7 @@ "settings_saved": "Настройки сохранены", "setup_pin_code": "Создание PIN-кода", "share": "Поделиться", + "share_action_prompt": "Расшарено {count} объектов", "share_add_photos": "Добавить фото", "share_assets_selected": "{count} выбрано", "share_dialog_preparing": "Подготовка...", @@ -1688,6 +1759,7 @@ "shared_link_clipboard_copied_massage": "Скопировано в буфер обмена", "shared_link_clipboard_text": "Ссылка: {link}\nПароль: {password}", "shared_link_create_error": "Ошибка при создании публичной ссылки", + "shared_link_custom_url_description": "Доступ к этой общей ссылке с помощью заданного пользователем URL-адреса", "shared_link_edit_description_hint": "Введите описание публичного доступа", "shared_link_edit_expire_after_option_day": "1 день", "shared_link_edit_expire_after_option_days": "{count} дней", @@ -1713,6 +1785,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Управление публичными ссылками", "shared_link_options": "Параметры публичных ссылок", + "shared_link_password_description": "Требовать пароль для доступа к этой общей ссылке", "shared_links": "Публичные ссылки", "shared_links_description": "Делитесь фотографиями и видео по ссылке", "shared_photos_and_videos_count": "{assetCount, plural, other {# фото и видео.}}", @@ -1767,10 +1840,11 @@ "sort_recent": "Недавние фото", "sort_title": "Заголовок", "source": "Исходный код", - "stack": "Группировать", - "stack_duplicates": "Группировать дубликаты", + "stack": "Сгруппировать", + "stack_action_prompt": "{count} сгруппировано", + "stack_duplicates": "Сгруппировать дубликаты", "stack_select_one_photo": "Выберите главную фотографию для группы", - "stack_selected_photos": "Группировать выбранные объекты", + "stack_selected_photos": "Сгруппировать выбранные объекты", "stacked_assets_count": "{count, plural, one {# объект объединен} many {# объектов объединено} other {# объекта объединено}} в группу", "stacktrace": "Трассировка стека", "start": "Старт", @@ -1787,6 +1861,7 @@ "storage_quota": "Квота хранилища", "storage_usage": "{used} из {available}", "submit": "Подтвердить", + "success": "Успешно", "suggestions": "Предложения", "sunrise_on_the_beach": "Восход солнца на пляже", "support": "Поддержка", @@ -1796,6 +1871,8 @@ "sync": "Синхр.", "sync_albums": "Синхронизировать альбомы", "sync_albums_manual_subtitle": "Синхронизировать все загруженные фото и видео в выбранные альбомы для резервного копирования", + "sync_local": "Синхронизировать локально", + "sync_remote": "Синхронизация с сервером", "sync_upload_album_setting_subtitle": "Создавайте и загружайте свои фотографии и видео в выбранные альбомы на сервер Immich", "tag": "Тег", "tag_assets": "Добавить теги", @@ -1806,6 +1883,7 @@ "tag_updated": "Тег {tag} изменен", "tagged_assets": "Тег назначен для {count, plural, one {# объекта} other {# объектов}}", "tags": "Теги", + "tap_to_run_job": "Нажмите для запуска задачи", "template": "Шаблон", "theme": "Тема", "theme_selection": "Выбор темы", @@ -1838,7 +1916,8 @@ "total": "Всего", "total_usage": "Общая статистика", "trash": "Корзина", - "trash_all": "Удалить всё", + "trash_action_prompt": "{count} перемещено в корзину", + "trash_all": "Удалить все", "trash_count": "Удалить {count, number}", "trash_delete_asset": "Переместить в корзину", "trash_emptied": "Корзина очищена", @@ -1855,9 +1934,11 @@ "unable_to_change_pin_code": "Ошибка при изменении PIN-кода", "unable_to_setup_pin_code": "Ошибка при создании PIN-кода", "unarchive": "Восстановить", + "unarchive_action_prompt": "{count} удалено из архива", "unarchived_count": "{count, plural, one {# объект возвращён} many {# объектов возвращено} other {# объекта возвращено}} из архива", "undo": "Отменить", "unfavorite": "Удалить из избранного", + "unfavorite_action_prompt": "{count} удалено из избранного", "unhide_person": "Показать человека", "unknown": "Неизвестно", "unknown_country": "Неизвестная страна", @@ -1872,18 +1953,23 @@ "unnamed_share": "Общий доступ без названия", "unsaved_change": "Несохранённое изменение", "unselect_all": "Отменить выделение", - "unselect_all_duplicates": "Отменить выбор всех дубликатов", + "unselect_all_duplicates": "Выбрать все для удаления", "unselect_all_in": "Отменить выделение в {group}", "unstack": "Разгруппировать", + "unstack_action_prompt": "{count} разгруппировано", "unstacked_assets_count": "{count, plural, one {Разгруппирован # объект} many {Разгруппировано # объектов} other {Разгруппировано # объекта}}", + "untagged": "Без тегов", "up_next": "Следующее", "updated_at": "Обновлён", "updated_password": "Пароль изменён", "upload": "Загрузить", + "upload_action_prompt": "{count} ожидают загрузки", "upload_concurrency": "Параллельность загрузки", + "upload_details": "Подробности загрузки", "upload_dialog_info": "Хотите загрузить выбранные объекты на сервер?", "upload_dialog_title": "Загрузить объект", "upload_errors": "Загрузка завершена с {count, plural, one {# ошибкой} other {# ошибками}}, обновите страницу, чтобы увидеть новые загруженные объекты.", + "upload_finished": "Загрузка завершена", "upload_progress": "Осталось {remaining, number} - Обработано {processed, number}/{total, number}", "upload_skipped_duplicates": "Пропущен{count, plural, one { # дубликат} many {о # дубликатов} other {о # дубликата}}", "upload_status_duplicates": "Дубликаты", @@ -1892,6 +1978,7 @@ "upload_success": "Загрузка прошла успешно. Обновите страницу, чтобы увидеть новые объекты.", "upload_to_immich": "Загрузка в Immich ({count})", "uploading": "Загружается", + "uploading_media": "Выполняется загрузка", "url": "URL", "usage": "Использование", "use_biometric": "Использовать биометрию", @@ -1912,6 +1999,7 @@ "user_usage_stats_description": "Посмотреть статистику использования аккаунта", "username": "Имя пользователя", "users": "Пользователи", + "users_added_to_album_count": "{count, plural, one {# пользователь добавлен} many {# пользователей добавлено} other {# пользователя добавлено}} к альбому", "utilities": "Утилиты", "validate": "Проверить", "validate_endpoint_error": "Введите корректный URL", @@ -1927,9 +2015,10 @@ "videos": "Видео", "videos_count": "{count, plural, one {# видео} other {# видео}}", "view": "Просмотр", - "view_album": "Просмотреть альбом", + "view_album": "Открыть альбом", "view_all": "Посмотреть всё", "view_all_users": "Показать всех пользователей", + "view_details": "Посмотреть подробности", "view_in_timeline": "Показать на временной шкале", "view_link": "Показать ссылку", "view_links": "Показать ссылки", @@ -1937,7 +2026,7 @@ "view_next_asset": "Показать следующий объект", "view_previous_asset": "Показать предыдущий объект", "view_qr_code": "Посмотреть QR код", - "view_stack": "Показать стек", + "view_stack": "Показать группу", "view_user": "Просмотреть пользователя", "viewer_remove_from_stack": "Убрать из группы", "viewer_stack_use_as_main_asset": "Использовать в качестве основного объекта", diff --git a/i18n/sk.json b/i18n/sk.json index cd64c52532..a5c4c08af9 100644 --- a/i18n/sk.json +++ b/i18n/sk.json @@ -14,6 +14,8 @@ "add_a_location": "Pridať polohu", "add_a_name": "Pridať meno", "add_a_title": "Pridať názov", + "add_birthday": "Pridať narodeniny", + "add_endpoint": "Pridať koncový bod", "add_exclusion_pattern": "Pridať vzor vylúčenia", "add_import_path": "Pridať cestu pre import", "add_location": "Pridať polohu", @@ -33,6 +35,7 @@ "added_to_favorites_count": "Pridané {count, number} do obľúbených", "admin": { "add_exclusion_pattern_description": "Pridávanie vzorov na vylúčenie. Globovanie pomocou *, ** a ? je podporované. Ak chcete ignorovať všetky súbory v akomkoľvek adresári s názvom \"Raw\", použite \"**/Raw/**\". Ak chcete ignorovať všetky súbory končiace na \".tif\", použite \"**/*.tif\". Ak chcete ignorovať absolútnu cestu, použite príkaz \"/cesta/k/ignorovanym/**\".", + "admin_user": "Správca", "asset_offline_description": "Táto položka externej knižnice sa už na disku nenachádza a bola presunutá do koša. Pokiaľ bol súbor presunutý v rámci knižnice, skontrolujte časovú os a vyhľadajte nové odpovedajúce položky. Ak chcete túto položku obnoviť, uistite sa, že je cesta k nižšie uvedenému súboru prístupná pre aplikáciu Immich a prehľadajte knižnicu.", "authentication_settings": "Overovanie a prihlásenie", "authentication_settings_description": "Spravovať heslo, protokol OAuth a ďalšie nastavenia overenia", @@ -42,27 +45,34 @@ "backup_database": "Vytvoriť výpis databázy", "backup_database_enable_description": "Povoliť výpisy z databázy", "backup_keep_last_amount": "Množstvo predchádzajúcich výpisov, ktoré sa majú zachovať", + "backup_onboarding_1_description": "externú kópiu v cloude alebo na inom fyzickom mieste.", + "backup_onboarding_2_description": "lokálne kópie na rôznych zariadeniach. To zahŕňa hlavné súbory a ich lokálnu zálohu.", + "backup_onboarding_3_description": "celkový počet kópií vašich údajov vrátane pôvodných súborov. Toto zahŕňa 1 externú kópiu a 2 lokálne kópie.", + "backup_onboarding_description": "Na ochranu vašich údajov sa odporúča stratégia zálohovania 3-2-1. Pre komplexné riešenie zálohovania by ste mali uchovávať kópie nahratých fotografií/videí, ako aj databázy Immich.", + "backup_onboarding_footer": "Ďalšie informácie o vytváraní zálohy Immich nájdete v dokumentácii.", + "backup_onboarding_parts_title": "Zálohovanie 3-2-1 zahŕňa:", + "backup_onboarding_title": "Zálohy", "backup_settings": "Nastavenia výpisu databázy", - "backup_settings_description": "Správa nastavení výpisu databázy.", + "backup_settings_description": "Spravovať nastavenia výpisu databázy.", "cleared_jobs": "Hotové úlohy pre: {job}", "config_set_by_file": "Konfigurácia je v súčasnosti nastavená konfiguračným súborom", "confirm_delete_library": "Naozaj chcete vymazať knižnicu {library}?", - "confirm_delete_library_assets": "Ste si istí, že chcete vymazať túto knižnicu? Tato operácia nenávratne odstráni {count, plural, one {# contained asset} other {all # contained assets}} súborov z Immich. Súbory budú ponechané na disku.", + "confirm_delete_library_assets": "Ste si istí, že chcete vymazať túto knižnicu? Tato operácia nenávratne odstráni {count, plural, one {# zahrnutú položku} few {# zahrnuté položky} other {všetkých # zahrnutých položiek}} z aplikácie Immich. Súbory budú ponechané na disku.", "confirm_email_below": "Pre potvrdenie zadajte \"{email}\" nižšie", "confirm_reprocess_all_faces": "Naozaj chcete spracovať všetky tváre znova? Tento proces vymaže pomenovaných ľudí.", - "confirm_user_password_reset": "Naozaj chcete resetovať heslo pre {user}?", + "confirm_user_password_reset": "Naozaj chcete obnoviť heslo pre {user}?", "confirm_user_pin_code_reset": "Ste si istí, že chcete opätovne nastaviť PIN kód používateľa {user}?", "create_job": "Vytvoriť úlohu", "cron_expression": "Výraz cron", "cron_expression_description": "Nastavte interval skenovania pomocou formátu cron. Pre viac informácií navštívte Crontab Guru", - "cron_expression_presets": "Presety cron výrazov", + "cron_expression_presets": "Predvoľby výrazov Cron", "disable_login": "Zakázať prihlásenie", - "duplicate_detection_job_description": "Spustiť strojové učenie na položkách pre detekciu podobných obrázkov. Spolieha sa na inteligentné vyhľadávanie", + "duplicate_detection_job_description": "Spustite strojové učenie na položkách pre detekciu podobných obrázkov. Spolieha sa na inteligentné vyhľadávanie", "exclusion_pattern_description": "Vylučovacie vzory Vám umožňujú ignorovať súbory a priečinky pri skenovaní Vašej knižnice. Toto je užitočné, ak máte priečinky obsahujúce súbory, ktoré nechcete importovať, napríklad RAW súbory.", - "external_library_management": "Správa Externej Knižnice", + "external_library_management": "Spravovanie externej knižnice", "face_detection": "Detekcia tvárí", - "face_detection_description": "Rozpoznajte tváre v položkách pomocou strojového učenia. V prípade videí sa berie do úvahy len náhľad. „Obnoviť“ (znovu) spracuje všetky položky. „Obnoviť“ dodatočne vymaže všetky aktuálne údaje o tvárach. „Chýbajúce“ zaradí do poradia položky aktív, ktoré ešte neboli spracované. Zistené tváre sa po dokončení rozpoznávania tvárí zaradia do poradia na rozpoznávanie tvárí, pričom sa zoskupia do existujúcich alebo nových osôb.", - "facial_recognition_job_description": "Zoskupovať rozpoznané tváre do osôb. Tento krok sa vykoná po dokončení rozpoznávania tvárí. „Obnoviť“ (znovu) zoskupí všetky tváre. „Chýbajúce“ zaradí tváre, ktoré nemajú pridelenú osobu.", + "face_detection_description": "Rozpoznajte tváre v položkách pomocou strojového učenia. V prípade videí sa berie do úvahy len náhľad. „Aktualizovať“ (znovu) spracuje všetky položky. „Obnoviť“ dodatočne vymaže všetky aktuálne údaje o tvárach. „Chýbajúce“ zaradí do poradia médiá, ktoré ešte neboli spracované. Zistené tváre sa po dokončení rozpoznávania tvárí zaradia do poradia na rozpoznávanie tvárí, pričom sa zoskupia do existujúcich alebo nových osôb.", + "facial_recognition_job_description": "Zoskupte rozpoznané tváre do osôb. Tento krok sa vykoná po dokončení rozpoznávania tvárí. „Obnoviť“ (znovu) zoskupí všetky tváre. „Chýbajúce“ zaradí tváre, ktoré nemajú pridelenú osobu.", "failed_job_command": "Príkaz {command} zlyhal pre úlohu: {job}", "force_delete_user_warning": "VAROVANIE: Toto okamžite odstráni používateľa a všetky položky. Tento krok nie je možné vrátiť späť a súbory nebude možné obnoviť.", "image_format": "Formát", @@ -84,7 +94,7 @@ "image_resolution_description": "Vyššie rozlíšenie môže zachovať viac detailov, ale kódovanie trvá dlhšie, súbory sú väčšie a môže to znížiť rýchlosť odozvy aplikácie.", "image_settings": "Obrázky", "image_settings_description": "Spravovať kvalitu a rozlíšenie generovaných obrázkov", - "image_thumbnail_description": "Malá miniatúra s odstránenými metadátami, používané pri zobrazovaní skupín fotiek ako na hlavnej časovej osi", + "image_thumbnail_description": "Malá miniatúra s odstránenými metadátami, ktorá sa používa pri prezeraní skupín fotografií ako na hlavnej časovej osi", "image_thumbnail_quality_description": "Kvalita miniatúry v stupnici od 1 do 100. Vyššia hodnota znamená lepšiu kvalitu, ale produkuje väčšie súbory a môže znížiť odozvu aplikácie.", "image_thumbnail_title": "Miniatúry", "job_concurrency": "Súbežnosť úlohy - {job}", @@ -92,7 +102,7 @@ "job_not_concurrency_safe": "Táto úloha nie je bezpečná pre súbežné spracovanie.", "job_settings": "Úlohy", "job_settings_description": "Spravovať súbežnosť úloh", - "job_status": "Stav Úloh", + "job_status": "Stav úloh", "jobs_delayed": "{jobCount, plural, one {# oneskorený} few {# oneskorené} other {# oneskorených}}", "jobs_failed": "{jobCount, plural, one {# neúspešný} few {# neúspešné} other {# neúspešných}}", "library_created": "Vytvorená knižnica: {library}", @@ -103,18 +113,18 @@ "library_scanning_enable_description": "Zapnúť pravidelné skenovanie knižnice", "library_settings": "Externá knižnica", "library_settings_description": "Spravovať nastavenia externej knižnice", - "library_tasks_description": "Vyhľadávanie nových alebo zmenených položiek v externých knižniciach", + "library_tasks_description": "Vyhľadajte nové alebo zmenené médiá v externých knižniciach", "library_watching_enable_description": "Sledovať externé knižnice pre zmeny v súboroch", "library_watching_settings": "Sledovanie knižnice (EXPERIMENTÁLNE)", "library_watching_settings_description": "Automaticky sledovať zmenené súbory", - "logging_enable_description": "Povoliť logovanie", - "logging_level_description": "Ak je povolené, akú úroveň logovania použiť.", - "logging_settings": "Logovanie", + "logging_enable_description": "Povoliť ukladanie záznamov", + "logging_level_description": "Ak je povolené, akú úroveň záznamov použiť.", + "logging_settings": "Ukladanie záznamov", "machine_learning_clip_model": "Model CLIP", "machine_learning_clip_model_description": "Názov modelu CLIP je uvedený tu. Pamätajte, že pri zmene modelu je nutné znovu spustiť úlohu 'Inteligentné vyhľadávanie' pre všetky obrázky.", "machine_learning_duplicate_detection": "Detekcia duplikátov", "machine_learning_duplicate_detection_enabled": "Povoliť detekciu duplikátov", - "machine_learning_duplicate_detection_enabled_description": "Ak je vypnuté, presne identické položky budú stále deduplikované.", + "machine_learning_duplicate_detection_enabled_description": "Ak je vypnuté, úplne identické položky budú stále deduplikované.", "machine_learning_duplicate_detection_setting_description": "Použiť CLIP embeddings na identifikáciu pravdepodobných duplikátov", "machine_learning_enabled": "Povoliť strojové učenie", "machine_learning_enabled_description": "Ak je vypnuté, všetky funkcie strojového učenia (ML) budú vypnuté, bez ohľadu na nastavenia nižšie.", @@ -124,10 +134,10 @@ "machine_learning_facial_recognition_model_description": "Modely sú zoradené od najväčšieho po najmenší. Väčšie modely sú pomalšie a vyžadujú viac pamäte, ale poskytujú lepšie výsledky. Pamätajte, že po zmene modelu je potrebné znovu spustiť úlohu detekcie tvárí pre všetky obrázky.", "machine_learning_facial_recognition_setting": "Povoliť rozpoznávanie tvárí", "machine_learning_facial_recognition_setting_description": "Ak je vypnuté, obrázky nebudú spracované pre rozpoznávanie tvárí a nebudú sa zobrazovať v sekcii Ľudia na stránke Preskúmať.", - "machine_learning_max_detection_distance": "Maximálna detekčná odchylka", - "machine_learning_max_detection_distance_description": "Maximálna odchylka medzi dvoma obrázkami, aby boli považované za duplikáty, v rozsahu od 0.001 do 0.1. Vyššie hodnoty odhalia viac duplikátov, ale môžu viesť k falošným pozitívam.", - "machine_learning_max_recognition_distance": "Maximálna rozpoznávacia odchylka", - "machine_learning_max_recognition_distance_description": "Maximálna odchylka medzi dvoma tvárami, aby boli považované za rovnakú osobu, v rozsahu od 0 do 2. Zníženie tejto hodnoty môže zabrániť označeniu dvoch ľudí za tú istú osobu, zatiaľ čo zvýšenie môže zabrániť označeniu jednej osoby za dve rôzne osoby. Pamätajte, že je jednoduchšie spojiť dvoch ľudí ako rozdeliť jednu osobu na dve, takže je lepšie voliť nižší prah, ak je to možné.", + "machine_learning_max_detection_distance": "Maximálna detekčná odchýlka", + "machine_learning_max_detection_distance_description": "Maximálna odchýlka medzi dvoma obrázkami, aby boli považované za duplikáty, v rozsahu od 0.001 do 0.1. Vyššie hodnoty odhalia viac duplikátov, ale môžu viesť k falošným pozitívam.", + "machine_learning_max_recognition_distance": "Maximálna rozpoznávacia odchýlka", + "machine_learning_max_recognition_distance_description": "Maximálna odchýlka medzi dvomi tvárami, aby boli považované za rovnakú osobu, v rozsahu od 0 do 2. Zníženie tejto hodnoty môže zabrániť označeniu dvoch ľudí za tú istú osobu, zatiaľ čo zvýšenie môže zabrániť označeniu jednej osoby za dve rôzne osoby. Pamätajte, že je jednoduchšie spojiť dvoch ľudí ako rozdeliť jednu osobu na dve, takže je lepšie voliť nižší prah, ak je to možné.", "machine_learning_min_detection_score": "Minimálne detekčné skóre", "machine_learning_min_detection_score_description": "Minimálne skóre dôveryhodnosti pre detekciu tváre v rozsahu od 0 do 1. Nižšie hodnoty odhalia viac tvárí, ale môžu viesť k falošným pozitivním výsledkom.", "machine_learning_min_recognized_faces": "Minimum rozpoznaných tvárí", @@ -139,15 +149,15 @@ "machine_learning_smart_search_enabled": "Povoliť inteligentné vyhľadávanie", "machine_learning_smart_search_enabled_description": "Ak je vypnuté, obrázky nebudú spracované pre inteligentné vyhľadávanie.", "machine_learning_url_description": "URL adresa servera strojového učenia. Ak je zadaných viacero adries URL, každý server bude testovaný postupne, kým jeden z nich neodpovie úspešne, v poradí od prvého po posledný. Servery, ktoré neodpovedajú, budú dočasne ignorované, kým nebudú opäť online.", - "manage_concurrency": "Správa súbežnosti", - "manage_log_settings": "Spravovať nastavenia logovania", + "manage_concurrency": "Spravovať súbežnosť", + "manage_log_settings": "Spravovať nastavenia ukladania záznamov", "map_dark_style": "Tmavý štýl", "map_enable_description": "Povoliť funkcie mapy", - "map_gps_settings": "Mapa & GPS", - "map_gps_settings_description": "Správa nastavení máp a GPS reverzného geokódovania", + "map_gps_settings": "Mapa a nastavenia GPS", + "map_gps_settings_description": "Spravovať nastavenia mapy a GPS (reverzné geokódovanie)", "map_implications": "Táto funkčnosť sa spolieha na externý servis spracovania mapových dlaždíc (tiles.immich.cloud)", "map_light_style": "Svetlý štýl", - "map_manage_reverse_geocoding_settings": "Správa nastavení Reverzného geokódovania", + "map_manage_reverse_geocoding_settings": "Spravovať nastavenia reverzného geokódovania", "map_reverse_geocoding": "Reverzné Geokódovanie", "map_reverse_geocoding_enable_description": "Povoliť reverzné geokódovanie", "map_reverse_geocoding_settings": "Reverzné geokódovanie", @@ -157,16 +167,30 @@ "memory_cleanup_job": "Vymazávanie spomienok", "memory_generate_job": "Vytváranie spomienok", "metadata_extraction_job": "Extrahovať metadáta", - "metadata_extraction_job_description": "Vytiahne metadáta z každej položky, ako napríklad GPS, tváre a rozlíšenie", + "metadata_extraction_job_description": "Získajte informácie o metadátach z každého média, ako sú GPS, tváre a rozlíšenie", "metadata_faces_import_setting": "Povoliť import tváre", - "metadata_faces_import_setting_description": "Importuj tváre z EXIF dát obrázkov a sidecar súborov", + "metadata_faces_import_setting_description": "Importovať tváre z EXIF údajov obrázka a pridružených súborov", "metadata_settings": "Metadáta", "metadata_settings_description": "Spravovať nastavenia metadát", "migration_job": "Migrácia", - "migration_job_description": "Migrácia miniatúr položiek a tvárí na najnovšiu štruktúru priečinkov", + "migration_job_description": "Presunúť miniatúry pre médiá a tváre do najnovšej štruktúry priečinkov", + "nightly_tasks_cluster_faces_setting_description": "Spustiť rozpoznávanie tváre na novo-zistených tvárach", + "nightly_tasks_cluster_new_faces_setting": "Zoskupiť nové tváre", + "nightly_tasks_database_cleanup_setting": "Úlohy čistenia databázy", + "nightly_tasks_database_cleanup_setting_description": "Vyčistiť databázu od starých, neplatných údajov", + "nightly_tasks_generate_memories_setting": "Vytvoriť spomienky", + "nightly_tasks_generate_memories_setting_description": "Vytvoriť nové spomienky z položiek", + "nightly_tasks_missing_thumbnails_setting": "Vytvoriť chýbajúce náhľady", + "nightly_tasks_missing_thumbnails_setting_description": "Zaradiť položky bez náhľadov do poradia na vytvorenie náhľadov", + "nightly_tasks_settings": "Nastavenia nočných úloh", + "nightly_tasks_settings_description": "Spravovať nočné úlohy", + "nightly_tasks_start_time_setting": "Čas spustenia", + "nightly_tasks_start_time_setting_description": "Čas, kedy server začne vykonávať nočné úlohy", + "nightly_tasks_sync_quota_usage_setting": "Synchronizovať využitie kvóty", + "nightly_tasks_sync_quota_usage_setting_description": "Aktualizovať kvótu úložiska používateľa na základe aktuálneho využitia", "no_paths_added": "Neboli pridané žiadne cesty", "no_pattern_added": "Nebol pridaný žiadny vzor", - "note_apply_storage_label_previous_assets": "Poznámka: Ak chcete použiť Štítkovanie úložiska na predtým nahrané aktíva, spustite príkaz", + "note_apply_storage_label_previous_assets": "Poznámka: Ak chcete použiť štítok úložiska na predtým nahrané položky, spustite príkaz", "note_cannot_be_changed_later": "POZNÁMKA: Toto nie je možné neskôr zmeniť!", "notification_email_from_address": "Z adresy", "notification_email_from_address_description": "E-mailová adresa odosielateľa, napríklad: \"Immich Foto Server \". Uistite sa, že používate adresu, z ktorej máte povolené odosielať e-maily.", @@ -189,19 +213,24 @@ "oauth_auto_register": "Automatická regristrácia", "oauth_auto_register_description": "Automatické zaregistrovanie nového požívateľa pri prihlásení pomocou OAuth", "oauth_button_text": "Text tlačítka", + "oauth_client_secret_description": "Vyžaduje sa, ak poskytovateľ OAuth nepodporuje PKCE (Proof Key for Code Exchange)", "oauth_enable_description": "Prihlásiť sa pomocou OAuth", "oauth_mobile_redirect_uri": "URI mobilného presmerovania", "oauth_mobile_redirect_uri_override": "Prepísanie URI mobilného presmerovania", "oauth_mobile_redirect_uri_override_description": "Povoľte, keď poskytovateľ protokolu OAuth nepovoľuje identifikátor URI pre mobilné zariadenia, napríklad ''{callback}''", + "oauth_role_claim": "Požiadavka na rolu", + "oauth_role_claim_description": "Automaticky udeliť prístup správcu na základe prítomnosti tejto požiadavky. Požiadavka môže mať príznak „user“ alebo „admin“.", "oauth_settings": "OAuth", "oauth_settings_description": "Spravovať nastavenia prihlásenia OAuth", - "oauth_settings_more_details": "Pre viac informácii o tejto funkcii, prejdite na docs.", + "oauth_settings_more_details": "Pre viac informácii o tejto funkcii, prejdite na dokumentáciu.", "oauth_storage_label_claim": "Nárokovať Štítok úložiska", - "oauth_storage_label_claim_description": "Automaticky nastaviť Štítok úložiska používateľa na hodnotu tohto nároku.", + "oauth_storage_label_claim_description": "Automaticky nastaviť štítok úložiska používateľa na hodnotu tohto nároku.", "oauth_storage_quota_claim": "Deklarácia kvóty úložiska", "oauth_storage_quota_claim_description": "Automaticky nastaviť kvótu úložiska používateľa na hodnotu tejto deklarácie.", "oauth_storage_quota_default": "Predvolený limit úložiska (GiB)", "oauth_storage_quota_default_description": "Kvóta v GiB, ktorá sa použije, ak nie je poskytnutá žiadna požiadavka.", + "oauth_timeout": "Časový limit požiadavky", + "oauth_timeout_description": "Časový limit pre požiadavky v milisekundách", "password_enable_description": "Prihlásiť sa pomocou emailu a hesla", "password_settings": "Prihlásenie cez heslo", "password_settings_description": "Spravovať nastavenia prihlásenia cez heslo", @@ -213,20 +242,20 @@ "registration_description": "Keďže ste prvým používateľom v systéme, budú vám pridelené správcovské práva na vykonávanie všetkých úloh a vrátane tvorby nových používateľov.", "require_password_change_on_login": "Vyžadovať od používateľa zmenu hesla pri prvom prihlásení", "reset_settings_to_default": "Obnoviť pôvodné nastavenia", - "reset_settings_to_recent_saved": "Obnoviť naposledy uložené nastavenia", + "reset_settings_to_recent_saved": "Nastavenia boli obnovené na posledné uložené nastavenia", "scanning_library": "Knižnica sa skenuje", "search_jobs": "Vyhľadať úlohy…", "send_welcome_email": "Odoslať uvítací e-mail", "server_external_domain_settings": "Externá doména", "server_external_domain_settings_description": "Verejná doména pre zdieľané odkazy, vrátane http(s)://", - "server_public_users": "Verejní užívatelia", - "server_public_users_description": "Všetci užívatelia (meno a email) sú uvedení pri pridávaní užívateľa do zdieľaných albumov. Ak je táto funkcia vypnutá, zoznam užívateľov bude dostupný iba správcom.", + "server_public_users": "Verejní používatelia", + "server_public_users_description": "Všetci používatelia (meno a email) sú uvedení pri pridávaní používateľa do zdieľaných albumov. Ak je táto funkcia vypnutá, zoznam používateľov bude dostupný iba správcom.", "server_settings": "Server", "server_settings_description": "Spravovať nastavenia servera", "server_welcome_message": "Uvítacia správa", "server_welcome_message_description": "Správa, ktorá sa zobrazí na prihlasovacej stránke.", - "sidecar_job": "Sidecar metadáta", - "sidecar_job_description": "Objavte alebo synchronizujte metadáta Sidecar zo súborového systému", + "sidecar_job": "Pridružené metadáta", + "sidecar_job_description": "Objavte alebo synchronizujte pridružené metadáta zo súborového systému", "slideshow_duration_description": "Čas zobrazenia obrázku v sekundách", "smart_search_job_description": "Spustite strojové učenie na médiách na podporu inteligentného vyhľadávania", "storage_template_date_time_description": "Časová pečiatka vytvorenia položky sa používa pre informácie o dátume a čase", @@ -238,50 +267,51 @@ "storage_template_migration_description": "Použite aktuálnu {template} na predtým nahrané médiá", "storage_template_migration_info": "Šablóna úložiska skonvertuje všetky prípony na malé písmená. Zmeny šablón sa budú vzťahovať iba na nové diela. Ak chcete šablónu spätne použiť na predtým nahrané médiá, spustite {job}.", "storage_template_migration_job": "Úloha migrácie šablóny úložiska", - "storage_template_more_details": "Ďalšie podrobnosti o tejto funkcii nájdete v Šablóna úložiska a jej dôsledky", + "storage_template_more_details": "Podrobnejšie informácie o tejto funkcii nájdete v časti šablóna úložiska a jej následky", + "storage_template_onboarding_description_v2": "Ak je táto funkcia zapnutá, automaticky usporiada súbory na základe šablóny definovanej používateľom. Ďalšie informácie nájdete v dokumentácii.", "storage_template_path_length": "Približný limit dĺžky cesty: {length, number}/{limit, number}", "storage_template_settings": "Šablóna úložiska", "storage_template_settings_description": "Spravujte štruktúru priečinkov a názov súboru odovzdaného média", - "storage_template_user_label": "{label} je Štítok úložiska používateľa", + "storage_template_user_label": "{label} je štítok úložiska používateľa", "system_settings": "Nastavenia systému", - "tag_cleanup_job": "Premazanie značiek", - "template_email_available_tags": "V šablóne môžeš použiť nasledujúce stítky: {tags}", - "template_email_if_empty": "Ak nie je zadaná žiadna šablóna, bude použitá predvolená šablóna.", + "tag_cleanup_job": "Prečistenie štítkov", + "template_email_available_tags": "V šablóne môžete použiť nasledujúce premenné: {tags}", + "template_email_if_empty": "Ak je šablóna prázdna, použije sa predvolený e-mail.", "template_email_invite_album": "Šablóna Pozvánky do albumu", "template_email_preview": "Ukážka", "template_email_settings": "Emailové šablóny", "template_email_update_album": "Upraviť šablónu albumu", - "template_email_welcome": "Šablóna uvítajúceho emailu", + "template_email_welcome": "Šablóna uvítacieho e-mailu", "template_settings": "Šablóna upozornení", "template_settings_description": "Spravovanie vlastných šablón upozornení", "theme_custom_css_settings": "Vlastné CSS", "theme_custom_css_settings_description": "CSS štýly umožňujú prispôsobiť dizajn Immich.", - "theme_settings": "Motívy", + "theme_settings": "Nastavenia témy", "theme_settings_description": "Spravovať prispôsobenie webového rozhrania Immich", - "thumbnail_generation_job": "Generovať Miniatúry", - "thumbnail_generation_job_description": "Generuje veľké, malé a rozostrení miniatúry pre každú položku, ako aj miniatúry pre každú osobu", + "thumbnail_generation_job": "Generovať miniatúry", + "thumbnail_generation_job_description": "Generujte veľké, malé a rozmazané miniatúry pre každú položku, ako aj miniatúry pre každú osobu", "transcoding_acceleration_api": "API pre akceleráciu", - "transcoding_acceleration_api_description": "Rozhranie API, ktoré bude interagovať s vaším zariadením s cieľom urýchliť prekódovanie. Toto nastavenie je „najlepšie úsilie“: pri zlyhaní sa vráti k softvérovému prekódovaniu. VP9 môže alebo nemusí fungovať v závislosti od vášho hardvéru.", + "transcoding_acceleration_api_description": "Rozhranie API, ktoré bude spolupracovať s vaším zariadením s cieľom urýchliť prekódovanie. Toto nastavenie je „najlepšie úsilie“: pri zlyhaní sa vráti k softvérovému prekódovaniu. VP9 môže alebo nemusí fungovať v závislosti od vášho hardvéru.", "transcoding_acceleration_nvenc": "NVENC (vyžaduje NVIDIA GPU)", "transcoding_acceleration_qsv": "Quick Sync (vyžaduje 7. generáciu Intel CPU alebo novšiu)", "transcoding_acceleration_rkmpp": "RKMPP (iba na Rockchip SOC)", "transcoding_acceleration_vaapi": "VAAPI", "transcoding_accepted_audio_codecs": "Akceptované zvukové kodeky", - "transcoding_accepted_audio_codecs_description": "Vyberte, ktoré zvukové kodeky nie je potrebné prekódovať. Používa sa len pre určité zásady prekódovania.", + "transcoding_accepted_audio_codecs_description": "Vyberte, ktoré zvukové kodeky nie je potrebné prekódovať. Používa sa len pre určité pravidlá prekódovania.", "transcoding_accepted_containers": "Akceptované kontajnery", - "transcoding_accepted_containers_description": "Vyberte, ktoré formáty kontajnerov nie je potrebné remuxovať na MP4. Používa sa len pre určité zásady prekódovania.", + "transcoding_accepted_containers_description": "Vyberte, ktoré formáty kontajnerov nie je potrebné remuxovať na MP4. Používa sa len pre určité pravidlá prekódovania.", "transcoding_accepted_video_codecs": "Akceptované video kodeky", - "transcoding_accepted_video_codecs_description": "Vyberte, ktoré video kodeky nie je potrebné prekódovať. Používa sa len pre určité zásady prekódovania.", + "transcoding_accepted_video_codecs_description": "Vyberte, ktoré video kodeky nie je potrebné prekódovať. Používa sa len pre určité pravidlá prekódovania.", "transcoding_advanced_options_description": "Možnosti, ktoré by väčšina používateľov nemala meniť", "transcoding_audio_codec": "Zvukový kodek", "transcoding_audio_codec_description": "Opus je najkvalitnejšia možnosť, ale má nižšiu kompatibilitu so starými zariadeniami alebo softvérom.", "transcoding_bitrate_description": "Videá presahujúce maximálnu bitovú rýchlosť alebo videá, ktoré nie sú v akceptovanom formáte", "transcoding_codecs_learn_more": "Ak sa chcete dozvedieť viac o tu použitej terminológii, pozrite si dokumentáciu FFmpeg pre kodek H.264, kodek HEVC a VP9 kodek.", "transcoding_constant_quality_mode": "Režim konštantnej kvality", - "transcoding_constant_quality_mode_description": "ICQ je lepšie ako CQP, ale niektoré zariadenia na hardvérovú akceleráciu tento režim nepodporujú. Nastavenie tejto možnosti uprednostní špecifikovaný režim pri použití kódovania založeného na kvalite. Ignorované spoločnosťou NVENC, pretože nepodporuje ICQ.", + "transcoding_constant_quality_mode_description": "ICQ je lepšie ako CQP, ale niektoré zariadenia na hardvérovú akceleráciu tento režim nepodporujú. Nastavenie tejto možnosti uprednostní špecifikovaný režim pri použití kódovania založeného na kvalite. Ignorované funkciou NVENC, pretože nepodporuje ICQ.", "transcoding_constant_rate_factor": "Faktor konštantnej rýchlosti (-crf)", "transcoding_constant_rate_factor_description": "Úroveň kvality videa. Typické hodnoty sú 23 pre H.264, 28 pre HEVC, 31 pre VP9 a 35 pre AV1. Nižšie je lepšie, ale vytvára väčšie súbory.", - "transcoding_disabled_description": "Neprekódujte žiadne videá, na niektorých klientoch môže prerušiť prehrávanie", + "transcoding_disabled_description": "Neprekódovať žiadne videá, na niektorých klientoch môže prerušiť prehrávanie", "transcoding_encoding_options": "Možnosti kódovania", "transcoding_encoding_options_description": "Nastavte kodeky, rozlíšenie, kvalitu a ďalšie možnosti pre kódované videá", "transcoding_hardware_acceleration": "Hardvérová akcelerácia", @@ -295,17 +325,17 @@ "transcoding_max_keyframe_interval": "Maximálny interval medzi kľúčovými snímkami", "transcoding_max_keyframe_interval_description": "Nastavuje maximálnu vzdialenosť medzi kľúčovými snímkami. Nižšie hodnoty zhoršujú účinnosť kompresie, ale zlepšujú časy vyhľadávania a môžu zlepšiť kvalitu v scénach s rýchlym pohybom. Hodnota 0 nastavuje túto hodnotu automaticky.", "transcoding_optimal_description": "Videá s vyšším ako cieľovým rozlíšením alebo videá, ktoré nie sú v prijateľnom formáte", - "transcoding_policy": "Politika prekódovania", + "transcoding_policy": "Pravidlá prekódovania", "transcoding_policy_description": "Nastavte, kedy bude video prekódované", "transcoding_preferred_hardware_device": "Uprednostňované hardvérové zariadenie", "transcoding_preferred_hardware_device_description": "Platí len pre VAAPI a QSV. Nastavuje uzol dri, ktorý sa používa na hardvérové prekódovanie.", - "transcoding_preset_preset": "Prednastavenie (-preset)", + "transcoding_preset_preset": "Predvoľba (-preset)", "transcoding_preset_preset_description": "Rýchlosť kompresie. Pomalšie predvoľby vytvárajú menšie súbory a zvyšujú kvalitu, keď sa zameriavajú na určitý dátový tok. VP9 ignoruje rýchlosti vyššie ako „rýchlejšie“.", "transcoding_reference_frames": "Referenčné snímky", "transcoding_reference_frames_description": "Počet snímok, na ktoré sa má odkazovať pri kompresii daného snímku. Vyššie hodnoty zvyšujú účinnosť kompresie, ale spomaľujú kódovanie. Hodnota 0 sa nastavuje automaticky.", "transcoding_required_description": "Iba videá, ktoré nie sú v prijatom formáte", - "transcoding_settings": "Transkódovania videa", - "transcoding_settings_description": "Spravujte, ktoré videá sa majú prekódovať a ako ich spracovať", + "transcoding_settings": "Nastavenia prekódovania videa", + "transcoding_settings_description": "Spravovať, ktoré videá sa majú prekódovať a ako sa majú spracovať", "transcoding_target_resolution": "Cieľové rozlíšenie", "transcoding_target_resolution_description": "Vyššie rozlíšenia môžu zachovať viac detailov, ale ich kódovanie trvá dlhšie, majú väčšiu veľkosť súborov a môžu znížiť odozvu aplikácie.", "transcoding_temporal_aq": "Časové AQ", @@ -314,30 +344,30 @@ "transcoding_threads_description": "Vyššie hodnoty vedú k rýchlejšiemu kódovaniu, ale ponechávajú serveru menej priestoru na spracovanie iných úloh počas aktivity. Táto hodnota by nemala byť väčšia ako počet jadier CPU. Maximalizuje využitie, ak je nastavená na hodnotu 0.", "transcoding_tone_mapping": "Tónové mapovanie", "transcoding_tone_mapping_description": "Snaží sa zachovať vzhľad videí HDR pri konverzii na SDR. Každý algoritmus robí rôzne kompromisy v oblasti farieb, detailov a jasu. Hable zachováva detaily, Mobius zachováva farby a Reinhard zachováva jas.", - "transcoding_transcode_policy": "Politika prekódovania", - "transcoding_transcode_policy_description": "Zásady, kedy sa má video prekódovať. Videá HDR sa vždy prekódujú (okrem prípadov, keď je prekódovanie vypnuté).", + "transcoding_transcode_policy": "Pravidlá prekódovania", + "transcoding_transcode_policy_description": "Pravidlá, kedy sa má video prekódovať. HDR videá sa prekódujú vždy (okrem prípadov, keď je prekódovanie vypnuté).", "transcoding_two_pass_encoding": "Dvojpriechodové kódovanie", - "transcoding_two_pass_encoding_setting_description": "Prekladajte v dvoch priechodoch, aby ste vytvorili lepšie zakódované videá. Keď je povolený maximálny dátový tok (vyžaduje sa na prácu s formátmi H.264 a HEVC), tento režim používa rozsah dátového toku na základe maximálneho dátového toku a ignoruje CRF. V prípade VP9 sa CRF môže použiť, ak je max bitrate vypnutý.", + "transcoding_two_pass_encoding_setting_description": "Prekódovať v dvoch fázach, aby sa vytvorili lepšie kódované videá. Keď je povolený maximálny dátový tok (vyžaduje sa na prácu s formátmi H.264 a HEVC), tento režim používa rozsah dátového toku na základe maximálneho dátového toku a ignoruje CRF. V prípade VP9 sa CRF môže použiť, ak je maximálny bitrate vypnutý.", "transcoding_video_codec": "Video kodek", - "transcoding_video_codec_description": "VP9 má vysokú účinnosť a kompatibilitu s webom, ale prekódovanie trvá dlhšie. HEVC má podobnú výkonnosť, ale nižšiu kompatibilitu s webom. H.264 je široko kompatibilný a rýchlo sa prekódováva, ale vytvára oveľa väčšie súbory. AV1 je najúčinnejší kodek, ale chýba mu podpora v starších zariadeniach.", + "transcoding_video_codec_description": "VP9 má vysokú účinnosť a kompatibilitu s webom, ale prekódovanie trvá dlhšie. HEVC má podobnú výkonnosť, ale nižšiu kompatibilitu s webom. H.264 je široko kompatibilný a rýchlo sa prekóduje, ale vytvára oveľa väčšie súbory. AV1 je najúčinnejší kodek, ale chýba mu podpora v starších zariadeniach.", "trash_enabled_description": "Povoliť funkcie koša", "trash_number_of_days": "Počet dní", - "trash_number_of_days_description": "Počet dní, počas ktorých sa má majetok ponechať v koši pred jeho trvalým odstránením", + "trash_number_of_days_description": "Počet dní, počas ktorých sa majú médiá ponechať v koši pred ich trvalým odstránením", "trash_settings": "Kôš", "trash_settings_description": "Spravovať nastavenia koša", - "user_cleanup_job": "Premazanie používateľov", - "user_delete_delay": "Konto {user} a jeho médiá budú podľa plánu natrvalo vymazané za {delay, plural, one {# day} other {# days}}.", - "user_delete_delay_settings": "Odstrániť oneskorenie", - "user_delete_delay_settings_description": "Počet dní po odstránení na trvalé vymazanie účtu a aktív používateľa. Úloha odstraňovania používateľov sa spúšťa o polnoci, aby sa skontrolovali používatelia, ktorí sú pripravení na odstránenie. Zmeny tohto nastavenia sa vyhodnotia pri ďalšom spustení.", - "user_delete_immediately": "Konto a médiá {user} budú zaradené do frontu na trvalé vymazanie okamžite.", - "user_delete_immediately_checkbox": "Používateľ a médiá budú zaradení do frontu na okamžité vymazanie", + "user_cleanup_job": "Prečistenie používateľov", + "user_delete_delay": "Konto {user} a jeho médiá budú podľa plánu natrvalo vymazané za {delay, plural, one {# deň} few {# dni} other {# dní}}.", + "user_delete_delay_settings": "Oneskorenie vymazania", + "user_delete_delay_settings_description": "Počet dní, po ktorých sa po odstránení používateľa natrvalo odstráni jeho účet a položky. Úloha odstraňovania používateľov sa spúšťa o polnoci, aby sa skontrolovali používatelia, ktorí sú pripravení na odstránenie. Zmeny tohto nastavenia sa vyhodnotia pri ďalšom spustení.", + "user_delete_immediately": "Konto a médiá používateľa {user} budú zaradené do poradia na trvalé vymazanie okamžite.", + "user_delete_immediately_checkbox": "Zaradiť používateľa a položky do poradia na okamžité vymazanie", "user_details": "Podrobnosti o používateľovi", - "user_management": "Správa používateľov", - "user_password_has_been_reset": "Heslo používateľa bolo resetované:", + "user_management": "Spravovanie používateľov", + "user_password_has_been_reset": "Heslo používateľa bolo obnovené:", "user_password_reset_description": "Poskytnite používateľovi dočasné heslo a informujte ho, že si ho bude musieť zmeniť pri ďalšom prihlásení.", "user_restore_description": "{user} bude účet obnovený.", "user_restore_scheduled_removal": "Obnoviť používateľa - plánované odstránenie na {date, date, long}", - "user_settings": "Používateľ", + "user_settings": "Nastavenia používateľa", "user_settings_description": "Spravovať používateľské nastavenia", "user_successfully_removed": "Používateľ {email} bol úspešne odstránený.", "version_check_enabled_description": "Povoliť kontrolu verzie", @@ -351,11 +381,19 @@ "admin_password": "Administrátorské heslo", "administration": "Administrácia", "advanced": "Pokročilé", - "advanced_settings_log_level_title": "Úroveň logovania: {level}", - "advanced_settings_prefer_remote_subtitle": "Niektoré zariadenia sú extrémne pomalé pre načítavanie miniatúr z fotiek na zariadení. Povoľte toto nastavenie aby sa namiesto toho načítavali obrázky zo servera.", - "advanced_settings_prefer_remote_title": "Preferovať vzdialené obrázky", + "advanced_settings_beta_timeline_subtitle": "Vyskúšajte prostredie novej aplikácie", + "advanced_settings_beta_timeline_title": "Beta verzia časovej osi", + "advanced_settings_enable_alternate_media_filter_subtitle": "Túto možnosť použite na filtrovanie médií počas synchronizácie na základe alternatívnych kritérií. Túto možnosť vyskúšajte len vtedy, ak máte problémy s detekciou všetkých albumov v aplikácii.", + "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTÁLNE] Použiť alternatívny filter synchronizácie albumu zariadenia", + "advanced_settings_log_level_title": "Úroveň ukladania záznamov: {level}", + "advanced_settings_prefer_remote_subtitle": "V niektorých zariadeniach sa miniatúry z miestnych položiek načítavajú veľmi pomaly. Aktivovaním tohto nastavenia sa namiesto toho načítajú vzdialené obrázky.", + "advanced_settings_prefer_remote_title": "Uprednostniť vzdialené obrázky", + "advanced_settings_proxy_headers_subtitle": "Určite hlavičky proxy servera, ktoré by mal Immich posielať s každou požiadavkou na sieť", + "advanced_settings_proxy_headers_title": "Proxy hlavičky", "advanced_settings_self_signed_ssl_subtitle": "Preskakuje overovanie SSL certifikátom zo strany servera. Vyžaduje sa pre samo-podpísané certifikáty.", "advanced_settings_self_signed_ssl_title": "Povoliť samo-podpísané SSL certifikáty", + "advanced_settings_sync_remote_deletions_subtitle": "Automaticky vymazať alebo obnoviť položku na tomto zariadení, keď sa táto akcia vykoná na webe", + "advanced_settings_sync_remote_deletions_title": "Synchronizovať vzdialené vymazania [EXPERIMENTÁLNE]", "advanced_settings_tile_subtitle": "Pokročilé nastavenia používateľa", "advanced_settings_troubleshooting_subtitle": "Povoliť ďalšie funkcie pre opravu chýb", "advanced_settings_troubleshooting_title": "Oprava chýb", @@ -363,10 +401,11 @@ "age_year_months": "Vek 1 rok, {months, plural, one {# month} other {# months}}", "age_years": "{years, plural, other {Vek #}}", "album_added": "Album bol pridaný", - "album_added_notification_setting_description": "Obdržať upozornenie emailom, keď ste pridaní do zdieľaného albumu", + "album_added_notification_setting_description": "Obdržať upozornenie emailom, keď vás pridajú do zdieľaného albumu", "album_cover_updated": "Obal albumu aktualizovaný", "album_delete_confirmation": "Ste si istý, že chcete odstrániť album {album}?", "album_delete_confirmation_description": "Ak je tento album zdieľaný, ostatní používatelia k nemu už nebudú mať prístup.", + "album_deleted": "Album bol vymazaný", "album_info_card_backup_album_excluded": "VYLÚČENÉ", "album_info_card_backup_album_included": "ZAHRNUTÉ", "album_info_updated": "Informácie albumu aktualizované", @@ -376,6 +415,7 @@ "album_options": "Nastavenia albumu", "album_remove_user": "Odstrániť používateľa?", "album_remove_user_confirmation": "Ste si istý, že chcete odstrániť používateľa {user}?", + "album_search_not_found": "Neboli nájdené žiadne albumy zodpovedajúce vášmu hľadaniu", "album_share_no_users": "Vyzerá to, že ste tento album zdieľali so všetkými používateľmi alebo nemáte žiadneho používateľa, s ktorým by ste ho mohli zdieľať.", "album_updated": "Album bol aktualizovaný", "album_updated_setting_description": "Obdržať e-mailové upozornenie, keď v zdieľanom albume pribudnú nové položky", @@ -391,15 +431,19 @@ "album_viewer_page_share_add_users": "Pridať používateľov", "album_with_link_access": "Umožnite komukoľvek s odkazom pozrieť si fotky a ľudí v tomto albume.", "albums": "Albumy", - "albums_count": "{count, plural, one {{count, number} Album} other {{count, number} Albumov}}", + "albums_count": "{count, plural, one {{count, number} album} few {{count, number} albumy} other {{count, number} albumov}}", + "albums_default_sort_order": "Predvolené poradie albumov", + "albums_default_sort_order_description": "Počiatočné poradie triedenia položiek pri vytváraní nových albumov.", + "albums_feature_description": "Zbierky médií, ktoré možno zdieľať s ostatnými používateľmi.", + "albums_on_device_count": "Albumy v zariadení ({count})", "all": "Všetko", "all_albums": "Všetky albumy", "all_people": "Všetci ľudia", "all_videos": "Všetky videa", "allow_dark_mode": "Povoliť tmavý režim", "allow_edits": "Povoliť úpravy", - "allow_public_user_to_download": "Povoľte verejnému používateľovi sťahovať", - "allow_public_user_to_upload": "Umožniť verejnému používateľovi nahrávať", + "allow_public_user_to_download": "Povoliť verejnému používateľovi stiahnutie", + "allow_public_user_to_upload": "Umožniť verejnému používateľovi nahrať", "alt_text_qr_code": "Obrázok QR kódu", "anti_clockwise": "Proti smeru hodinových ručičiek", "api_key": "API Klúč", @@ -409,9 +453,10 @@ "app_bar_signout_dialog_content": "Skutočne sa chcete odhlásiť?", "app_bar_signout_dialog_ok": "Áno", "app_bar_signout_dialog_title": "Odhlásiť sa", - "app_settings": "Nastavenia Aplikácie", + "app_settings": "Nastavenia aplikácie", "appears_in": "Vyskytuje sa v", - "archive": "Archivovať", + "archive": "Archív", + "archive_action_prompt": "{count} pridaných do archívu", "archive_or_unarchive_photo": "Archivácia alebo odarchivovanie fotografie", "archive_page_no_archived_assets": "Žiadne archivované médiá", "archive_page_title": "Archív ({count})", @@ -439,31 +484,46 @@ "asset_list_settings_title": "Fotografická mriežka", "asset_offline": "Médium je offline", "asset_offline_description": "Toto externý obsah sa už nenachádza na disku. Požiadajte o pomoc svojho správcu Immich.", + "asset_restored_successfully": "Položky boli úspešne obnovené", "asset_skipped": "Preskočené", "asset_skipped_in_trash": "V koši", "asset_uploaded": "Nahrané", "asset_uploading": "Nahráva sa…", - "asset_viewer_settings_title": "Zobrazovač položiek", + "asset_viewer_settings_subtitle": "Spravujte nastavenia prehliadača galérie", + "asset_viewer_settings_title": "Prehliadač médií", "assets": "Položky", "assets_added_count": "{count, plural, one {Pridaná # položka} few {Pridané # položky} other {Pridaných # položek}}", "assets_added_to_album_count": "Do albumu {count, plural, one {bola pridaná # položka} few {boli pridané # položky} other {bolo pridaných # položiek}}", - "assets_added_to_name_count": "{count, plural, one {Pridaná # položka} few {Pridané # položky} other {Pridaných # položiek}} do {hasName, select, true {alba {name}} other {nového albumu}}", + "assets_cannot_be_added_to_album_count": "{count, plural, one {položku} other {položiek}} nie je možné pridať do albumu", "assets_count": "{count, plural, one {# položka} few {# položky} other {# položiek}}", + "assets_deleted_permanently": "{count} položka(iek) natrvalo vymazaná(ých)", + "assets_deleted_permanently_from_server": "{count} položka(iek) natrvalo vymazaná(ých) zo servera Immich", + "assets_downloaded_failed": "{count, plural, one {Stiahnutý # súbor - {error} súbor zlyhal} few {Stiahnuté # súbory - {error} súbory zlyhali} other {Stiahnutých # súborov - {error} súborov zlyhalo}}", + "assets_downloaded_successfully": "{count, plural, one {# súbor bol úspešne stiahnutý} few {# súbory boli úspešne stiahnuté} other {# súborov bolo úspešne stiahnutých}}", "assets_moved_to_trash_count": "Do koša {count, plural, one {bola presunutá # položka} few {boli presunuté # položky} other {bolo presunutých # položiek}}", "assets_permanently_deleted_count": "Trvalo {count, plural, one {vymazaná # položka} few {vymazané # položky} other {vymazaných # položiek}}", "assets_removed_count": "{count, plural, one {Odstránená # položka} few {Odstránené # položky} other {Odstránených # položiek}}", + "assets_removed_permanently_from_device": "{count} položka(iek) natrvalo vymazaná(ých) z vášho zariadenia", "assets_restore_confirmation": "Naozaj chcete obnoviť všetky vyhodené položky? Túto akciu nie je možné vrátiť späť! Upozorňujeme, že týmto spôsobom nie je možné obnoviť žiadne offline položky.", "assets_restored_count": "{count, plural, one {Obnovená # položka} few {Obnovené # položky} other {Obnovených # položiek}}", - "assets_restored_successfully": "{count} medií úspešne obnovených", + "assets_restored_successfully": "{count} médií úspešne obnovených", + "assets_trashed": "{count} položka(iek) vyhodená(ých) do koša", "assets_trashed_count": "{count, plural, one {Odstránená # položka} few {Odstránené # položky} other {Odstránených # položiek}}", - "assets_were_part_of_album_count": "{count, plural, one {Položka bola} other {Položky boli}} súčasťou albumu", + "assets_trashed_from_server": "{count} položka(iek) vyhodená(ých) do koša zo servera Immich", + "assets_were_part_of_album_count": "{count, plural, one {Položka už bola} other {Položky už boli}} súčasťou albumu", "authorized_devices": "Autorizované zariadenia", + "automatic_endpoint_switching_subtitle": "Pripojiť sa lokálne prostredníctvom určeného pripojenia Wi-Fi, ak je k dispozícii, a používať alternatívne pripojenia inde", + "automatic_endpoint_switching_title": "Automatické prepínanie URL adresy", + "autoplay_slideshow": "Automatické prehrávanie prezentácie", "back": "Späť", "back_close_deselect": "Späť, zavrieť alebo zrušiť výber", + "background_location_permission": "Povolenie na určenie polohy na pozadí", + "background_location_permission_content": "Aby bolo možné prepínať siete pri spustení na pozadí, musí mať aplikácia Immich *vždy* presný prístup k polohe, aby mohla prečítať názov siete Wi-Fi", + "backup": "Zálohovanie", "backup_album_selection_page_albums_device": "Albumy v zariadení ({count})", "backup_album_selection_page_albums_tap": "Ťuknutím na položku ju zahrniete, dvojitým ťuknutím ju vylúčite", "backup_album_selection_page_assets_scatter": "Súbory môžu byť roztrúsené vo viacerých albumoch. To umožňuje zahrnúť alebo vylúčiť albumy počas procesu zálohovania.", - "backup_album_selection_page_select_albums": "Vybrané albumy", + "backup_album_selection_page_select_albums": "Vybrať albumy", "backup_album_selection_page_selection_info": "Informácie o výbere", "backup_album_selection_page_total_assets": "Celkový počet jedinečných súborov", "backup_all": "Všetko", @@ -485,7 +545,7 @@ "backup_controller_page_background_charging": "Len počas nabíjania", "backup_controller_page_background_configure_error": "Nepodarilo sa nakonfigurovať službu na pozadí", "backup_controller_page_background_delay": "Oneskorenie zálohovania nových médií: {duration}", - "backup_controller_page_background_description": "Povoľte službu na pozadí na automatické zálohovanie všetkých nových aktív bez nutnosti otvorenia aplikácie", + "backup_controller_page_background_description": "Povoľte službu na pozadí na automatické zálohovanie všetkých nových položiek bez nutnosti otvorenia aplikácie", "backup_controller_page_background_is_off": "Automatické zálohovanie na pozadí je vypnuté", "backup_controller_page_background_is_on": "Automatické zálohovanie na pozadí je zapnuté", "backup_controller_page_background_turn_off": "Vypnúť zálohovanie na pozadí", @@ -503,7 +563,7 @@ "backup_controller_page_info": "Informácie o zálohovaní", "backup_controller_page_none_selected": "Žiadne vybrané", "backup_controller_page_remainder": "Zostáva", - "backup_controller_page_remainder_sub": "Zostávajúce fotografie a videá, ktoré sa majú zálohovať z výbraných albumov", + "backup_controller_page_remainder_sub": "Zostávajúce fotografie a videá, ktoré sa majú zálohovať z výberu", "backup_controller_page_server_storage": "Serverové úložisko", "backup_controller_page_start_backup": "Spustiť zálohovanie", "backup_controller_page_status_off": "Automatické zálohovanie na popredí je vypnuté", @@ -521,22 +581,29 @@ "backup_manual_success": "Úspech", "backup_manual_title": "Stav nahrávania", "backup_options_page_title": "Možnosti zálohovania", - "backward": "Spätne", + "backup_setting_subtitle": "Spravovať nastavenia odosielania na pozadí a v popredí", + "backward": "Dozadu", + "beta_sync": "Stav synchronizácie verzie Beta", + "beta_sync_subtitle": "Spravovať nový systém synchronizácie", + "biometric_auth_enabled": "Biometrické overovanie je povolené", + "biometric_locked_out": "Ste vymknutí z biometrického overovania", + "biometric_no_options": "Nie sú k dispozícii žiadne biometrické možnosti", + "biometric_not_available": "Biometrické overenie nie je v tomto zariadení k dispozícii", "birthdate_saved": "Dátum narodenia bol úspešne uložený", "birthdate_set_description": "Dátum narodenia sa používa na výpočet veku tejto osoby v čase fotografie.", "blurred_background": "Rozmazané pozadie", "bugs_and_feature_requests": "Chyby a požiadavky na funkcie", "build": "Zostava", "build_image": "Obraz zostavy", - "bulk_delete_duplicates_confirmation": "Naozaj chcete hromadne odstrániť {count, plural, one {# duplikátnu položku} few {# duplikáte položky} other {# duplikátnych položiek}}? Týmto sa zachová najväčšia položka z každej skupiny a všetky ostatné duplikáty sa natrvalo odstránia. Túto akciu nie je možné vrátiť späť!", - "bulk_keep_duplicates_confirmation": "Naozaj chceš ponechať {count, plural, one {# duplicitný súbor} other {# duplicitné súbory}}? Týmto sa vysporiadaš so všetkými duplicitnými skupinami bez mazania súborov.", - "bulk_trash_duplicates_confirmation": "Naozaj chcete hromadne vymazať {count, plural, one {# duplicitný súbor} other {# duplicitné súbory}}? Týmto si ponecháš z každej skupiny najväčší súbor a vymažeš všetky ostatné duplicitné súbory v skupine.", + "bulk_delete_duplicates_confirmation": "Naozaj chcete hromadne odstrániť {count, plural, one {# duplikátnu položku} few {# duplikátne položky} other {# duplikátnych položiek}}? Týmto sa zachová najväčšia položka z každej skupiny a všetky ostatné duplikáty sa natrvalo odstránia. Túto akciu nie je možné vrátiť späť!", + "bulk_keep_duplicates_confirmation": "Naozaj chcete ponechať {count, plural, one {# duplicitnú položku} few {# duplicitné položky} other {# duplicitných položiek}}? Týmto sa vyriešia všetky duplicitné skupiny bez toho, aby sa čokoľvek odstránilo.", + "bulk_trash_duplicates_confirmation": "Naozaj chcete hromadne vymazať {count, plural, one {# duplicitnú položku} few {# duplicitné položky} other {# duplicitných položiek}}? Týmto sa zachová najväčšia položka z každej skupiny a všetky ostatné duplicitné položky sa vyhodia.", "buy": "Kúpiť Immich", "cache_settings_clear_cache_button": "Vymazať vyrovnávaciu pamäť", "cache_settings_clear_cache_button_title": "Vymaže vyrovnávaciu pamäť aplikácie. To výrazne ovplyvní výkon aplikácie, kým sa vyrovnávacia pamäť neobnoví.", "cache_settings_duplicated_assets_clear_button": "VYČISTIŤ", - "cache_settings_duplicated_assets_subtitle": "Fotky a videá ktoré sú na čiernej listine zvolené aplikáciou", - "cache_settings_duplicated_assets_title": "Duplikáty ({count})", + "cache_settings_duplicated_assets_subtitle": "Fotografie a videá, ktoré aplikácia ignoruje podľa zoznamu", + "cache_settings_duplicated_assets_title": "Duplicitné položky ({count})", "cache_settings_statistics_album": "Knižnica náhľadov", "cache_settings_statistics_full": "Kompletné fotografie", "cache_settings_statistics_shared": "Zdieľané náhľady albumov", @@ -552,9 +619,12 @@ "cancel": "Zrušiť", "cancel_search": "Zrušiť vyhľadávanie", "canceled": "Zrušené", + "canceling": "Ruší sa", "cannot_merge_people": "Nie je možné zlúčiť ľudí", "cannot_undo_this_action": "Túto akciu nemôžete vrátiť späť!", "cannot_update_the_description": "Popis nie je možné aktualizovať", + "cast": "Prenos (cast)", + "cast_description": "Nastavte dostupné ciele prenosu", "change_date": "Upraviť dátum", "change_description": "Zmeniť popis", "change_display_order": "Zmeniť poradie zobrazenia", @@ -570,9 +640,11 @@ "change_password_form_password_mismatch": "Heslá sa nezhodujú", "change_password_form_reenter_new_password": "Znova zadajte nové heslo", "change_pin_code": "Zmeniť PIN kód", - "change_your_password": "Zmeňte si heslo", + "change_your_password": "Zmeniť heslo", "changed_visibility_successfully": "Viditeľnosť bola úspešne zmenená", + "check_corrupt_asset_backup": "Skontrolovať, či nie sú poškodené zálohy položiek", "check_corrupt_asset_backup_button": "Vykonať kontrolu", + "check_corrupt_asset_backup_description": "Spustiť túto kontrolu len cez Wi-Fi a po zálohovaní všetkých položiek. Tento postup môže trvať niekoľko minút.", "check_logs": "Skontrolovať logy", "choose_matching_people_to_merge": "Vyberte rovnakých ľudí na zlúčenie", "city": "Mesto", @@ -584,6 +656,11 @@ "client_cert_dialog_msg_confirm": "OK", "client_cert_enter_password": "Zadať heslo", "client_cert_import": "Importovať", + "client_cert_import_success_msg": "Certifikát klienta je naimportovaný", + "client_cert_invalid_msg": "Neplatný súbor certifikátu alebo nesprávne heslo", + "client_cert_remove_msg": "Certifikát klienta je odstránený", + "client_cert_subtitle": "Podporuje iba formát PKCS12 (.p12, .pfx). Importovanie/odstránenie certifikátu je k dispozícii len pred prihlásením", + "client_cert_title": "SSL certifikát klienta", "clockwise": "V smere hodinových ručičiek", "close": "Zatvoriť", "collapse": "Zbaliť", @@ -607,7 +684,8 @@ "confirm_tag_face": "Chcete označiť túto tvár ako {name}?", "confirm_tag_face_unnamed": "Chcete označiť túto tvár?", "connected_device": "Pripojené zariadenie", - "contain": "Obsiahnúť", + "connected_to": "Pripojené k", + "contain": "Prispôsobiť", "context": "Kontext", "continue": "Pokračovať", "control_bottom_app_bar_create_new_album": "Vytvoriť nový album", @@ -616,6 +694,7 @@ "control_bottom_app_bar_edit_location": "Upraviť polohu", "control_bottom_app_bar_edit_time": "Upraviť dátum a čas", "control_bottom_app_bar_share_link": "Zdieľať odkaz", + "control_bottom_app_bar_share_to": "Zdieľať cez", "control_bottom_app_bar_trash_from_immich": "Presunúť do koša", "copied_image_to_clipboard": "Obrázok skopírovaný do schránky.", "copied_to_clipboard": "Skopírované do schránky!", @@ -627,7 +706,7 @@ "copy_password": "Skopírovať heslo", "copy_to_clipboard": "Skopírovať do schránky", "country": "Krajina", - "cover": "Titulka", + "cover": "Vyplniť", "covers": "Dlaždice", "create": "Vytvoriť", "create_album": "Vytvoriť album", @@ -635,15 +714,15 @@ "create_library": "Vytvoriť knižnicu", "create_link": "Vytvoriť odkaz", "create_link_to_share": "Vytvoriť odkaz na zdieľanie", - "create_link_to_share_description": "Umožniť každému kto má odkaz zobraziť vybrané fotografie", + "create_link_to_share_description": "Umožniť každému, kto má odkaz, zobraziť vybrané fotografie", "create_new": "VYTVORIŤ NOVÉ", "create_new_person": "Vytvoriť novú osobu", "create_new_person_hint": "Priradiť vybrané položky novej osobe", "create_new_user": "Vytvorenie nového používateľa", "create_shared_album_page_share_add_assets": "Pridať položky", "create_shared_album_page_share_select_photos": "Vybrať fotografie", - "create_tag": "Vytvoriť značku", - "create_tag_description": "Vytvorenie nového štítku. Pre Vnorené štítky, prosím, zadaj celú cestu štítku, vrátane lomítok vpred.", + "create_tag": "Vytvoriť štítok", + "create_tag_description": "Vytvorte nový štítok. V prípade vnorených štítkov zadajte celú cestu k štítku vrátane lomiek.", "create_user": "Vytvoriť používateľa", "created": "Vytvorené", "created_at": "Vytvorené", @@ -652,11 +731,13 @@ "current_device": "Súčasné zariadenie", "current_pin_code": "Aktuálny PIN kód", "current_server_address": "Aktuálna adresa servera", - "custom_locale": "Vlastná Lokalizácia", + "custom_locale": "Vlastné nastavenie jazyka", "custom_locale_description": "Formátovanie dátumov a čísel podľa jazyka a regiónu", + "custom_url": "Vlastná URL adresa", "daily_title_text_date": "EEEE, d. MMMM", "daily_title_text_date_year": "EEEE, d. MMMM y", - "dark": "Tmavý", + "dark": "Tmavá", + "dark_theme": "Prepnúť tmavú tému", "date_after": "Dátum po", "date_and_time": "Dátum a Čas", "date_before": "Dátum pred", @@ -669,14 +750,16 @@ "deduplication_criteria_2": "Počet EXIF údajov", "deduplication_info": "Info o deduplikácii", "deduplication_info_description": "Na automatický predvýber položiek a hromadné odstránenie duplicít, sa pozeráme do:", - "default_locale": "Predvolená Lokalizácia", - "default_locale_description": "Formátovanie dátumu a čísel podľa lokalizácie vášho prehliadača", + "default_locale": "Predvolené miestne nastavenie", + "default_locale_description": "Formátovanie dátumov a čísel na základe miestneho nastavenia prehliadača", "delete": "Vymazať", + "delete_action_confirmation_message": "Naozaj chcete túto položku odstrániť? Táto akcia presunie položku do koša na serveri a zobrazí sa otázka, či ju chcete odstrániť aj lokálne", + "delete_action_prompt": "{count} vymazaných", "delete_album": "Odstrániť album", "delete_api_key_prompt": "Naozaj chcete odstrániť tento API kľúč?", "delete_dialog_alert": "Tieto položky budú natrvalo odstránené z aplikácie Immich a z vášho zariadenia", "delete_dialog_alert_local": "Tieto položky budú permanentne vymazané z vašeho zariadenia, ale budú stále k dispozícií na serveri Immich", - "delete_dialog_alert_local_non_backed_up": "Niektoré položky nie sú zálohované na Immichi a budú permanentne vymazané z vášho zariadenia", + "delete_dialog_alert_local_non_backed_up": "Niektoré položky nie sú zálohované na Immich a budú permanentne odstránené z vášho zariadenia", "delete_dialog_alert_remote": "Tieto položky budú permanentne vymazané zo serveru Immich", "delete_dialog_ok_force": "Napriek tomu vymazať", "delete_dialog_title": "Vymazať natrvalo", @@ -685,23 +768,27 @@ "delete_key": "Odstrániť kľúč", "delete_library": "Vymazať knižnicu", "delete_link": "Odstrániť odkaz", + "delete_local_action_prompt": "{count} vymazané lokálne", "delete_local_dialog_ok_backed_up_only": "Vymazať len zálohované", "delete_local_dialog_ok_force": "Napriek tomu vymazať", "delete_others": "Vymazať ostatné", + "delete_permanently": "Natrvalo odstrániť", + "delete_permanently_action_prompt": "{count} natrvalo odstránených", "delete_shared_link": "Odstrániť zdieľaný odkaz", "delete_shared_link_dialog_title": "Odstrániť zdieľaný odkaz", - "delete_tag": "Odstrániť označenie", + "delete_tag": "Odstrániť štítok", "delete_tag_confirmation_prompt": "Naozaj chcete odstrániť štítok menom {tagName}?", "delete_user": "Vymazať používateľa", "deleted_shared_link": "Vymazaný zdieľaný odkaz", - "deletes_missing_assets": "Chýbajú vymazané položky z disku", + "deletes_missing_assets": "Odstráni položky chýbajúce na disku", "description": "Popis", "description_input_hint_text": "Pridať popis...", "description_input_submit_error": "Chyba pri aktualizovaní popisu, zobrazte log pre viac detailov", + "deselect_all": "Zrušiť výber všetkých", "details": "Podrobnosti", "direction": "Smer", "disabled": "Vypnuté", - "disallow_edits": "Zakázať editovanie", + "disallow_edits": "Zakázať úpravy", "discord": "Discord", "discover": "Objaviť", "discovered_devices": "Objavené zariadenia", @@ -715,28 +802,34 @@ "documentation": "Dokumentácia", "done": "Hotovo", "download": "Stiahnuť", + "download_action_prompt": "Sťahuje sa {count} položiek", "download_canceled": "Stiahnutie zrušené", "download_complete": "Stiahnutie dokončené", + "download_enqueue": "Stiahnutie v poradí", "download_error": "Chyba sťahovania", "download_failed": "Stiahnutie sa nepodarilo", "download_finished": "Stiahnutie dokončené", "download_include_embedded_motion_videos": "Vložené videá", "download_include_embedded_motion_videos_description": "Zahrnúť videá vložené do pohyblivých fotiek ako samostatné súbory", + "download_notfound": "Stiahnutie nebolo nájdené", "download_paused": "Stiahnutie pozastavené", "download_settings": "Stiahnuť", "download_settings_description": "Spravovať nastavenia súvisiace so sťahovaním položiek", "download_started": "Sťahovanie spustené", + "download_sucess": "Stiahnutie úspešné", "download_sucess_android": "Médiá boli stiahnuté do DCIM/Immich", + "download_waiting_to_retry": "Čaká sa na opakovanie pokusu", "downloading": "Sťahuje sa", "downloading_asset_filename": "Sťahuje sa položka {filename}", "downloading_media": "Sťahovanie médií", - "drop_files_to_upload": "Hoď súbory kdekoľvek, nahrajú sa", + "drop_files_to_upload": "Umiestnite súbory kamkoľvek na nahratie", "duplicates": "Duplikáty", "duplicates_description": "Vysporiadať sa s každou skupinou tak, že sa duplicitné označia ako duplicitné", "duration": "Trvanie", "edit": "Upraviť", "edit_album": "Upraviť album", - "edit_avatar": "Upraviť avatar", + "edit_avatar": "Upraviť profilový obrázok", + "edit_birthday": "Upraviť narodeniny", "edit_date": "Upraviť dátum", "edit_date_and_time": "Upraviť dátum a čas", "edit_description": "Upraviť popis", @@ -748,10 +841,11 @@ "edit_key": "Upraviť kľúč", "edit_link": "Upraviť odkaz", "edit_location": "Upraviť polohu", + "edit_location_action_prompt": "{count} poloha upravená", "edit_location_dialog_title": "Poloha", "edit_name": "Upraviť meno", "edit_people": "Upraviť osoby", - "edit_tag": "Upraiť značku", + "edit_tag": "Upraviť štítok", "edit_title": "Upraviť názov", "edit_user": "Upraviť používateľa", "edited": "Upravené", @@ -759,41 +853,46 @@ "editor_close_without_save_prompt": "Úpravy nebudú uložené", "editor_close_without_save_title": "Zavrieť editor?", "editor_crop_tool_h2_aspect_ratios": "Pomer strán", - "editor_crop_tool_h2_rotation": "Rotovanie", + "editor_crop_tool_h2_rotation": "Otočenie", "email": "E-mail", "email_notifications": "E-mailové oznámenia", "empty_folder": "Tento priečinok je prázdny", "empty_trash": "Vyprázdniť kôš", "empty_trash_confirmation": "Naozaj chcete vyprázdniť kôš? Nenávratne sa vymažú všetky položky z Immich.\nTáto akcia sa nedá vrátiť!", "enable": "Aktivovať", + "enable_backup": "Povoliť zálohovanie", + "enable_biometric_auth_description": "Zadajte svoj PIN kód, aby ste povolili biometrické overenie", "enabled": "Aktivovaný", "end_date": "Koncový dátum", + "enqueued": "V poradí", "enter_wifi_name": "Zadajte názov Wi-Fi", "enter_your_pin_code": "Zadajte svoj PIN kód", "enter_your_pin_code_subtitle": "Zadaním kódu PIN získate prístup k zamknutému priečinku", "error": "Chyba", + "error_change_sort_album": "Nepodarilo sa zmeniť poradie albumu", "error_delete_face": "Chyba pri odstraňovaní tváre z položky", "error_loading_image": "Nepodarilo sa načítať obrázok", "error_saving_image": "Chyba: {error}", + "error_tag_face_bounding_box": "Chyba pri označovaní tváre - nemožno získať súradnice ohraničujúceho poľa", "error_title": "Chyba - niečo sa pokazilo", "errors": { - "cannot_navigate_next_asset": "Nedokážem prejsť na ďaľšiu položku", - "cannot_navigate_previous_asset": "Nedokážem prejsť na predošlú položku", - "cant_apply_changes": "Nedokážem aplikovať zmeny", - "cant_change_activity": "Nodokážem {enabled, select, true {zakázať} other {povoliť}} aktivitu", - "cant_change_asset_favorite": "Nedokážem zmeniť obľúbenosť pre položku", - "cant_change_metadata_assets_count": "Nedokážem zmeniť metadáta pre {count, plural, one {# túto položku} other {# tieto položky}}", + "cannot_navigate_next_asset": "Nie je možné prejsť na ďalšiu položku", + "cannot_navigate_previous_asset": "Nie je možné prejsť na predošlú položku", + "cant_apply_changes": "Nie je možné použiť zmeny", + "cant_change_activity": "Nie je možné {enabled, select, true {zakázať} other {povoliť}} aktivitu", + "cant_change_asset_favorite": "Nie je možné zmeniť stav obľúbenosti pre položku", + "cant_change_metadata_assets_count": "Nie je možné zmeniť metadáta pre {count, plural, one {# položku} few {# položky} other {# položiek}}", "cant_get_faces": "Nedokážem získať tváre", "cant_get_number_of_comments": "Nedokážem získať počet komentárov", "cant_search_people": "Nedokážem hľadať osoby", "cant_search_places": "Nedokážem hľadať miesta", "error_adding_assets_to_album": "Nepodarilo sa pridať položky do albumu", - "error_adding_users_to_album": "Nepodarilo sa pridať užívateľov do albumu", - "error_deleting_shared_user": "Nepodarilo sa odstrániť zdieľaného používateľa", + "error_adding_users_to_album": "Nepodarilo sa pridať používateľov do albumu", + "error_deleting_shared_user": "Chyba pri odstraňovaní zdieľaného používateľa", "error_downloading": "Nepodarilo sa stiahnuť {filename}", "error_hiding_buy_button": "Nepodarilo sa skryť tlačidlo kúpiť", "error_removing_assets_from_album": "Nepodarilo sa odstrániť položku z albumu, podrobnejšie informácie nájdete v konzole", - "error_selecting_all_assets": "Nepodarilo sa vybrať položky", + "error_selecting_all_assets": "Chyba pri výbere všetkých položiek", "exclusion_pattern_already_exists": "Tento vzor vylúčenia už existuje.", "failed_to_create_album": "Nepodarilo sa vytvoriť album", "failed_to_create_shared_link": "Nepodarilo sa vytvoriť zdieľaný odkaz", @@ -802,10 +901,12 @@ "failed_to_keep_this_delete_others": "Nepodarilo sa ponechať túto položku a vymazať tie ostatné položky", "failed_to_load_asset": "Nepodarilo sa načítať položku", "failed_to_load_assets": "Nepodarilo sa načítať položky", + "failed_to_load_notifications": "Nepodarilo sa načítať oznámenia", "failed_to_load_people": "Nepodarilo sa načítať ľudí", "failed_to_remove_product_key": "Nepodarilo sa odstrániť produktový kľúč", "failed_to_stack_assets": "Nepodarilo sa zoskupiť položky", "failed_to_unstack_assets": "Nepodarilo sa rozdeliť položky", + "failed_to_update_notification_status": "Nepodarilo sa aktualizovať stav oznámenia", "import_path_already_exists": "Táto cesta importu už existuje.", "incorrect_email_or_password": "Nesprávny e-mail alebo heslo", "paths_validation_failed": "{paths, plural, one {# cesta zlyhala} few {# cesty zlyhali} other {# ciest zlyhalo}} pri validácii", @@ -826,14 +927,14 @@ "unable_to_change_favorite": "Nie je možné zmeniť obľúbené pre položku", "unable_to_change_location": "Nie je možné zmeniť polohu", "unable_to_change_password": "Nie je možné zmeniť heslo", - "unable_to_change_visibility": "Nie je možné zmeniť viditeľnosť pre {count, plural, one {# osobu} other {# ľudí}}", + "unable_to_change_visibility": "Nie je možné zmeniť viditeľnosť pre {count, plural, one {# osobu} few {# osoby} other {# osôb}}", "unable_to_complete_oauth_login": "Nemožno dokončiť prihlásenie cez OAuth", "unable_to_connect": "Nie je možné sa pripojiť", "unable_to_copy_to_clipboard": "Nie je možné kopírovať do schránky, overte si, že stránku navštevujete cez https", - "unable_to_create_admin_account": "Nie je možné vytvoriť admin účet", + "unable_to_create_admin_account": "Nie je možné vytvoriť účet správcu", "unable_to_create_api_key": "Nie je možné vytvoriť nový API Klúč", "unable_to_create_library": "Nie je možné vytvoriť knihovňu", - "unable_to_create_user": "Nie je možné vytvoriť uživateľa", + "unable_to_create_user": "Nie je možné vytvoriť používateľa", "unable_to_delete_album": "Nie je možné vymazať album", "unable_to_delete_asset": "Nie je možné vymazať položku", "unable_to_delete_assets": "Chyba pri odstraňovaní položiek", @@ -842,7 +943,7 @@ "unable_to_delete_shared_link": "Nie je možné vymazať zdieľaný odkaz", "unable_to_delete_user": "Nie je možné vymazať používateľa", "unable_to_download_files": "Nie je možné stiahnuť súbory", - "unable_to_edit_exclusion_pattern": "Nie je možné upravit vzorec vylúčenia", + "unable_to_edit_exclusion_pattern": "Nie je možné upraviť vzorec vylúčenia", "unable_to_edit_import_path": "Nie je možné upraviť cestu importu", "unable_to_empty_trash": "Nie je možné vyprázdniť kôš", "unable_to_enter_fullscreen": "Nie je možné prejsť do režimu celej obrazovky", @@ -865,7 +966,7 @@ "unable_to_remove_library": "Nie je možné odstrániť knižnicu", "unable_to_remove_partner": "Nie je možné odstrániť partnera", "unable_to_remove_reaction": "Nie je možné odstrániť reakciu", - "unable_to_reset_password": "Nie je možné resetovať heslo", + "unable_to_reset_password": "Nie je možné obnoviť heslo", "unable_to_reset_pin_code": "Nie je možné obnoviť PIN kód", "unable_to_resolve_duplicate": "Nie je možné vyriešiť duplikát", "unable_to_restore_assets": "Nie je možné obnoviť položky", @@ -896,25 +997,29 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "Pridať popis...", + "exif_bottom_sheet_description_error": "Chyba pri aktualizácii popisu", "exif_bottom_sheet_details": "PODROBNOSTI", "exif_bottom_sheet_location": "POLOHA", "exif_bottom_sheet_people": "ĽUDIA", "exif_bottom_sheet_person_add_person": "Pridať meno", + "exif_bottom_sheet_person_age_months": "Vek {months} mesiacov", "exif_bottom_sheet_person_age_year_months": "Vek 1 rok, {months} mesiacov", "exif_bottom_sheet_person_age_years": "Vek {years}", - "exit_slideshow": "Opustiť Slideshow", + "exit_slideshow": "Opustiť prezentáciu", "expand_all": "Rozbaliť všetko", "experimental_settings_new_asset_list_subtitle": "Prebiehajúca práca", "experimental_settings_new_asset_list_title": "Povolenie experimentálnej mriežky fotografií", "experimental_settings_subtitle": "Používajte na vlastné riziko!", "experimental_settings_title": "Experimentálne", - "expire_after": "Expiruje po", + "expire_after": "Platnosť vyprší", "expired": "Vypršalo", "expires_date": "Expiruje {date}", "explore": "Preskúmať", "explorer": "Prieskumník", "export": "Exportovať", "export_as_json": "Exportovať do JSON", + "export_database": "Exportovať databázu", + "export_database_description": "Exportovať databázu SQLite", "extension": "Rozšírenie", "external": "Externý", "external_libraries": "Externé knižnice", @@ -922,17 +1027,20 @@ "external_network_sheet_info": "Ak nie ste v preferovanej sieti Wi-Fi, aplikácia sa pripojí k serveru prostredníctvom prvej z nižšie uvedených adries URL, na ktorú sa dostane, počnúc zhora nadol", "face_unassigned": "Nepriradená", "failed": "Neúspešné", + "failed_to_authenticate": "Nepodarilo sa overiť", "failed_to_load_assets": "Nepodarilo sa načítať položky", + "failed_to_load_folder": "Nepodarilo sa načítať priečinok", "favorite": "Obľúbené", + "favorite_action_prompt": "{count} pridané do obľúbených", "favorite_or_unfavorite_photo": "Označiť fotku ako obľúbenú alebo neobľúbenú", "favorites": "Obľúbené", "favorites_page_no_favorites": "Žiadne obľúbené médiá", "feature_photo_updated": "Hlavný obrázok bol aktualizovaný", "features": "Funkcie", "features_setting_description": "Spravovať funkcie aplikácie", - "file_name": "Meno súboru", + "file_name": "Názov súboru", "file_name_or_extension": "Názov alebo prípona súboru", - "filename": "Meno súboru", + "filename": "Názov súboru", "filetype": "Typ súboru", "filter": "Filter", "filter_people": "Filtrovať ľudí", @@ -940,11 +1048,15 @@ "find_them_fast": "Nájdite ich rýchlejšie podľa mena", "fix_incorrect_match": "Opraviť nesprávnu zhodu", "folder": "Priečinok", + "folder_not_found": "Priečinok nebol nájdený", "folders": "Priečinky", - "folders_feature_description": "Prehliadanie zobrazenia priečinka s fotografiami a videami na súborovom systéme", + "folders_feature_description": "Prezeranie zobrazenia priečinkov fotografií a videí v systéme súborov", "forward": "Dopredu", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "Táto funkcia načítava externé zdroje zo spoločnosti Google, aby mohla fungovať.", "general": "Všeobecné", "get_help": "Získať pomoc", + "get_wifiname_error": "Nepodarilo sa získať názov Wi-Fi siete. Uistite sa, že ste udelili potrebné oprávnenia a ste pripojení k sieti Wi-Fi", "getting_started": "Začíname", "go_back": "Vrátiť sa späť", "go_to_folder": "Prejsť do priečinka", @@ -959,6 +1071,15 @@ "haptic_feedback_switch": "Povoliť hmatovú odozvu", "haptic_feedback_title": "Hmatová odozva", "has_quota": "Má kvótu", + "hash_asset": "Hashovať položku", + "hashed_assets": "Hashované položky", + "hashing": "Hashovanie", + "header_settings_add_header_tip": "Pridať hlavičku", + "header_settings_field_validator_msg": "Hodnota nemôže byť prázdna", + "header_settings_header_name_input": "Názov hlavičky", + "header_settings_header_value_input": "Hodnota hlavičky", + "headers_settings_tile_subtitle": "Určite hlavičky proxy servera, ktoré má aplikácia posielať s každou požiadavkou na sieť", + "headers_settings_tile_title": "Vlastné hlavičky proxy servera", "hi_user": "Ahoj {name} ({email})", "hide_all_people": "Skryť všetky osoby", "hide_gallery": "Skryť galériu", @@ -974,25 +1095,32 @@ "home_page_archive_err_partner": "Nemožno archivovať partnerské položky, preskakuje sa", "home_page_building_timeline": "Vytváranie časovej osi", "home_page_delete_err_partner": "Nie je možné vymazať položky partnera, preskakuje sa", + "home_page_delete_remote_err_local": "Miestne položky vo výbere vzdialeného odstránenia, preskakuje sa", "home_page_favorite_err_local": "Zatiaľ nie je možné zaradiť lokálne média medzi obľúbené, preskakuje sa", "home_page_favorite_err_partner": "Na teraz nemôžete pridať partnerove médiá medzi obľúbené", "home_page_first_time_notice": "Ak aplikáciu používate prvýkrát, uistite sa, že ste si vybrali záložný album, aby sa na časovej osi mohli zobrazovať fotografie a videá", + "home_page_locked_error_local": "Nie je možné presunúť miestne položky do zamknutého priečinka, preskakuje sa", + "home_page_locked_error_partner": "Nie je možné presunúť partnerské položky do zamknutého priečinka, preskakuje sa", "home_page_share_err_local": "Nemožno zdieľať lokálne médiá pomocou odkazu", "home_page_upload_err_limit": "Naraz môžete nahrať len 30 médií, preskakuje sa", "host": "Hostiteľ", "hour": "Hodina", "id": "ID", + "idle": "Nečinné", + "ignore_icloud_photos": "Ignorovať fotky v službe iCloud", + "ignore_icloud_photos_description": "Fotografie uložené v službe iCloud sa nebudú odosielať na server Immich", "image": "Obrázok", "image_alt_text_date": "{isVideo, select, true {Video} other {Image}} nasnímané {date}", "image_alt_text_date_1_person": "{isVideo, select, true {Video} other {Image}} nasnímané s {person1} dňa {date}", "image_alt_text_date_2_people": "{isVideo, select, true {Video} other {Image}} nasnímané s {person1} a {person2} dňa {date}", "image_alt_text_date_3_people": "{isVideo, select, true {Video} other {Image}} nasnímané s {person1}, {person2} a {person3} dňa {date}", - "image_alt_text_date_4_or_more_people": "{isVideo, select, true {Video} other {Image}} nasnímané s {person1}, {person2} a {additionalCount, number} inými dňa {date}", - "image_alt_text_date_place": "{isVideo, select, true {Video} other {Obrázok}} nasnímané v {city}, {country} dňa {date}", - "image_alt_text_date_place_1_person": "{isVideo, select, true {Video} other {Obrázok}} zo dňa {date} v {city}, {country} s {person1}", - "image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {Obrázok}} v {city}, {country} s {person1} a {person2} zo dňa {date}", - "image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {Obrázok}} zo dňa {date} v {city}, {country} s {person1}, {person2} a {person3}", - "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {Image}} nasnímaný v {city}, {country} s {person1}, {person2} a {additionalCount, number} inými dňa {date}", + "image_alt_text_date_4_or_more_people": "{isVideo, select, true {Video nasnímané} other {Obrázok odfotený}} s osobami {person1}, {person2} a {additionalCount, number} inými dňa {date}", + "image_alt_text_date_place": "{isVideo, select, true {Video nasnímané} other {Obrázok odfotený}} v {city}, {country} dňa {date}", + "image_alt_text_date_place_1_person": "{isVideo, select, true {Video nasnímané} other {Obrázok odfotený}} dňa {date} v {city}, {country} s {person1}", + "image_alt_text_date_place_2_people": "{isVideo, select, true {Video nasnímané} other {Obrázok odfotený}} v {city}, {country} s {person1} a {person2} dňa {date}", + "image_alt_text_date_place_3_people": "{isVideo, select, true {Video nasnímané} other {Obrázok odfotený}} dňa {date} v {city}, {country} s {person1}, {person2} a {person3}", + "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video nasnímamé} other {Obrázok odfotený}} v {city}, {country} s {person1}, {person2} a {additionalCount, number} inými dňa {date}", + "image_saved_successfully": "Obrázok bol uložený", "image_viewer_page_state_provider_download_started": "Sťahovanie sa začalo", "image_viewer_page_state_provider_download_success": "Sťahovanie bolo úspešné", "image_viewer_page_state_provider_share_error": "Chyba zdieľania", @@ -1015,17 +1143,28 @@ "night_at_twoam": "Každú noc o 2:00" }, "invalid_date": "Neplatný dátum", + "invalid_date_format": "Neplatný formát dátumu", "invite_people": "Pozvať ľudí", "invite_to_album": "Pozvať do albumu", + "ios_debug_info_fetch_ran_at": "Načítanie prebehlo {dateTime}", + "ios_debug_info_last_sync_at": "Posledná synchronizácia {dateTime}", + "ios_debug_info_no_processes_queued": "Žiadne procesy nie sú v poradí na pozadí", + "ios_debug_info_no_sync_yet": "Zatiaľ nebola spustená žiadna úloha synchronizácie na pozadí", + "ios_debug_info_processes_queued": "{count, plural, one {{count} proces na pozadí v poradí} few {{count} procesy na pozadí v poradí} other {{count} procesov na pozadí v poradí}}", + "ios_debug_info_processing_ran_at": "Spracovanie prebehlo {dateTime}", "items_count": "{count, plural, one {# položka} few {# položky} other {# položiek}}", "jobs": "Úlohy", "keep": "Ponechať", "keep_all": "Ponechať všetko", "keep_this_delete_others": "Ponechať toto, odstrániť ostatné", - "kept_this_deleted_others": "Ponechá túto položku a odstráni {count, plural, one {# položku} other {# položiek}}", + "kept_this_deleted_others": "Ponechal túto položku a odstránil {count, plural, one {# položku} few {# položky} other {# položiek}}", "keyboard_shortcuts": "Klávesové skratky", "language": "Jazyk", - "language_setting_description": "Vyberte preferovaný jazyk", + "language_no_results_subtitle": "Skúste upraviť hľadaný výraz", + "language_no_results_title": "Neboli nájdené žiadne jazyky", + "language_search_hint": "Vyhľadať jazyky...", + "language_setting_description": "Vyberte požadovaný jazyk", + "large_files": "Veľké súbory", "last_seen": "Naposledy videné", "latest_version": "Najnovšia verzia", "latitude": "Zemepisná šírka", @@ -1041,16 +1180,21 @@ "library_page_sort_created": "Najnovšie vytvorené", "library_page_sort_last_modified": "Naposledy upravené", "library_page_sort_title": "Podľa názvu albumu", - "light": "Svetlý", + "licenses": "Licencie", + "light": "Svetlá", "like_deleted": "Like odstránený", "link_motion_video": "Pripojiť pohyblivé video", - "link_options": "Možnosti odkazu", "link_to_oauth": "Prepojiť s OAuth", "linked_oauth_account": "Pripojený OAuth účet", "list": "Zoznam", "loading": "Načítavanie", "loading_search_results_failed": "Načítanie výsledkov hľadania sa nepodarilo", + "local": "Lokálne", + "local_asset_cast_failed": "Nie je možné preniesť médium, ktoré nie je nahrané na serveri", + "local_assets": "Lokálne položky", "local_network": "Miestna sieť", + "local_network_sheet_info": "Pri použití zadanej siete Wi-Fi sa aplikácia pripojí k serveru prostredníctvom tejto URL adresy", + "location_permission": "Povolenie na určenie polohy", "location_permission_content": "Na používanie funkcie automatického prepínania potrebuje aplikácia Immich presné povolenie na určenie polohy, aby mohla prečítať názov aktuálnej Wi-Fi siete", "location_picker_choose_on_map": "Zvoľte na mape", "location_picker_latitude_error": "Zadajte platnú zemepisnú šírku", @@ -1061,6 +1205,7 @@ "locked_folder": "Zamknutý priečinok", "log_out": "Odhlásiť sa", "log_out_all_devices": "Odhlásiť všetky zariadenia", + "logged_in_as": "Prihlásený ako {user}", "logged_out_all_devices": "Všetky zariadenia odhlásené", "logged_out_device": "Zariadenie odhlásené", "login": "Prihlásenie", @@ -1069,7 +1214,7 @@ "login_form_back_button_text": "Späť", "login_form_email_hint": "tvojmail@email.com", "login_form_endpoint_hint": "http://ip-tvojho-servera:port", - "login_form_endpoint_url": "URL adresa servera", + "login_form_endpoint_url": "URL adresa koncového bodu servera", "login_form_err_http": "Prosím, uveďte http:// alebo https://", "login_form_err_invalid_email": "Neplatný e-mail", "login_form_err_invalid_url": "Neplatná URL adresa", @@ -1078,7 +1223,7 @@ "login_form_failed_get_oauth_server_config": "Chyba prihlásenia pomocou OAuth, skontrolujte adresu URL servera", "login_form_failed_get_oauth_server_disable": "Funkcia OAuth nie je na tomto serveri dostupná", "login_form_failed_login": "Chyba prihlásenia, skontrolujte url adresu servera, e-mail a heslo", - "login_form_handshake_exception": "Nastala chyba handshake. Zapnite podoporu samo-podpísaných certifikátov v nastaveniach ak používate samo-podpísané certifikáty.", + "login_form_handshake_exception": "Došlo k výnimke Handshake so serverom. Ak používate certifikát s vlastným podpisom, povoľte v nastaveniach podporu certifikátov s vlastným podpisom.", "login_form_password_hint": "heslo", "login_form_save_login": "Zostať prihlásený", "login_form_server_empty": "Zadajte URL adresu servera.", @@ -1089,7 +1234,7 @@ "logout_all_device_confirmation": "Ste si istý, že sa chcete odhlásiť zo všetkých zariadení?", "logout_this_device_confirmation": "Ste si istý, že sa chcete odhlásiť z tohoto zariadenia?", "longitude": "Zemepisná dĺžka", - "look": "Zobrazenie", + "look": "Vzhľad", "loop_videos": "Opakovať videá", "loop_videos_description": "Povolí prehrávanie videí v slučke v detailnom zobrazení.", "main_branch_warning": "Používate vývojársku verziu; dôrazne odporúčame používať vydané verzie!", @@ -1103,8 +1248,7 @@ "manage_your_devices": "Spravovať vaše prihlásené zariadenia", "manage_your_oauth_connection": "Spravovať vaše OAuth spojenia", "map": "Mapa", - "map_assets_in_bound": "{count} fotka", - "map_assets_in_bounds": "{count} fotiek", + "map_assets_in_bounds": "{count, plural, one {# fotka} few {# fotky} other {# fotiek}}", "map_cannot_get_user_location": "Nie je možné získať polohu používateľa", "map_location_dialog_yes": "Áno", "map_location_picker_page_use_location": "Použiť túto polohu", @@ -1127,6 +1271,9 @@ "map_settings_only_show_favorites": "Zobraziť iba obľúbené", "map_settings_theme_settings": "Téma mapy", "map_zoom_to_see_photos": "Oddiaľte priblíženie aby ste videli fotky", + "mark_all_as_read": "Označiť všetko ako prečítané", + "mark_as_read": "Označiť ako prečítané", + "marked_all_as_read": "Označené všetko ako prečítané", "matches": "Zhody", "media_type": "Typ média", "memories": "Spomienky", @@ -1143,7 +1290,7 @@ "merge_people_limit": "Zlúčiť môžete naraz najviac 5 tvárí", "merge_people_prompt": "Chcete zlúčiť týchto ľudí? Táto akcia sa nedá vrátiť.", "merge_people_successfully": "Zlúčenie ľudí sa podarilo", - "merged_people_count": "Zlúčení {count, plural, one {# človek} other {# ľudia}}", + "merged_people_count": "{count, plural, one {Zlúčená # osoba} few {Zlúčené # osoby} other {Zlúčených # osôb}}", "minimize": "Minimalizovať", "minute": "Minúta", "missing": "Chýbajúce", @@ -1153,15 +1300,20 @@ "more": "Viac", "move": "Presunúť", "move_off_locked_folder": "Presunúť zo zamknutého priečinka", + "move_to_lock_folder_action_prompt": "{count} pridaných do zamknutého priečinka", "move_to_locked_folder": "Presunúť do zamknutého priečinka", - "move_to_locked_folder_confirmation": "Tieto fotografie a videá budú odstránené zo všetkých albumov a bude ich možné zobraziť len v zamknutom priečinku", + "move_to_locked_folder_confirmation": "Tieto fotografie a videá budú odobrané zo všetkých albumov a bude ich možné zobraziť len v zamknutom priečinku", + "moved_to_archive": "{count, plural, one {Presunutá # položka} few {Presunuté # položky} other {Presunutých # položiek}} do archívu", + "moved_to_library": "{count, plural, one {Presunutá # položka} few {Presunuté # položky} other {Presunutých # položiek}} do knižnice", "moved_to_trash": "Presunuté do koša", "multiselect_grid_edit_date_time_err_read_only": "Nemožno upraviť dátum položky len na čítanie, preskakujem", - "multiselect_grid_edit_gps_err_read_only": "Nie je možné upraviť polohu položky (položiek) len na čítanie, preskakuje sa", + "multiselect_grid_edit_gps_err_read_only": "Nie je možné upraviť polohu položky (položiek), ktorá je len na čítanie, preskakuje sa", "mute_memories": "Vyblednutie spomienok", "my_albums": "Moje albumy", "name": "Meno", "name_or_nickname": "Meno alebo prezývka", + "networking_settings": "Sieť", + "networking_subtitle": "Spravovať nastavenia koncového bodu servera", "never": "nikdy", "new_album": "Nový album", "new_api_key": "Nový API kľúč", @@ -1171,16 +1323,17 @@ "new_pin_code_subtitle": "Toto je váš prvý prístup k zamknutému priečinku. Vytvorte si PIN kód na bezpečný prístup k tejto stránke", "new_user_created": "Nový používateľ vytvorený", "new_version_available": "JE DOSTUPNÁ NOVÁ VERZIA", - "newest_first": "Najnovšie prvé", + "newest_first": "Najprv najnovšie", "next": "Ďalej", "next_memory": "Ďalšia spomienka", "no": "Nie", "no_albums_message": "Vytvorí album na organizovanie fotiek a videí", "no_albums_with_name_yet": "Vyzerá, že zatiaľ nemáte album s týmto názvom.", "no_albums_yet": "Vyzerá, že zatiaľ nemáte žiadne albumy.", - "no_archived_assets_message": "Archivovať fotografie a videá, aby sa skryli zo zobrazenia Fotografie", + "no_archived_assets_message": "Archivujte fotografie a videá a skryte ich z vášho zobrazenia fotografií", "no_assets_message": "KLIKNITE A NAHRAJTE SVOJU PRVÚ FOTKU", "no_assets_to_show": "Žiadne položky", + "no_cast_devices_found": "Nenašli sa žiadne zariadenia na prenos", "no_duplicates_found": "Nenašli sa žiadne duplicity.", "no_exif_info_available": "Nie sú dostupné exif údaje", "no_explore_results_message": "Nahrajte viac fotiek na objavovanie vašej zbierky.", @@ -1188,16 +1341,21 @@ "no_libraries_message": "Vytvorí externú knižnicu na prezeranie fotiek a videí", "no_locked_photos_message": "Fotografie a videá v zamknutom priečinku sú skryté a nezobrazujú sa pri prehľadávaní alebo vyhľadávaní v knižnici.", "no_name": "Bez mena", + "no_notifications": "Žiadne oznámenia", + "no_people_found": "Nenašli sa žiadni vyhovujúci ľudia", "no_places": "Bez miesta", "no_results": "Žiadne výsledky", "no_results_description": "Skúste synonymum alebo všeobecnejší výraz", "no_shared_albums_message": "Vytvorí album na zdieľanie fotiek a videí s ľuďmi vo vašej sieti", + "no_uploads_in_progress": "Žiadne prebiehajúce nahrávanie", "not_in_any_album": "Nie je v žiadnom albume", + "not_selected": "Nevybrané", "note_apply_storage_label_to_previously_uploaded assets": "Poznámka: Ak chcete použiť Štítok úložiska na predtým nahrané médiá, spustite príkaz", "notes": "Poznámky", + "nothing_here_yet": "Zatiaľ tu nič nie je", "notification_permission_dialog_content": "Ak chcete povoliť upozornenia, prejdite do Nastavenia a vyberte možnosť Povoliť.", - "notification_permission_list_tile_content": "Udeľte oprávnenie k aktivácii oznámení.", - "notification_permission_list_tile_enable_button": "Povoliť upozornenia", + "notification_permission_list_tile_content": "Udeľte povolenie na zapnutie oznámení.", + "notification_permission_list_tile_enable_button": "Povoliť oznámenia", "notification_permission_list_tile_title": "Povolenie oznámení", "notification_toggle_setting_description": "Povoliť e-mailové upozornenia", "notifications": "Oznámenia", @@ -1206,10 +1364,12 @@ "official_immich_resources": "Oficiálne Immich zdroje", "offline": "Offline", "ok": "OK", - "oldest_first": "Najstaršie prvé", + "oldest_first": "Najprv najstaršie", "on_this_device": "Na tomto zariadení", "onboarding": "Na palube", + "onboarding_locale_description": "Vyberte požadovaný jazyk. Neskôr ho môžete zmeniť v nastaveniach.", "onboarding_privacy_description": "Nasledujúce (voliteľné) funkcie závisia na externých službách a kedykoľvek ich môžete vypnúť nastaveniach.", + "onboarding_server_welcome_description": "Poďme si nastaviť vašu inštanciu s niekoľkými bežnými nastaveniami.", "onboarding_theme_description": "Vyberte farbu témy pre váš server. Môžete to aj neskôr zmeniť vo vašich nastaveniach.", "onboarding_user_welcome_description": "Začnime!", "onboarding_welcome_user": "Vitaj, {user}", @@ -1225,6 +1385,7 @@ "original": "originál", "other": "Ostatné", "other_devices": "Ďalšie zariadenia", + "other_entities": "Ostatné subjekty", "other_variables": "Ostatné premenné", "owned": "Vlastnené", "owner": "Vlastník", @@ -1235,7 +1396,7 @@ "partner_list_user_photos": "Fotky používateľa {user}", "partner_list_view_all": "Zobraziť všetky", "partner_page_empty_message": "Vaše fotky zatiaľ nie sú zdieľané so žiadnym partnerom.", - "partner_page_no_more_users": "Žiadni ďalší užívatelia na zdieľanie", + "partner_page_no_more_users": "Žiadni ďalší používatelia na pridanie", "partner_page_partner_add_failed": "Pridávanie partnera zlyhalo", "partner_page_select_partner": "Zvoliť partnera", "partner_page_shared_to_title": "Zdieľané pre", @@ -1247,9 +1408,9 @@ "password_required": "Heslo je povinné", "password_reset_success": "Obnovenie hesla úspešné", "past_durations": { - "days": "{days, plural, one {Posledný deň} other {Posledných # dní }}", - "hours": "{hours, plural, one {Posledná hodina} other {Posledných # hodín}}", - "years": "{years, plural, one {Posledný rok} other {Posledné # roky}}" + "days": "{days, plural, one {Posledný deň} few {Posledné # dni} other {Posledných # dní }}", + "hours": "{hours, plural, one {Posledná hodina} few {Posledné # hodiny} other {Posledných # hodín}}", + "years": "{years, plural, one {Posledný rok} few {Posledné # roky} other {Posledných # rokov}}" }, "path": "Cesta", "pattern": "Vzor", @@ -1258,17 +1419,18 @@ "paused": "Pozastavené", "pending": "Čakajúce", "people": "Ľudia", - "people_edits_count": "{count, plural, one {Upravená # osoba} other {Upravených # ľudí}}", + "people_edits_count": "{count, plural, one {Upravená # osoba} few {Upravené # osoby} other {Upravených # osôb}}", "people_feature_description": "Prehliadanie fotiek a videí zoskupených podľa ľudí", - "people_sidebar_description": "Zobrazí odkaz na Ľudí v bočnom paneli", + "people_sidebar_description": "Zobraziť odkaz na Ľudí v bočnom paneli", "permanent_deletion_warning": "Varovanie o trvalom zmazaní", "permanent_deletion_warning_setting_description": "Zobraziť varovanie pri trvalom zmazaní položky", "permanently_delete": "Trvalo zmazať", - "permanently_delete_assets_count": "Navždy zmazať {count, plural, one {položku} other {položky}}", - "permanently_delete_assets_prompt": "Naozaj si prajete navždy zmazať {count, plural, one {túto položku?} other {týchto # položiek?}} Vymažú sa aj {count, plural, one {zo svojho albumu} other {zo svojich albumov}}.", + "permanently_delete_assets_count": "Natrvalo vymazať {count, plural, one {položku} few {položky} other {položiek}}", + "permanently_delete_assets_prompt": "Ste si istí, že chcete natrvalo vymazať {count, plural, one {túto položku?} few {tieto # položky?} other {týchto # položiek?}} Týmto sa odstráni aj {count, plural, one {z jej albumu} other {zo svojich albumov}}.", "permanently_deleted_asset": "Navždy odstránená položka", - "permanently_deleted_assets_count": "Navždy {count, plural, one {odstránená # položka} other {odstránené # položky}}", + "permanently_deleted_assets_count": "Natrvalo {count, plural, one {odstránená # položka} few {odstránené # položky} other {odstránených # položiek}}", "permission": "Povolenie", + "permission_empty": "Vaše povolenie by nemalo byť prázdne", "permission_onboarding_back": "Späť", "permission_onboarding_continue_anyway": "Pokračovať aj tak", "permission_onboarding_get_started": "Začať", @@ -1283,9 +1445,13 @@ "photo_shared_all_users": "Vyzerá, že zdieľate svoje fotky so všetkými používateľmi alebo nemáte žiadnych používateľov.", "photos": "Fotografie", "photos_and_videos": "Fotografie & Videa", - "photos_count": "{count, plural, one {{count, number} Fotka} other {{count, number} Fotiek}}", + "photos_count": "{count, plural, one {{count, number} fotka} few {{count, number} fotky} other {{count, number} fotiek}}", "photos_from_previous_years": "Fotky z minulých rokov", "pick_a_location": "Vyberte polohu", + "pin_code_changed_successfully": "Úspešne ste zmenili PIN kód", + "pin_code_reset_successfully": "Úspešne ste obnovili PIN kód", + "pin_code_setup_successfully": "Úspešne ste nastavili PIN kód", + "pin_verification": "Overenie PIN kódom", "place": "Miesto", "places": "Miesta", "places_count": "{count, plural, one {{count, number} miesto} few {{count, number} miesta} other {{count, number} miest}}", @@ -1293,17 +1459,22 @@ "play_memories": "Prehrať spomienky", "play_motion_photo": "Prehrať pohyblivú fotku", "play_or_pause_video": "Pustí alebo pozastaví video", + "please_auth_to_access": "Prosím, potvrďte overenie pre prístup", "port": "Port", - "preferences_settings_title": "Preferencie", - "preset": "Prednastavenie", + "preferences_settings_subtitle": "Spravovať predvoľby aplikácie", + "preferences_settings_title": "Predvoľby", + "preset": "Predvoľba", "preview": "Náhľad", "previous": "Predošlé", "previous_memory": "Predošlá spomienka", + "previous_or_next_day": "Deň dopredu/dozadu", + "previous_or_next_month": "Mesiac dopredu/dozadu", "previous_or_next_photo": "Fotka ďalšia/predošlá", + "previous_or_next_year": "Rok dopredu/dozadu", "primary": "Primárne", "privacy": "Súkromie", "profile": "Profil", - "profile_drawer_app_logs": "Logy", + "profile_drawer_app_logs": "Záznamy", "profile_drawer_client_out_of_date_major": "Mobilná aplikácia je zastaralá. Prosím aktualizujte na najnovšiu verziu.", "profile_drawer_client_out_of_date_minor": "Mobilná aplikácia je zastaralá. Prosím aktualizujte na najnovšiu verziu.", "profile_drawer_client_server_up_to_date": "Klient a server sú aktuálne", @@ -1315,7 +1486,7 @@ "public_album": "Verejný album", "public_share": "Verejné zdieľanie", "purchase_account_info": "Podporovateľ", - "purchase_activated_subtitle": "Ďakujeme za podporu Immich a softvéru s otvorenými zdrojákmi", + "purchase_activated_subtitle": "Ďakujeme vám za podporu aplikácie Immich a softvéru s otvoreným zdrojovým kódom", "purchase_activated_time": "Aktivované {date}", "purchase_activated_title": "Váš kľúč je úspešne aktivovaný", "purchase_button_activate": "Aktivovať", @@ -1327,14 +1498,14 @@ "purchase_button_select": "Vybrať", "purchase_failed_activation": "Aktivácia sa nepodarila! Prosím skontrolujte email či je správny kľúč produktu!", "purchase_individual_description_1": "Pre jednotlivca", - "purchase_individual_description_2": "Stav podporovateľa", + "purchase_individual_description_2": "Štatút podporovateľa", "purchase_individual_title": "Jednotlivec", "purchase_input_suggestion": "Máte produktový kľúč? Zadajte ho nižšie", "purchase_license_subtitle": "Kúpte si Immich a podporte neustály vývoj tejto služby", "purchase_lifetime_description": "Doživotná platnosť", "purchase_option_title": "MOŽNOSTI NÁKUPU", - "purchase_panel_info_1": "Vývoj Immich zaberá veľa času a úsilia, a máme zamestnaných fulltime inžinierov, aby ho spravili ako sa najlepšie dá. Naša misia je, aby sa open-source softvér a etické biznis praktiky stali udržateľným zdrojom príjmu pre vývojárov a vytvorili ekosystém rešpektujúci súkromie so skutočnými náhradami voči zneužívajúcim cloudovým službám.", - "purchase_panel_info_2": "Keďže sme zaviazaní nezavádzať platené verzie, nezískate týmto nákupom žiadne prídavné funkcie. Spoliehame sa na používateľov, ako ste vy, že podporia neustály vývoj aplikácie Immich.", + "purchase_panel_info_1": "Vývoj aplikácie Immich zaberá veľa času a úsilia, pričom na ňom pracujú inžinieri na plný úväzok, aby bol čo najlepší. Naším poslaním je, aby sa softvér s otvoreným zdrojovým kódom a etické obchodné postupy stali udržateľným zdrojom príjmov pre vývojárov a aby sme vytvorili ekosystém rešpektujúci súkromie so skutočnými alternatívami k zneužívajúcim cloudovým službám.", + "purchase_panel_info_2": "Keďže sme zaviazaní nezavádzať platené verzie, nezískate týmto nákupom žiadne pridané funkcie. Spoliehame sa na používateľov, ako ste vy, že podporia neustály vývoj aplikácie Immich.", "purchase_panel_title": "Podporiť projekt", "purchase_per_server": "Za server", "purchase_per_user": "Za používateľa", @@ -1343,46 +1514,52 @@ "purchase_remove_server_product_key": "Odstrániť produktový kľúč servera", "purchase_remove_server_product_key_prompt": "Naozaj chcete odstrániť produktový kľúč servera?", "purchase_server_description_1": "Pre celý server", - "purchase_server_description_2": "Stav podporovateľa", + "purchase_server_description_2": "Štatút podporovateľa", "purchase_server_title": "Server", "purchase_settings_server_activated": "Produktový kľúč servera spravuje admin", + "queue_status": "V poradí {count}/{total}", "rating": "Hodnotenie hviezdičkami", "rating_clear": "Vyčistiť hodnotenie", - "rating_count": "{count, plural, one {# hviezdička} other {# hviezdičky}}", - "rating_description": "Zobrazí EXIF hodnotenie v info paneli", + "rating_count": "{count, plural, one {# hviezdička} few {# hviezdičky} other {# hviezdičiek}}", + "rating_description": "Zobraziť EXIF hodnotenie v informačnom paneli", "reaction_options": "Možnosti reakcie", "read_changelog": "Prečítať zoznam zmien", "reassign": "Preradiť", - "reassigned_assets_to_existing_person": "Preradené {count, plural, one {# položka} other {# položky}} k {name, select, null {existujúcej osobe} other {{name}}}", - "reassigned_assets_to_new_person": "Preradené {count, plural, one {# položka} other {# položiek}} novej osobe", + "reassigned_assets_to_existing_person": "Opätovne {count, plural, one {priradená # položka} few {priradené # položky} other {priradených # položiek}} k {name, select, null {existujúcej osobe} other {{name}}}", + "reassigned_assets_to_new_person": "Opätovne {count, plural, one {priradená # položka} few {priradené # položky} other {priradených # položiek}} novej osobe", "reassing_hint": "Priradí zvolenú položku k existujúcej osobe", "recent": "Nedávne", "recent-albums": "Posledné albumy", "recent_searches": "Posledné vyhľadávania", + "recently_added": "Nedávno pridané", "recently_added_page_title": "Nedávno pridané", "recently_taken": "Nedávno nasnímané", "recently_taken_page_title": "Nedávno zhotovené", - "refresh": "Obnoviť", + "refresh": "Aktualizovať", "refresh_encoded_videos": "Obnoviť enkódované videá", "refresh_faces": "Obnoviť tváre", "refresh_metadata": "Obnoviť metadáta", "refresh_thumbnails": "Obnoviť miniatúry", - "refreshed": "Aktualizované", + "refreshed": "Obnovené", "refreshes_every_file": "Znova prečíta všetky existujúce a nové súbory", "refreshing_encoded_video": "Obnovovanie enkódovaných videí", - "refreshing_faces": "Obnovovnie tvárí", + "refreshing_faces": "Obnovovanie tvárí", "refreshing_metadata": "Obnovovanie metadát", "regenerating_thumbnails": "Pregenerovanie náhľadov", + "remote": "Vzdialené", + "remote_assets": "Vzdialené položky", "remove": "Odstrániť", - "remove_assets_album_confirmation": "Naozaj chcete odstrániť {count, plural, one {# položky} other {# položiek}} z albumu?", - "remove_assets_shared_link_confirmation": "Naozaj chcete odstrániť {count, plural, one {# položku} other {# položiek}} z tohoto zdieľaného odkazu?", + "remove_assets_album_confirmation": "Naozaj chcete odstrániť {count, plural, one {# položku} few {# položky} other {# položiek}} z albumu?", + "remove_assets_shared_link_confirmation": "Naozaj chcete odstrániť {count, plural, one {# položku} few {# položky} other {# položiek}} z tohoto zdieľaného odkazu?", "remove_assets_title": "Odstrániť položky?", "remove_custom_date_range": "Odstrániť vlastný rozsah dátumov", "remove_deleted_assets": "Odstrániť vymazané položky", "remove_from_album": "Odstrániť z albumu", + "remove_from_album_action_prompt": "{count} odstránené z albumu", "remove_from_favorites": "Odstrániť z obľúbených", - "remove_from_locked_folder": "Odstrániť zo zamknutého priečinka", - "remove_from_locked_folder_confirmation": "Ste si istí, že chcete tieto fotografie a videá presunúť zo zamknutého priečinka? Budú viditeľné vo vašej knižnici.", + "remove_from_lock_folder_action_prompt": "{count} odobrané zo zamknutého priečinka", + "remove_from_locked_folder": "Odobrať zo zamknutého priečinka", + "remove_from_locked_folder_confirmation": "Ste si istí, že chcete tieto fotografie a videá odobrať zo zamknutého priečinka? Budú viditeľné vo vašej knižnici.", "remove_from_shared_link": "Odstrániť zo zdieľaného odkazu", "remove_memory": "Odstrániť spomienku", "remove_photo_from_memory": "Odstrániť fotografiu z tejto spomienky", @@ -1395,7 +1572,7 @@ "removed_from_favorites_count": "{count, plural, other {Odstránených #}} z obľúbených", "removed_memory": "Odstránená pamäť", "removed_photo_from_memory": "Fotografia odstránená z pamäte", - "removed_tagged_assets": "Odstránená značka z {count, plural, one {# položky} other {# položiek}}", + "removed_tagged_assets": "Odstránený štítok z {count, plural, one {# položky} other {# položiek}}", "rename": "Premenovať", "repair": "Opraviť", "repair_no_results_message": "Nesledované a chýbajúce súbory sa zobrazia tu", @@ -1404,27 +1581,34 @@ "require_password": "Vyžadovať heslo", "require_user_to_change_password_on_first_login": "Vyžadovať zmenu hesla po prvom prihlásení", "rescan": "Opätovné vyhľadávanie", - "reset": "Resetovať", + "reset": "Obnoviť", "reset_password": "Obnoviť heslo", - "reset_people_visibility": "Resetovať viditeľnosť ľudí", + "reset_people_visibility": "Obnoviť viditeľnosť ľudí", "reset_pin_code": "Obnoviť PIN kód", - "reset_to_default": "Resetovať na predvolené", + "reset_sqlite": "Obnoviť SQLite databázu", + "reset_sqlite_confirmation": "Ste si istí, že chcete obnoviť SQLite databázu? Na opätovnú synchronizáciu údajov sa budete musieť odhlásiť a znova prihlásiť", + "reset_sqlite_success": "Úspešné obnovenie databázy SQLite", + "reset_to_default": "Obnoviť na predvolené", "resolve_duplicates": "Vyriešiť duplicity", "resolved_all_duplicates": "Vyriešené všetky duplicity", "restore": "Navrátiť", - "restore_all": "Navrátit všetko", + "restore_all": "Navrátiť všetko", + "restore_trash_action_prompt": "{count} obnovených z koša", "restore_user": "Navrátiť používateľa", - "restored_asset": "Navrátené položky", + "restored_asset": "Navrátená položka", "resume": "Pokračovať", "retry_upload": "Zopakovať nahrávanie", - "review_duplicates": "Prezrieť duplikáty", + "review_duplicates": "Preskúmať duplikáty", + "review_large_files": "Skontrolovať veľké súbory", "role": "Rola", "role_editor": "Editor", "role_viewer": "Divák", + "running": "Spustené", "save": "Uložiť", + "save_to_gallery": "Uložiť do galérie", "saved_api_key": "Uložený API Kľúč", "saved_profile": "Uložený profil", - "saved_settings": "Uložené nastavenia", + "saved_settings": "Nastavenia boli uložené", "say_something": "Napíšte niečo", "scaffold_body_error_occurred": "Vyskytla sa chyba", "scan_all_libraries": "Preskenovať všetky knižnice", @@ -1436,7 +1620,7 @@ "search_by_context": "Hľadať s kontextom", "search_by_description": "Vyhľadávanie podľa popisu", "search_by_description_example": "Pešia turistika v Sape", - "search_by_filename": "Hľadať s názvom alebo príponou súboru", + "search_by_filename": "Hľadať podľa názvu alebo prípony súboru", "search_by_filename_example": "napr. IMG_1234.JPG alebo PNG", "search_camera_make": "Hľadať značku fotoaparátu...", "search_camera_model": "Hľadať model fotoaparátu...", @@ -1445,9 +1629,11 @@ "search_filter_apply": "Použiť filter", "search_filter_camera_title": "Vyberte typ kamery", "search_filter_date": "Dátum", + "search_filter_date_interval": "{start} do {end}", "search_filter_date_title": "Vyberte rozsah dátumov", "search_filter_display_option_not_in_album": "Mimo albumu", "search_filter_display_options": "Možnosti zobrazenia", + "search_filter_filename": "Hľadať podľa názvu súboru", "search_filter_location": "Poloha", "search_filter_location_title": "Vyberte polohu", "search_filter_media_type": "Typ média", @@ -1490,6 +1676,7 @@ "select_album_cover": "Vyberte obal albumu", "select_all": "Vybrať všetko", "select_all_duplicates": "Vybrať všetky duplikáty", + "select_all_in": "Označiť všetky v {group}", "select_avatar_color": "Vyberte farbu avatara", "select_face": "Vyberte tvár", "select_featured_photo": "Vyberte náhľadovú fotku", @@ -1502,15 +1689,17 @@ "select_trash_all": "Vybrať zahodiť všetky", "select_user_for_sharing_page_err_album": "Nepodarilo sa vytvoriť album", "selected": "Vybrané", - "selected_count": "{count, plural, other {# vybrané}}", + "selected_count": "{count, plural, one {# vybraná} few {# vybrané} other {# vybraných}}", "send_message": "Odoslať správu", "send_welcome_email": "Odoslať uvítací e-mail", + "server_endpoint": "Koncový bod servera", "server_info_box_app_version": "Verzia aplikácie", - "server_info_box_server_url": "URL Serveru", + "server_info_box_server_url": "URL adresa servera", "server_offline": "Server je Offline", "server_online": "Server je Online", - "server_stats": "Serverové Štatistiky", - "server_version": "Verzia Servera", + "server_privacy": "Zásady ochrany osobných údajov servera", + "server_stats": "Štatistiky servera", + "server_version": "Verzia servera", "set": "Nastaviť", "set_as_album_cover": "Nastaviť ako obal albumu", "set_as_featured_photo": "Nastaviť ako hlavnú fotku", @@ -1518,14 +1707,16 @@ "set_date_of_birth": "Nastaviť dátum narodenia", "set_profile_picture": "Nastaviť profilový obrázok", "set_slideshow_to_fullscreen": "Nastaviť prezentáciu na celú obrazovku", - "setting_image_viewer_help": "Prehliadač detailov najprv načíta malú miniatúru, potom načíta náhľad strednej veľkosti (ak je povolený) a nakoniec načíta originál (ak je povolený).", + "set_stack_primary_asset": "Nastaviť ako primárnu položku", + "setting_image_viewer_help": "V detailnom prehliadači sa najprv načíta malá miniatúra, potom sa načíta stredne veľký náhľad (ak je povolený) a nakoniec sa načíta originál (ak je povolený).", "setting_image_viewer_original_subtitle": "Povolením umožníte načítať pôvodný obrázok v plnom rozlíšení (veľký!). Zakázaním znížite používania dát (v sieti, aj v dočasnej pamäte zariadenia).", "setting_image_viewer_original_title": "Načítať pôvodný obrázok", "setting_image_viewer_preview_subtitle": "Povolením umožníte načítať obrázok so stredným rozlíšením. Zakážte, ak chcete priamo načítať originál alebo použiť iba miniatúru.", "setting_image_viewer_preview_title": "Načítať náhľad obrázka", "setting_image_viewer_title": "Obrázky", "setting_languages_apply": "Použiť", - "setting_notifications_notify_failures_grace_period": "Oznámenie o zlyhaní zálohovania na pozadí: {duration}", + "setting_languages_subtitle": "Zmeniť jazyk aplikácie", + "setting_notifications_notify_failures_grace_period": "Upozorniť na zlyhanie zálohovania na pozadí: {duration}", "setting_notifications_notify_hours": "{count} hodín", "setting_notifications_notify_immediately": "okamžite", "setting_notifications_notify_minutes": "{count} minút", @@ -1533,14 +1724,18 @@ "setting_notifications_notify_seconds": "{count} sekúnd", "setting_notifications_single_progress_subtitle": "Podrobné informácie o priebehu nahrávania pre položku", "setting_notifications_single_progress_title": "Zobraziť priebeh detailov zálohovania na pozadí", - "setting_notifications_subtitle": "Prispôsobenie predvolieb oznámení", + "setting_notifications_subtitle": "Upravte svoje nastavenia oznámení", "setting_notifications_total_progress_subtitle": "Celkový priebeh nahrávania (nahraných/celkovo)", "setting_notifications_total_progress_title": "Zobraziť celkový priebeh zálohovania na pozadí", "setting_video_viewer_looping_title": "Opakovanie", + "setting_video_viewer_original_video_subtitle": "Pri streamovaní videa zo servera prehrať originál, aj keď je k dispozícii prekódované video. Môže to viesť k prerušovanému prehrávaniu videa. Videá dostupné lokálne sa prehrajú v pôvodnej kvalite bez ohľadu na toto nastavenie.", + "setting_video_viewer_original_video_title": "Vynútiť pôvodné video", "settings": "Nastavenia", "settings_require_restart": "Na použitie tohto nastavenia reštartujte Immich", "settings_saved": "Nastavenia boli uložené", + "setup_pin_code": "Nastavte si PIN kód", "share": "Zdieľať", + "share_action_prompt": "{count} položiek zdieľaných", "share_add_photos": "Pridať fotografie", "share_assets_selected": "{count} označených", "share_dialog_preparing": "Pripravujem...", @@ -1549,7 +1744,7 @@ "shared_album_activities_input_disable": "Komentár je zakázaný", "shared_album_activity_remove_content": "Chcete vymazať túto aktivitu?", "shared_album_activity_remove_title": "Vymazať aktivitu", - "shared_album_section_people_action_error": "Vyskytla sa chyba pri odchádzaní / odstraňovaní používateľa z albumu", + "shared_album_section_people_action_error": "Vyskytla sa chyba pri opustení/odobratí z albumu", "shared_album_section_people_action_leave": "Odstrániť používateľa z albumu", "shared_album_section_people_action_remove_user": "Odstrániť používateľa z albumu", "shared_album_section_people_title": "ĽUDIA", @@ -1562,6 +1757,7 @@ "shared_link_clipboard_copied_massage": "Skopírované do schránky", "shared_link_clipboard_text": "Odkaz: {link}\nHeslo: {password}", "shared_link_create_error": "Vyskytla sa chyba behom vytvárania zdieľaného odkazu", + "shared_link_custom_url_description": "Prístup k tomuto zdieľanému odkazu pomocou vlastnej URL adresy", "shared_link_edit_description_hint": "Zadajte popis zdieľania", "shared_link_edit_expire_after_option_day": "1 deň", "shared_link_edit_expire_after_option_days": "{count} dní", @@ -1574,22 +1770,24 @@ "shared_link_edit_password_hint": "Zadajte heslo zdieľania", "shared_link_edit_submit_button": "Aktualizovať odkaz", "shared_link_error_server_url_fetch": "Nemožno nájsť URL severa", - "shared_link_expires_day": "Vyprší o {count} dní", + "shared_link_expires_day": "Vyprší o {count} deň", "shared_link_expires_days": "Vyprší o {count} dní", - "shared_link_expires_hour": "Vyprší o {count} hodín", + "shared_link_expires_hour": "Vyprší o {count} hodinu", "shared_link_expires_hours": "Vyprší o {count} hodín", - "shared_link_expires_minute": "Vyprší o {count} minút", + "shared_link_expires_minute": "Vyprší o {count} minútu", "shared_link_expires_minutes": "Vyprší o {count} minút", "shared_link_expires_never": "Nevyprší", - "shared_link_expires_second": "Vyprší o {count} sekúnd", + "shared_link_expires_second": "Vyprší o {count} sekundu", "shared_link_expires_seconds": "Vyprší o {count} sekúnd", "shared_link_individual_shared": "Individuálne zdieľané", "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Spravovať zdieľané odkazy", "shared_link_options": "Možnosti zdieľaných odkazov", + "shared_link_password_description": "Vyžadovať heslo pre prístup k tomuto zdieľanému odkazu", "shared_links": "Zdieľané odkazy", "shared_links_description": "Zdieľanie fotografií a videí pomocou odkazu", - "shared_photos_and_videos_count": "{assetCount, plural, other {# zdieľané fotky a videá.}}", + "shared_photos_and_videos_count": "{assetCount, plural, few {# zdieľané fotky a videá.} other {# zdieľaných fotiek a videí.}}", + "shared_with_me": "Zdieľané so mnou", "shared_with_partner": "Zdieľané s {partner}", "sharing": "Zdieľanie", "sharing_enter_password": "Ak chcete zobraziť túto stránku, prosím, zadajte heslo.", @@ -1599,7 +1797,7 @@ "sharing_sidebar_description": "Zobraziť odkaz na Zdieľanie v bočnom paneli", "sharing_silver_appbar_create_shared_album": "Vytvoriť zdieľaný album", "sharing_silver_appbar_share_partner": "Zdieľať s partnerom", - "shift_to_permanent_delete": "stlačte ⇧ pre nemenné zmazanie pložiek", + "shift_to_permanent_delete": "stlačte ⇧ na trvalé vymazanie položky", "show_album_options": "Zobraziť možnosti albumu", "show_albums": "Zobraziť albumy", "show_all_people": "Zobraziť všetkých ľudí", @@ -1608,21 +1806,21 @@ "show_gallery": "Zobraziť galériu", "show_hidden_people": "Zobraziť skrytých ľudí", "show_in_timeline": "Zobraziť na časovej osi", - "show_in_timeline_setting_description": "Zobrazí fotky a videá tohoto používateľa na časovej osi", + "show_in_timeline_setting_description": "Zobraziť fotky a videá tohoto používateľa na vašej časovej osi", "show_keyboard_shortcuts": "Zobraziť klávesové skratky", "show_metadata": "Zobraziť metadáta", - "show_or_hide_info": "Zobrazí alebo skryje info", + "show_or_hide_info": "Zobraziť alebo skryť informácie", "show_password": "Zobraziť heslo", - "show_person_options": "Zobrazí možnosti osoby", - "show_progress_bar": "Zobrazí ukazovateľ priebehu", + "show_person_options": "Zobraziť možnosti osoby", + "show_progress_bar": "Zobraziť ukazovateľ priebehu", "show_search_options": "Zobraziť možnosti vyhľadávania", "show_shared_links": "Zobraziť zdieľané odkazy", - "show_slideshow_transition": "Zobrazí prechody v prezentácii", + "show_slideshow_transition": "Zobraziť prechody v prezentácii", "show_supporter_badge": "Odznak podporovateľa", "show_supporter_badge_description": "Zobraziť odznak podporovateľa", "shuffle": "Náhodné poradie", "sidebar": "Bočný panel", - "sidebar_display_description": "Zobrazí odkaz na pohľad v bočnom paneli", + "sidebar_display_description": "Zobraziť odkaz na zobrazenie v bočnom paneli", "sign_out": "Odhlásiť sa", "sign_up": "Registrovať", "size": "Veľkosť", @@ -1641,24 +1839,27 @@ "sort_title": "Názov", "source": "Zdroj", "stack": "Zoskupenie", + "stack_action_prompt": "{count} zoskupených", "stack_duplicates": "Zoskupiť duplicity", "stack_select_one_photo": "Vyberte jednu hlavnú fotku pre zoskupenie", "stack_selected_photos": "Zoskupiť vybraté fotky", - "stacked_assets_count": "{count, plural, one {Zoskupená # položka} other {Zoskupených # položiek}}", + "stacked_assets_count": "{count, plural, one {Zoskupená # položka} few {Zoskupené # položky} other {Zoskupených # položiek}}", "stacktrace": "Výpis zásobníku", "start": "Štart", - "start_date": "Začiatočný dátum", + "start_date": "Počiatočný dátum", "state": "Štát", "status": "Stav", + "stop_casting": "Zastaviť prenos", "stop_motion_photo": "Stopmotion fotka", "stop_photo_sharing": "Zastaviť zdieľanie vašich fotiek?", "stop_photo_sharing_description": "{partner} už nebude mať prístup k vašim fotkám.", - "stop_sharing_photos_with_user": "Zastaviť zdieľanie týchto fotiek s týmto používateľom", + "stop_sharing_photos_with_user": "Zastaviť zdieľanie vašich fotiek s týmto používateľom", "storage": "Ukladací priestor", "storage_label": "Štítok úložiska", "storage_quota": "Úložný limit", "storage_usage": "Využitých {used} z {available}", "submit": "Odoslať", + "success": "Úspech", "suggestions": "Návrhy", "sunrise_on_the_beach": "Východ slnka na pláži", "support": "Podpora", @@ -1667,24 +1868,34 @@ "swap_merge_direction": "Vymeniť smer zlúčenia", "sync": "Synchronizovať", "sync_albums": "Synchronizovať albumy", - "tag": "Značka", - "tag_assets": "Pridať značku", - "tag_created": "Vytvorená značka: {tag}", - "tag_feature_description": "Prehliadanie fotiek a videá zoskupených podľa tematických značiek", - "tag_not_found_question": "Neviete nájsť značku? Vytvorte novú značku.", + "sync_albums_manual_subtitle": "Synchronizujte všetky nahrané videá a fotografie s vybranými záložnými albumami", + "sync_local": "Synchronizovať lokálne", + "sync_remote": "Synchronizovať vzdialené", + "sync_upload_album_setting_subtitle": "Vytvárajte a nahrávajte svoje fotografie a videá do vybraných albumov na Immich", + "tag": "Štítok", + "tag_assets": "Pridať štítky", + "tag_created": "Vytvorený štítok: {tag}", + "tag_feature_description": "Prehliadanie fotiek a videá zoskupených podľa tematických štítkov", + "tag_not_found_question": "Neviete nájsť štítok? Vytvorte nový štítok.", "tag_people": "Označiť ľudí", - "tag_updated": "Upravená značka: {tag}", - "tagged_assets": "Značka priradená {count, plural, one {# položke} other {# položkám}}", + "tag_updated": "Upravený štítok: {tag}", + "tagged_assets": "Štítok priradený {count, plural, one {# položke} other {# položkám}}", "tags": "Štítky", + "tap_to_run_job": "Ťuknutím na položku spustíte úlohu", "template": "Šablóna", "theme": "Téma", "theme_selection": "Výber témy", - "theme_selection_description": "Automaticky nastaví tému na svetlú alebo tmavú podľa systémových preferencií v prehliadači", - "theme_setting_asset_list_storage_indicator_title": "Zobraziť indikátor úložiska na dlaždiciach položiek", + "theme_selection_description": "Automaticky nastaví tému na svetlú alebo tmavú podľa systémových predvolieb v prehliadači", + "theme_setting_asset_list_storage_indicator_title": "Zobraziť indikátor úložiska na dlaždiciach médií", "theme_setting_asset_list_tiles_per_row_title": "Počet položiek na riadok ({count})", - "theme_setting_image_viewer_quality_subtitle": "Prispôsobenie kvality prehliadača detailov", + "theme_setting_colorful_interface_subtitle": "Použiť základnú farbu na plochy na pozadí.", + "theme_setting_colorful_interface_title": "Farebné rozhranie", + "theme_setting_image_viewer_quality_subtitle": "Upravte kvalitu detailného prehliadača obrázkov", "theme_setting_image_viewer_quality_title": "Kvalita prehliadača obrázkov", - "theme_setting_system_theme_switch": "Automaticky (podľa systemového nastavenia)", + "theme_setting_primary_color_subtitle": "Vyberte si farbu pre základné akcie a dôrazy.", + "theme_setting_primary_color_title": "Základná farba", + "theme_setting_system_primary_color_title": "Použiť systémovú farbu", + "theme_setting_system_theme_switch": "Automaticky (podľa systémového nastavenia)", "theme_setting_theme_subtitle": "Vyberte nastavenia témy aplikácie", "theme_setting_three_stage_loading_subtitle": "Trojstupňové načítanie môže zvýšiť výkonnosť načítania, ale vedie k výrazne vyššiemu zaťaženiu siete", "theme_setting_three_stage_loading_title": "Povolenie trojstupňového načítavania", @@ -1703,31 +1914,37 @@ "total": "Celkom", "total_usage": "Celkové využitie", "trash": "Kôš", + "trash_action_prompt": "{count} presunutých do koša", "trash_all": "Všetko do koša", "trash_count": "{count, number} do koša", "trash_delete_asset": "Položky do koša/odstrániť", + "trash_emptied": "Kôš vyprázdnený", "trash_no_results_message": "Vymazané fotografie a videá sa zobrazia tu.", "trash_page_delete_all": "Vymazať všetky", - "trash_page_empty_trash_dialog_content": "Skutočne chcete vyprázdniť kôš? Tieto položky budú permanentne odstránené z Immichu", + "trash_page_empty_trash_dialog_content": "Skutočne chcete vyprázdniť kôš? Tieto položky budú permanentne odstránené z aplikácie Immich", "trash_page_info": "Médiá v koši sa permanentne odstránia po {days} dňoch", "trash_page_no_assets": "Žiadne médiá v koši", "trash_page_restore_all": "Obnoviť všetky", - "trash_page_select_assets_btn": "Označiť médiá", + "trash_page_select_assets_btn": "Vybrať médiá", "trash_page_title": "Kôš ({count})", "trashed_items_will_be_permanently_deleted_after": "Položky v koši sa natrvalo vymažú po {days, plural, one {# dni} other {# dňoch}}.", "type": "Typ", + "unable_to_change_pin_code": "Nie je možné zmeniť PIN kód", + "unable_to_setup_pin_code": "Nie je možné nastaviť PIN kód", "unarchive": "Odarchivovať", + "unarchive_action_prompt": "{count} odstránené z archívu", "unarchived_count": "{count, plural, other {Odarchivovaných #}}", "undo": "Späť", "unfavorite": "Odznačiť ako obľúbené", + "unfavorite_action_prompt": "{count} odstránené z Obľúbených", "unhide_person": "Odkryť osobu", "unknown": "Neznáme", - "unknown_country": "Neznámy štát", + "unknown_country": "Neznáma krajina", "unknown_year": "Neznámy rok", "unlimited": "Neobmedzené", "unlink_motion_video": "Odpojiť pohyblivé video", "unlink_oauth": "Odpojiť OAuth", - "unlinked_oauth_account": "Odpojiť OAuth účet", + "unlinked_oauth_account": "Odpojený OAuth účet", "unmute_memories": "Zrušenie stlmenia spomienok", "unnamed_album": "Nepomenovaný album", "unnamed_album_delete_confirmation": "Ste si istý, že chcete zmazať tento album?", @@ -1735,47 +1952,59 @@ "unsaved_change": "Neuložená zmena", "unselect_all": "Zrušiť výber všetkých", "unselect_all_duplicates": "Zrušiť výber všetkých duplicít", + "unselect_all_in": "Zrušiť výber všetkých v {group}", "unstack": "Odskupiť", - "unstacked_assets_count": "{count, plural, one {Rozložená # položka} few {Rozložené # položky} other {Rozložených # položiek}}", + "unstack_action_prompt": "{count} nezoskupených", + "unstacked_assets_count": "Zrušenie zoskupenia pre {count, plural, one {# položku} few {# položky} other {# položiek}}", + "untagged": "Bez štítku", "up_next": "To je všetko", "updated_at": "Aktualizované", "updated_password": "Heslo zmenené", "upload": "Nahrať", + "upload_action_prompt": "{count} v poradí na nahratie", "upload_concurrency": "Súbežnosť nahrávania", + "upload_details": "Podrobnosti o nahrávaní", "upload_dialog_info": "Chcete zálohovať zvolené médiá na server?", "upload_dialog_title": "Nahrať médiá", - "upload_errors": "Nahrávanie ukončené s {count, plural, one {# chybou} other {# chybami}}, obnovte stránku aby sa zobrazili nové položky.", + "upload_errors": "Nahrávanie ukončené s {count, plural, one {# chybou} other {# chybami}}, obnovte stránku, aby sa zobrazili nové položky.", + "upload_finished": "Nahrávanie dokončené", "upload_progress": "Ostáva {remaining, number} - Spracovaných {processed, number}/{total, number}", - "upload_skipped_duplicates": "{count, plural, one {Preskočená # duplicita} few {Preskočené # duplicity} other {Preskočených # duplicít}}", + "upload_skipped_duplicates": "{count, plural, one {Preskočená # duplicitná položka} few {Preskočené # duplicitné položky} other {Preskočených # duplicitných položiek}}", "upload_status_duplicates": "Duplikáty", "upload_status_errors": "Chyby", "upload_status_uploaded": "Nahrané", "upload_success": "Nahrávanie úspešné, pridané súbory sa zobrazia po obnovení stránky.", "upload_to_immich": "Nahrať na Immich ({count})", "uploading": "Nahrávanie", + "uploading_media": "Nahrávanie médií", "url": "Odkaz URL", "usage": "Použitie", + "use_biometric": "Použiť biometrické údaje", + "use_current_connection": "použiť aktuálne pripojenie", "use_custom_date_range": "Použiť radšej vlastný rozsah dátumov", "user": "Používateľ", "user_has_been_deleted": "Tento používateľ bol vymazaný.", - "user_id": "Používateľské ID", + "user_id": "ID používateľa", "user_liked": "Používateľovi {user} sa páči {type, select, photo {táto fotka} video {toto video} asset {táto položka} other {toto}}", "user_pin_code_settings": "PIN kód", "user_pin_code_settings_description": "Spravujte svoj PIN kód", + "user_privacy": "Ochrana osobných údajov používateľa", "user_purchase_settings": "Nákup", - "user_purchase_settings_description": "Správa vášho nákupu", + "user_purchase_settings_description": "Spravujte svoj nákup", "user_role_set": "Nastav {user} ako {role}", "user_usage_detail": "Podrobnosti o využívaní používateľmi", "user_usage_stats": "Štatistiky využitia účtu", "user_usage_stats_description": "Zobraziť štatistiky využitia účtu", "username": "Používateľské meno", "users": "Používatelia", + "users_added_to_album_count": "{count, plural, one {Pridaný # používateľ} few {Pridaní # používatelia} other {Pridaných # používateľov}} do albumu", "utilities": "Nástroje", "validate": "Validovať", + "validate_endpoint_error": "Zadajte prosím platnú URL adresu", "variables": "Premenné", "version": "Verzia", "version_announcement_closing": "Tvoj kamarát, Alex", - "version_announcement_message": "Ahoj! Nová verzia Immich je dostupná. Prosím prečítajte si poznámky k vydaniu, aby ste sa uistili, že inštalácia bude aktuálna bez problémov, najmä ak používate WatchTower alebo akýkoľvek spôsob automatickej aktualizácie Immich servera.", + "version_announcement_message": "Ahoj! K dispozícii je nová verzia aplikácie Immich. Prosím, venujte trochu času prečítaniu poznámok k vydaniu, aby ste sa uistili, že vaša inštalácia je aktuálna a predišli tak akýmkoľvek chybám v konfigurácii, najmä ak používate WatchTower alebo akýkoľvek mechanizmus, ktorý sa stará o automatickú aktualizáciu vašej inštancie Immich.", "version_history": "História verzií", "version_history_item": "Inštalovaná {version} dňa {date}", "video": "Video", @@ -1783,10 +2012,11 @@ "video_hover_setting_description": "Prehrá video náhľad keď kurzor myši prejde cez položku. Aj keď je vypnuté, prehrávanie sa môže spustiť nabehnutí cez ikonu Prehrať.", "videos": "Videá", "videos_count": "{count, plural, one {# Video} few {# Videá} other {# Videí}}", - "view": "Zobraziť", + "view": "Zobrazenie", "view_album": "Zobraziť Album", "view_all": "Zobraziť všetky", "view_all_users": "Zobraziť všetkých používateľov", + "view_details": "Zobraziť podrobnosti", "view_in_timeline": "Zobraziť v časovej osi", "view_link": "Zobraziť odkaz", "view_links": "Zobraziť odkazy", @@ -1799,8 +2029,8 @@ "viewer_remove_from_stack": "Odstrániť zo zoskupenia", "viewer_stack_use_as_main_asset": "Použiť ako hlavnú fotku", "viewer_unstack": "Odskupiť", - "visibility_changed": "Viditeľnosť zmenená pre {count, plural, one {# osobu} other {# ľudí}}", - "waiting": "Čaká", + "visibility_changed": "Viditeľnosť zmenená pre {count, plural, one {# osobu} few {# osoby} other {# osôb}}", + "waiting": "Čakajúce", "warning": "Varovanie", "week": "Týždeň", "welcome": "Vitajte", @@ -1810,7 +2040,7 @@ "year": "Rok", "years_ago": "pred {years, plural, one {# rokom} other {# rokmi}}", "yes": "Áno", - "you_dont_have_any_shared_links": "Nemáte žiadne zdielané linky", + "you_dont_have_any_shared_links": "Nemáte žiadne zdielané odkazy", "your_wifi_name": "Váš názov siete Wi-Fi", "zoom_image": "Priblížiť obrázok" } diff --git a/i18n/sl.json b/i18n/sl.json index 5132d09fc8..ad67e2f5c4 100644 --- a/i18n/sl.json +++ b/i18n/sl.json @@ -14,6 +14,7 @@ "add_a_location": "Dodaj lokacijo", "add_a_name": "Dodaj ime", "add_a_title": "Dodaj naslov", + "add_birthday": "Dodaj rojstni dan", "add_endpoint": "Dodaj končno točko", "add_exclusion_pattern": "Dodaj vzorec izključitve", "add_import_path": "Dodaj pot uvoza", @@ -44,6 +45,13 @@ "backup_database": "Ustvari izpis baze podatkov", "backup_database_enable_description": "Omogoči izpise baze podatkov", "backup_keep_last_amount": "Število prejšnjih odlagališč, ki jih je treba obdržati", + "backup_onboarding_1_description": "kopijo zunaj lokacije v oblaku ali na drugi fizični lokaciji.", + "backup_onboarding_2_description": "lokalne kopije na različnih napravah. To vključuje glavne datoteke in lokalno varnostno kopijo teh datotek.", + "backup_onboarding_3_description": "skupno število kopij vaših podatkov, vključno z izvirnimi datotekami. To vključuje 1 kopijo zunaj lokacije in 2 lokalni kopiji.", + "backup_onboarding_description": "Za zaščito podatkov priporočamo strategijo varnostnega kopiranja 3-2-1. Za celovito rešitev varnostnega kopiranja hranite kopije naloženih fotografij/videoposnetkov in podatkovne baze Immich.", + "backup_onboarding_footer": "Za več informacij o varnostnem kopiranju Immicha glejte dokumentacijo.", + "backup_onboarding_parts_title": "Varnostna kopija 3-2-1 vključuje:", + "backup_onboarding_title": "Varnostne kopije", "backup_settings": "Nastavitve izpisa baze podatkov", "backup_settings_description": "Upravljanje nastavitev izpisa podatkovne baze.", "cleared_jobs": "Razčiščeno opravilo za: {job}", @@ -166,6 +174,20 @@ "metadata_settings_description": "Upravljanje nastavitev metapodatkov", "migration_job": "Migracija", "migration_job_description": "Preselite sličice za sredstva in obraze v najnovejšo strukturo map", + "nightly_tasks_cluster_faces_setting_description": "Zaženi prepoznavanje obrazov na novo zaznanih obrazih", + "nightly_tasks_cluster_new_faces_setting": "Združite nove obraze", + "nightly_tasks_database_cleanup_setting": "Naloge čiščenja baze podatkov", + "nightly_tasks_database_cleanup_setting_description": "Očistite stare, potekle podatke iz baze podatkov", + "nightly_tasks_generate_memories_setting": "Ustvarjajte spomine", + "nightly_tasks_generate_memories_setting_description": "Ustvarite nove spomine iz sredstev", + "nightly_tasks_missing_thumbnails_setting": "Ustvari manjkajoče sličice", + "nightly_tasks_missing_thumbnails_setting_description": "Sredstva brez sličic postavite v čakalno vrsto za ustvarjanje sličic", + "nightly_tasks_settings": "Nastavitve nočnih opravil", + "nightly_tasks_settings_description": "Upravljajte nočne naloge", + "nightly_tasks_start_time_setting": "Začetni čas", + "nightly_tasks_start_time_setting_description": "Čas, ko strežnik začne izvajati nočne naloge", + "nightly_tasks_sync_quota_usage_setting": "Poraba kvote za sinhronizacijo", + "nightly_tasks_sync_quota_usage_setting_description": "Posodobi kvoto shrambe uporabnikov glede na trenutno uporabo", "no_paths_added": "Ni dodanih poti", "no_pattern_added": "Brez dodanega vzorca", "note_apply_storage_label_previous_assets": "Opomba: Če želite oznako za shranjevanje uporabiti za predhodno naložena sredstva, zaženite", @@ -196,6 +218,8 @@ "oauth_mobile_redirect_uri": "Mobilni preusmeritveni URI", "oauth_mobile_redirect_uri_override": "Preglasitev URI preusmeritve za mobilne naprave", "oauth_mobile_redirect_uri_override_description": "Omogoči, ko ponudnik OAuth ne dovoli mobilnega URI-ja, kot je ''{callback}''", + "oauth_role_claim": "Zahteva vloge", + "oauth_role_claim_description": "Samodejno dodeli skrbniški dostop na podlagi prisotnosti tega zahtevka. Zahtevek ima lahko »uporabnik« ali »skrbnik«.", "oauth_settings": "OAuth", "oauth_settings_description": "Upravljanje nastavitev prijave OAuth", "oauth_settings_more_details": "Za več podrobnosti o tej funkciji glejte dokumentacijo.", @@ -357,10 +381,12 @@ "admin_password": "Skrbniško geslo", "administration": "Administracija", "advanced": "Napredno", + "advanced_settings_beta_timeline_subtitle": "Preizkusite novo izkušnjo aplikacije", + "advanced_settings_beta_timeline_title": "Časovnica beta različice", "advanced_settings_enable_alternate_media_filter_subtitle": "Uporabite to možnost za filtriranje medijev med sinhronizacijo na podlagi alternativnih meril. To poskusite le, če imate težave z aplikacijo, ki zaznava vse albume.", "advanced_settings_enable_alternate_media_filter_title": "[EKSPERIMENTALNO] Uporabite alternativni filter za sinhronizacijo albuma v napravi", "advanced_settings_log_level_title": "Nivo dnevnika: {level}", - "advanced_settings_prefer_remote_subtitle": "Nekatere naprave zelo počasi nalagajo sličice iz sredstev v napravi. Aktivirajte to nastavitev, če želite namesto tega naložiti oddaljene slike.", + "advanced_settings_prefer_remote_subtitle": "Nekatere naprave zelo počasi nalagajo sličice iz lokalnih sredstev. Aktivirajte to nastavitev, če želite namesto tega naložiti oddaljene slike.", "advanced_settings_prefer_remote_title": "Uporabi raje oddaljene slike", "advanced_settings_proxy_headers_subtitle": "Določi proxy glavo, ki jo naj Immich pošlje ob vsaki mrežni zahtevi", "advanced_settings_proxy_headers_title": "Proxy glave", @@ -379,6 +405,7 @@ "album_cover_updated": "Naslovnica albuma posodobljena", "album_delete_confirmation": "Ali ste prepričani, da želite izbrisati album {album}?", "album_delete_confirmation_description": "Če je ta album v skupni rabi, drugi uporabniki ne bodo mogli več dostopati do njega.", + "album_deleted": "Album izbrisan", "album_info_card_backup_album_excluded": "IZKLJUČENO", "album_info_card_backup_album_included": "VKLJUČENO", "album_info_updated": "Podatki o albumu posodobljeni", @@ -388,6 +415,7 @@ "album_options": "Možnosti albuma", "album_remove_user": "Odstrani uporabnika?", "album_remove_user_confirmation": "Ali ste prepričani, da želite odstraniti {user}?", + "album_search_not_found": "Ni najdenih albumov, ki bi ustrezali vašemu iskanju", "album_share_no_users": "Videti je, da ste ta album dali v skupno rabo z vsemi uporabniki ali pa nimate nobenega uporabnika, s katerim bi ga lahko delili.", "album_updated": "Album posodobljen", "album_updated_setting_description": "Prejmite e-poštno obvestilo, ko ima album v skupni rabi nova sredstva", @@ -407,6 +435,7 @@ "albums_default_sort_order": "Privzeti vrstni red razvrščanja albumov", "albums_default_sort_order_description": "Začetni vrstni red razvrščanja sredstev pri ustvarjanju novih albumov.", "albums_feature_description": "Zbirke sredstev, ki jih je mogoče deliti z drugimi uporabniki.", + "albums_on_device_count": "Albumi v napravi ({count})", "all": "Vse", "all_albums": "Vsi albumi", "all_people": "Vsi ljudje", @@ -427,6 +456,7 @@ "app_settings": "Nastavitve aplikacije", "appears_in": "Pojavi se v", "archive": "Arhiv", + "archive_action_prompt": "v arhiv je dodanih {count}", "archive_or_unarchive_photo": "Arhivirajte ali odstranite fotografijo iz arhiva", "archive_page_no_archived_assets": "Ni arhiviranih sredstev", "archive_page_title": "Arhiv ({count})", @@ -464,7 +494,6 @@ "assets": "Sredstva", "assets_added_count": "Dodano{count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}}", "assets_added_to_album_count": "Dodano {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}} v album", - "assets_added_to_name_count": "Dodano {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}} v {hasName, select, true {{name}} other {new album}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Sredstvo} two {Sredstvi} few {Sredstva} other {Sredstev}} ni mogoče dodati v album", "assets_count": "{count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}}", "assets_deleted_permanently": "trajno izrisana sredstva {count}", @@ -490,6 +519,7 @@ "back_close_deselect": "Nazaj, zaprite ali prekličite izbiro", "background_location_permission": "Dovoljenje za iskanje lokacije v ozadju", "background_location_permission_content": "Ko deluje v ozadju mora imeti Immich za zamenjavo omrežij, *vedno* dostop do natančne lokacije, da lahko aplikacija prebere ime omrežja Wi-Fi", + "backup": "Varnostna kopija", "backup_album_selection_page_albums_device": "Albumi v napravi ({count})", "backup_album_selection_page_albums_tap": "Tapnite za vključitev, dvakrat tapnite za izključitev", "backup_album_selection_page_assets_scatter": "Sredstva so lahko razpršena po več albumih. Tako je mogoče med postopkom varnostnega kopiranja albume vključiti ali izključiti.", @@ -553,6 +583,8 @@ "backup_options_page_title": "Možnosti varnostne kopije", "backup_setting_subtitle": "Upravljaj nastavitve nalaganja v ozadju in ospredju", "backward": "Nazaj", + "beta_sync": "Stanje sinhronizacije beta različice", + "beta_sync_subtitle": "Upravljanje novega sistema sinhronizacije", "biometric_auth_enabled": "Biometrična avtentikacija omogočena", "biometric_locked_out": "Biometrična avtentikacija vam je onemogočena", "biometric_no_options": "Biometrične možnosti niso na voljo", @@ -570,7 +602,7 @@ "cache_settings_clear_cache_button": "Počisti predpomnilnik", "cache_settings_clear_cache_button_title": "Počisti predpomnilnik aplikacije. To bo znatno vplivalo na delovanje aplikacije, dokler se predpomnilnik ne obnovi.", "cache_settings_duplicated_assets_clear_button": "POČISTI", - "cache_settings_duplicated_assets_subtitle": "Fotografije in videoposnetki, ki jih je aplikacija uvrstila na črni seznam", + "cache_settings_duplicated_assets_subtitle": "Fotografije in videoposnetki, ki so prezrti s strani aplikacije", "cache_settings_duplicated_assets_title": "Podvojena sredstva ({count})", "cache_settings_statistics_album": "Sličice knjižnice", "cache_settings_statistics_full": "Izvirne slike", @@ -587,6 +619,7 @@ "cancel": "Prekliči", "cancel_search": "Prekliči iskanje", "canceled": "Preklicano", + "canceling": "Preklic", "cannot_merge_people": "Oseb ni mogoče združiti", "cannot_undo_this_action": "Tega dejanja ne morete razveljaviti!", "cannot_update_the_description": "Opisa ni mogoče posodobiti", @@ -700,10 +733,11 @@ "current_server_address": "Trenutni naslov strežnika", "custom_locale": "Jezik po meri", "custom_locale_description": "Oblikujte datume in številke glede na jezik in regijo", + "custom_url": "URL po meri", "daily_title_text_date": "E, MMM dd", "daily_title_text_date_year": "E, MMM dd, yyyy", "dark": "Temno", - "darkTheme": "Preklopi na temno temo", + "dark_theme": "Preklopi temno temo", "date_after": "Datum po", "date_and_time": "Datum in ura", "date_before": "Datum pred", @@ -719,6 +753,8 @@ "default_locale": "Privzeti jezik", "default_locale_description": "Oblikujte datume in številke glede na lokalne nastavitve brskalnika", "delete": "Izbriši", + "delete_action_confirmation_message": "Ali ste prepričani, da želite izbrisati to sredstvo? S tem dejanjem boste sredstvo premaknili v koš na strežniku in vas pozvali, ali ga želite izbrisati lokalno", + "delete_action_prompt": "izbrisano {count}", "delete_album": "Izbriši album", "delete_api_key_prompt": "Ali ste prepričani, da želite izbrisati ta API ključ?", "delete_dialog_alert": "Ti elementi bodo trajno izbrisani iz Immicha in vaše naprave", @@ -732,9 +768,12 @@ "delete_key": "Izbriši ključ", "delete_library": "Izbriši knjižnico", "delete_link": "Izbriši povezavo", + "delete_local_action_prompt": "{count} izbrisano lokalno", "delete_local_dialog_ok_backed_up_only": "Izbriši samo kar je varnostno kopirano", "delete_local_dialog_ok_force": "Vseeno izbriši", "delete_others": "Izbriši ostale", + "delete_permanently": "Izbriši trajno", + "delete_permanently_action_prompt": "{count} trajno izbrisano", "delete_shared_link": "Izbriši povezavo skupne rabe", "delete_shared_link_dialog_title": "Izbriši povezavo skupne rabe", "delete_tag": "Izbriši oznako", @@ -745,6 +784,7 @@ "description": "Opis", "description_input_hint_text": "Dodaj opis ...", "description_input_submit_error": "Napaka pri posodabljanju opisa, preverite dnevnik za več podrobnosti", + "deselect_all": "Prekliči vse", "details": "Podrobnosti", "direction": "Usmeritev", "disabled": "Onemogočeno", @@ -762,6 +802,7 @@ "documentation": "Dokumentacija", "done": "Končano", "download": "Prenesi", + "download_action_prompt": "Prenašanje {count} sredstev", "download_canceled": "Prenos preklican", "download_complete": "Prenos končan", "download_enqueue": "Prenos v čakalni vrsti", @@ -788,6 +829,7 @@ "edit": "Uredi", "edit_album": "Uredi album", "edit_avatar": "Uredi avatar", + "edit_birthday": "Uredi rojstni dan", "edit_date": "Uredi datum", "edit_date_and_time": "Uredi datum in uro", "edit_description": "Uredi opis", @@ -799,6 +841,7 @@ "edit_key": "Uredi ključ", "edit_link": "Uredi povezavo", "edit_location": "Uredi lokacijo", + "edit_location_action_prompt": "urejenih {count} lokacij", "edit_location_dialog_title": "Lokacija", "edit_name": "Uredi ime", "edit_people": "Uredi osebe", @@ -817,6 +860,7 @@ "empty_trash": "Izprazni smetnjak", "empty_trash_confirmation": "Ste prepričani, da želite izprazniti smetnjak? S tem boste iz Immicha trajno odstranili vsa sredstva v smetnjaku.\nTega dejanja ne morete razveljaviti!", "enable": "Omogoči", + "enable_backup": "Omogoči varnostno kopiranje", "enable_biometric_auth_description": "Vnesite svojo PIN kodo, da omogočite biometrično preverjanje pristnosti", "enabled": "Omogočeno", "end_date": "Končni datum", @@ -953,6 +997,7 @@ }, "exif": "Exif", "exif_bottom_sheet_description": "Dodaj opis..", + "exif_bottom_sheet_description_error": "Napaka pri posodabljanju opisa", "exif_bottom_sheet_details": "PODROBNOSTI", "exif_bottom_sheet_location": "LOKACIJA", "exif_bottom_sheet_people": "OSEBE", @@ -973,6 +1018,8 @@ "explorer": "Raziskovalec", "export": "Izvoz", "export_as_json": "Izvozi kot JSON", + "export_database": "Izvoz baze podatkov", + "export_database_description": "Izvozite bazo podatkov SQLite", "extension": "Razširitev", "external": "Zunanji", "external_libraries": "Zunanje knjižnice", @@ -984,6 +1031,7 @@ "failed_to_load_assets": "Sredstev ni bilo mogoče naložiti", "failed_to_load_folder": "Mape ni bilo mogoče naložiti", "favorite": "Priljubljen", + "favorite_action_prompt": "med priljubljene je dodanih {count}", "favorite_or_unfavorite_photo": "Priljubljena ali nepriljubljena fotografija", "favorites": "Priljubljene", "favorites_page_no_favorites": "Ni priljubljenih sredstev", @@ -1023,6 +1071,9 @@ "haptic_feedback_switch": "Uporabi haptičen odziv", "haptic_feedback_title": "Haptičen odziv", "has_quota": "Ima kvoto", + "hash_asset": "Zgoščeno sredstvo", + "hashed_assets": "Zgoščena sredstva", + "hashing": "Zgoščevanje", "header_settings_add_header_tip": "Dodaj glavo", "header_settings_field_validator_msg": "Vrednost ne sme biti prazna", "header_settings_header_name_input": "Ime glave", @@ -1055,6 +1106,7 @@ "host": "Gostitelj", "hour": "Ura", "id": "ID", + "idle": "Nedejavnost", "ignore_icloud_photos": "Ignoriraj fotografije iCloud", "ignore_icloud_photos_description": "Fotografije, shranjene v iCloud, ne bodo naložene na strežnik Immich", "image": "Slika", @@ -1112,6 +1164,7 @@ "language_no_results_title": "Ni najdenih jezikov", "language_search_hint": "Iskanje jezikov...", "language_setting_description": "Izberite želeni jezik", + "large_files": "Velike datoteke", "last_seen": "Nazadnje viden", "latest_version": "Najnovejša različica", "latitude": "Zemljepisna širina", @@ -1127,16 +1180,18 @@ "library_page_sort_created": "Nazadnje ustvarjeno", "library_page_sort_last_modified": "Nazadnje spremenjeno", "library_page_sort_title": "Naslov albuma", + "licenses": "Licence", "light": "Svetlo", "like_deleted": "Všeček izbrisan", "link_motion_video": "Povezava videa gibanja", - "link_options": "Možnosti povezave", "link_to_oauth": "Povezava do OAuth", "linked_oauth_account": "Povezan račun OAuth", "list": "Seznam", "loading": "Nalaganje", "loading_search_results_failed": "Nalaganje rezultatov iskanja ni uspelo", + "local": "Lokalno", "local_asset_cast_failed": "Sredstva, ki niso naložena na strežnik, ni mogoče predvajati", + "local_assets": "Lokalna sredstva", "local_network": "Lokalno omrežje", "local_network_sheet_info": "Aplikacija se bo povezala s strežnikom prek tega URL-ja, ko bo uporabljala navedeno omrežje Wi-Fi", "location_permission": "Dovoljenje za lokacijo", @@ -1193,8 +1248,7 @@ "manage_your_devices": "Upravljajte svoje prijavljene naprave", "manage_your_oauth_connection": "Upravljajte svojo OAuth povezavo", "map": "Zemljevid", - "map_assets_in_bound": "{count} slika", - "map_assets_in_bounds": "{count} slik", + "map_assets_in_bounds": "{count, plural, one {# slika} other {# slik}}", "map_cannot_get_user_location": "Lokacije uporabnika ni mogoče dobiti", "map_location_dialog_yes": "Da", "map_location_picker_page_use_location": "Uporabi to lokacijo", @@ -1246,6 +1300,7 @@ "more": "Več", "move": "Premakni", "move_off_locked_folder": "Premakni iz zaklenjene mape", + "move_to_lock_folder_action_prompt": "V zaklenjeno mapo je bilo dodanih {count}", "move_to_locked_folder": "Premakni v zaklenjeno mapo", "move_to_locked_folder_confirmation": "Te fotografije in videoposnetki bodo odstranjeni iz vseh albumov in si jih bo mogoče ogledati le v zaklenjeni mapi", "moved_to_archive": "Premaknjeno {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}} v arhiv", @@ -1292,6 +1347,7 @@ "no_results": "Brez rezultatov", "no_results_description": "Poskusite s sinonimom ali bolj splošno ključno besedo", "no_shared_albums_message": "Ustvarite album za skupno rabo fotografij in videoposnetkov z osebami v vašem omrežju", + "no_uploads_in_progress": "Ni nalaganj v teku", "not_in_any_album": "Ni v nobenem albumu", "not_selected": "Ni izbrano", "note_apply_storage_label_to_previously_uploaded assets": "Opomba: Če želite oznako za shranjevanje uporabiti za predhodno naložena sredstva, zaženite", @@ -1329,6 +1385,7 @@ "original": "izvirnik", "other": "drugo", "other_devices": "Druge naprave", + "other_entities": "Drugi subjekti", "other_variables": "Druge spremenljivke", "owned": "V lasti", "owner": "Lastnik", @@ -1360,7 +1417,7 @@ "pause": "Premor", "pause_memories": "Zaustavi spomine", "paused": "Zaustavljeno", - "pending": "V teku", + "pending": "Čakanje", "people": "Osebe", "people_edits_count": "{count, plural, one {Urejena # oseba} two {Urejeni # osebi} few {Urejene # osebe} other {Urejenih # oseb}}", "people_feature_description": "Brskanje po fotografijah in videoposnetkih, razvrščenih po osebah", @@ -1460,6 +1517,7 @@ "purchase_server_description_2": "Status podpornika", "purchase_server_title": "Strežnik", "purchase_settings_server_activated": "Ključ izdelka strežnika upravlja skrbnik", + "queue_status": "Čakalna vrsta {count}/{total}", "rating": "Ocena z zvezdicami", "rating_clear": "Počisti oceno", "rating_count": "{count, plural, one {# zvezdica} two {# zvezdici} few {# zvezdice} other {# zvezdic}}", @@ -1488,6 +1546,8 @@ "refreshing_faces": "Osveževanje obrazev", "refreshing_metadata": "Osveževanje metapodatkov", "regenerating_thumbnails": "Obnavljanje sličic", + "remote": "Oddaljeno", + "remote_assets": "Oddaljena sredstva", "remove": "Odstrani", "remove_assets_album_confirmation": "Ali ste prepričani, da želite odstraniti {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}} iz albuma?", "remove_assets_shared_link_confirmation": "Ali ste prepričani, da želite odstraniti {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}} iz te skupne povezave?", @@ -1495,7 +1555,9 @@ "remove_custom_date_range": "Odstrani časovno obdobje po meri", "remove_deleted_assets": "Odstrani izbrisana sredstva", "remove_from_album": "Odstrani iz albuma", + "remove_from_album_action_prompt": "{count} odstranjenih iz albuma", "remove_from_favorites": "Odstrani iz priljubljenih", + "remove_from_lock_folder_action_prompt": "iz zaklenjene mape je odstranjenih {count}", "remove_from_locked_folder": "Odstrani iz zaklenjene mape", "remove_from_locked_folder_confirmation": "Ali ste prepričani, da želite premakniti te fotografije in videoposnetke iz zaklenjene mape? Vidni bodo v vaši knjižnici.", "remove_from_shared_link": "Odstrani iz skupne povezave", @@ -1523,19 +1585,25 @@ "reset_password": "Ponastavi geslo", "reset_people_visibility": "Ponastavi vidnost ljudi", "reset_pin_code": "Ponastavi PIN kodo", + "reset_sqlite": "Ponastavi bazo podatkov SQLite", + "reset_sqlite_confirmation": "Ali ste prepričani, da želite ponastaviti bazo podatkov SQLite? Za ponovno sinhronizacijo podatkov se boste morali odjaviti in znova prijaviti", + "reset_sqlite_success": "Uspešno ponastavljena baza podatkov SQLite", "reset_to_default": "Ponastavi na privzeto", "resolve_duplicates": "Razreši dvojnike", "resolved_all_duplicates": "Razrešeni vsi dvojniki", "restore": "Obnovi", "restore_all": "Obnovi vse", + "restore_trash_action_prompt": "{count} obnovljenih iz koša", "restore_user": "Obnovi uporabnika", "restored_asset": "Obnovljeno sredstvo", "resume": "Nadaljuj", "retry_upload": "Poskusite znova naložiti", "review_duplicates": "Pregled dvojnikov", + "review_large_files": "Pregled velikih datotek", "role": "Vloga", "role_editor": "Urejevalec", "role_viewer": "Gledalec", + "running": "V teku", "save": "Shrani", "save_to_gallery": "Shrani v galerijo", "saved_api_key": "Shranjen API ključ", @@ -1667,6 +1735,7 @@ "settings_saved": "Nastavitve shranjene", "setup_pin_code": "Nastavi PIN kodo", "share": "Deli", + "share_action_prompt": "Deljena sredstva {count}", "share_add_photos": "Dodaj fotografije", "share_assets_selected": "{count} izbrano", "share_dialog_preparing": "Priprava...", @@ -1688,6 +1757,7 @@ "shared_link_clipboard_copied_massage": "Kopirano v odložišče", "shared_link_clipboard_text": "Povezava: {link}\nGeslo: {password}", "shared_link_create_error": "Napaka pri ustvarjanju povezave skupne rabe", + "shared_link_custom_url_description": "Dostop do te deljene povezave z URL-jem po meri", "shared_link_edit_description_hint": "Vnesi opis skupne rabe", "shared_link_edit_expire_after_option_day": "1 dan", "shared_link_edit_expire_after_option_days": "{count} dni", @@ -1713,6 +1783,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Upravljanje povezav v skupni rabi", "shared_link_options": "Možnosti skupne povezave", + "shared_link_password_description": "Za dostop do te deljene povezave je potrebno geslo", "shared_links": "Povezave v skupni rabi", "shared_links_description": "Deli fotografije in videoposnetke s povezavo", "shared_photos_and_videos_count": "{assetCount, plural, other {# deljenih fotografij & videoposnetkov.}}", @@ -1768,6 +1839,7 @@ "sort_title": "Naslov", "source": "Vir", "stack": "Sklad", + "stack_action_prompt": "{count} naloženih", "stack_duplicates": "Nabor dvojnikov", "stack_select_one_photo": "Izberite eno glavno fotografijo za nabor", "stack_selected_photos": "Nabor izbranih fotografij", @@ -1787,6 +1859,7 @@ "storage_quota": "Kvota shranjevanja", "storage_usage": "uporabljeno {used} od {available}", "submit": "Predloži", + "success": "Uspeh", "suggestions": "Predlogi", "sunrise_on_the_beach": "Sončni vzhod na plaži", "support": "Podpora", @@ -1796,6 +1869,8 @@ "sync": "Sinhronizacija", "sync_albums": "Sinhronizacija albumov", "sync_albums_manual_subtitle": "Sinhronizirajte vse naložene videoposnetke in fotografije v izbrane varnostne albume", + "sync_local": "Sinhroniziraj lokalno", + "sync_remote": "Sinhroniziraj oddaljeno", "sync_upload_album_setting_subtitle": "Ustvarite in naložite svoje fotografije in videoposnetke v izbrane albume na Immich", "tag": "Oznaka", "tag_assets": "Označi sredstva", @@ -1806,6 +1881,7 @@ "tag_updated": "Posodobljena oznaka: {tag}", "tagged_assets": "Označeno {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}}", "tags": "Oznake", + "tap_to_run_job": "Dotaknite se za zagon opravila", "template": "Predloga", "theme": "Tema", "theme_selection": "Izbira teme", @@ -1838,6 +1914,7 @@ "total": "Skupno", "total_usage": "Skupna poraba", "trash": "Smetnjak", + "trash_action_prompt": "premaknjeno v smetnjak {count}", "trash_all": "Vse v smetnjak", "trash_count": "Smetnjak {count, number}", "trash_delete_asset": "V smetnjak/izbriši sredstvo", @@ -1855,9 +1932,11 @@ "unable_to_change_pin_code": "PIN kode ni mogoče spremeniti", "unable_to_setup_pin_code": "PIN kode ni mogoče nastaviti", "unarchive": "Odstrani iz arhiva", + "unarchive_action_prompt": "{count} odstranjenih iz arhiva", "unarchived_count": "{count, plural, other {nearhiviranih #}}", "undo": "Razveljavi", "unfavorite": "Odznači priljubljeno", + "unfavorite_action_prompt": "{count} odstranjenih iz priljubljenih", "unhide_person": "Prikaži osebo", "unknown": "Neznano", "unknown_country": "Neznana država", @@ -1875,15 +1954,20 @@ "unselect_all_duplicates": "Odznači vse dvojnike", "unselect_all_in": "Prekliči izbor vseh v {group}", "unstack": "Razklad", + "unstack_action_prompt": "{count} razloženih", "unstacked_assets_count": "Razloži {count, plural, one {# sredstvo} two {# sredstvi} few {# sredstva} other {# sredstev}}", + "untagged": "Neoznačeno", "up_next": "Naslednja", "updated_at": "Posodobljeno", "updated_password": "Posodobljeno geslo", "upload": "Naloži", + "upload_action_prompt": "{count} v čakalni vrsti za nalaganje", "upload_concurrency": "Sočasnost nalaganja", + "upload_details": "Podrobnosti o nalaganju", "upload_dialog_info": "Ali želite varnostno kopirati izbrana sredstva na strežnik?", "upload_dialog_title": "Naloži sredstvo", "upload_errors": "Nalaganje je končano s/z {count, plural, one {# napako} two {# napakama} other {# napakami}}, osvežite stran, da vidite nova sredstva za nalaganje.", + "upload_finished": "Nalaganje končano", "upload_progress": "Preostalo {remaining, number} - Obdelano {processed, number}/{total, number}", "upload_skipped_duplicates": "Preskočeno {count, plural, one {# podvojeno sredstvo} two {# podvojeni sredstvi} few {# podvojena sredstva} other {# podvojenih sredstev}}", "upload_status_duplicates": "Dvojniki", @@ -1892,6 +1976,7 @@ "upload_success": "Nalaganje je uspelo, osvežite stran, da vidite nova sredstva za nalaganje.", "upload_to_immich": "Naloži v Immich ({count})", "uploading": "Nalagam", + "uploading_media": "Nalaganje medijev", "url": "URL", "usage": "Uporaba", "use_biometric": "Uporabite biometrične podatke", @@ -1912,6 +1997,7 @@ "user_usage_stats_description": "Oglejte si statistiko uporabe računa", "username": "Uporabniško ime", "users": "Uporabniki", + "users_added_to_album_count": "V album {count, plural, one {je bil dodan # uporabnik} two {sta bila dodana # uporabnika} few {so bili dodani # uporabniki} other {je bilo dodanih # uporanikov}}", "utilities": "Pripomočki", "validate": "Potrdi", "validate_endpoint_error": "Vnesite veljaven URL", @@ -1930,6 +2016,7 @@ "view_album": "Ogled albuma", "view_all": "Poglej vse", "view_all_users": "Ogled vseh uporabnikov", + "view_details": "Ogled podrobnosti", "view_in_timeline": "Ogled na časovnici", "view_link": "Odpri povezavo", "view_links": "Ogled povezav", diff --git a/i18n/sr_Cyrl.json b/i18n/sr_Cyrl.json index b518bb39af..f085adf532 100644 --- a/i18n/sr_Cyrl.json +++ b/i18n/sr_Cyrl.json @@ -458,7 +458,6 @@ "assets": "Записи", "assets_added_count": "Додато {count, plural, one {# датотека} other {# датотека}}", "assets_added_to_album_count": "Додато је {count, plural, one {# датотека} other {# датотека}} у албум", - "assets_added_to_name_count": "Додато {count, plural, one {# датотека} other {# датотеке}} у {hasName, select, true {{name}} other {нови албум}}", "assets_count": "{count, plural, one {# датотека} few {# датотеке} other {# датотека}}", "assets_deleted_permanently": "{count} елемената трајно обрисано", "assets_deleted_permanently_from_server": "{count} ресурс(а) трајно обрисан(а) са Immich сервера", @@ -480,6 +479,7 @@ "back_close_deselect": "Назад, затворите или опозовите избор", "background_location_permission": "Дозвола за локацију у позадини", "background_location_permission_content": "Да би се мењале мреже док се ради у позадини, Имих мора *увек* имати прецизан приступ локацији како би апликација могла да прочита име Wi-Fi мреже", + "backup": "Направи резервну копију", "backup_album_selection_page_albums_device": "Албума на уређају ({count})", "backup_album_selection_page_albums_tap": "Додирни да укључиш, додирни двапут да искључиш", "backup_album_selection_page_assets_scatter": "Записи се могу наћи у више различитих албума. Одатле албуми се могу укључити или искључити током процеса прављења позадинских копија.", @@ -1086,7 +1086,6 @@ "light": "Светло", "like_deleted": "Лајкуј избрисано", "link_motion_video": "Направи везу за видео запис", - "link_options": "Опције везе", "link_to_oauth": "Веза до OAuth-а", "linked_oauth_account": "Повезани OAuth налог", "list": "Излистај", @@ -1145,7 +1144,6 @@ "manage_your_devices": "Управљајте својим пријављеним уређајима", "manage_your_oauth_connection": "Управљајте својом OAuth везом", "map": "Мапа", - "map_assets_in_bound": "{count} фотографија", "map_assets_in_bounds": "{count} фотографија", "map_cannot_get_user_location": "Није могуц́е добити локацију корисника", "map_location_dialog_yes": "Да", diff --git a/i18n/sr_Latn.json b/i18n/sr_Latn.json index aa92e6da49..6eb88d7fd2 100644 --- a/i18n/sr_Latn.json +++ b/i18n/sr_Latn.json @@ -456,7 +456,6 @@ "assets": "Zapisi", "assets_added_count": "Dodato {count, plural, one {# datoteka} other {# datoteka}}", "assets_added_to_album_count": "Dodato je {count, plural, one {# datoteka} other {# datoteka}} u album", - "assets_added_to_name_count": "Dodato {count, plural, one {# datoteka} other {# datoteke}} u {hasName, select, true {{name}} other {novi album}}", "assets_count": "{count, plural, one {# datoteka} few {# datoteke} other {# datoteka}}", "assets_deleted_permanently": "{count} elemenata trajno obrisano", "assets_deleted_permanently_from_server": "{count} resurs(a) trajno obrisan(a) sa Immich servera", @@ -478,6 +477,7 @@ "back_close_deselect": "Nazad, zatvorite ili opozovite izbor", "background_location_permission": "Dozvola za lokaciju u pozadini", "background_location_permission_content": "Da bi se menjale mreže dok se radi u pozadini, Imih mora *uvek* imati precizan pristup lokaciji kako bi aplikacija mogla da pročita ime Wi-Fi mreže", + "backup": "Napravi rezervnu kopiju", "backup_album_selection_page_albums_device": "Albuma na uređaju ({count})", "backup_album_selection_page_albums_tap": "Dodirni da uključiš, dodirni dvaput da isključiš", "backup_album_selection_page_assets_scatter": "Zapisi se mogu naći u više različitih albuma. Odatle albumi se mogu uključiti ili isključiti tokom procesa pravljenja pozadinskih kopija.", @@ -1071,7 +1071,6 @@ "light": "Svetlo", "like_deleted": "Lajkuj izbrisano", "link_motion_video": "Napravi vezu za video zapis", - "link_options": "Opcije veze", "link_to_oauth": "Veza do OAuth-a", "linked_oauth_account": "Povezani OAuth nalog", "list": "Izlistaj", @@ -1128,7 +1127,6 @@ "manage_your_devices": "Upravljajte svojim prijavljenim uređajima", "manage_your_oauth_connection": "Upravljajte svojom OAuth vezom", "map": "Mapa", - "map_assets_in_bound": "{count} fotografija", "map_assets_in_bounds": "{count} fotografija", "map_cannot_get_user_location": "Nije moguće dobiti lokaciju korisnika", "map_location_dialog_yes": "Da", diff --git a/i18n/sv.json b/i18n/sv.json index a4c08f8c22..e4842084f8 100644 --- a/i18n/sv.json +++ b/i18n/sv.json @@ -22,6 +22,7 @@ "add_partner": "Lägg till partner", "add_path": "Lägg till sökväg", "add_photos": "Lägg till foton", + "add_tag": "Lägg till tagg", "add_to": "Lägg till i…", "add_to_album": "Lägg till i album", "add_to_album_bottom_sheet_added": "Tillagd till {album}", @@ -33,6 +34,7 @@ "added_to_favorites_count": "{count, number} tillagda till favoriter", "admin": { "add_exclusion_pattern_description": "Lägg till exkluderande mönster. Matchning med jokertecken *, ** samt ? stödjs. För att ignorera alla filer i samtliga mappar som heter \"Raw\", använd \"**/Raw/**\". För att ignorera alla filer som slutar med \".tif\", använd \"**/*.tif\". För att ignorera en absolut sökväg, använd \"/sökväg/att/ignorera/**\".", + "admin_user": "Adminanvändare", "asset_offline_description": "Denna externa bibliotekstillgång finns inte längre på disken och har flyttats till papperskorgen. Om filen flyttades inom biblioteket, kontrollera din tidslinje för den nya motsvarande tillgången. För att återställa denna tillgång, se till att filsökvägen nedan kan nås av Immich och skanna biblioteket.", "authentication_settings": "Autentiseringsinställningar", "authentication_settings_description": "Hantera lösenord, OAuth, och andra autentiseringsinställningar", @@ -164,12 +166,16 @@ "metadata_settings_description": "Hantera metadata-inställningar", "migration_job": "Migrering", "migration_job_description": "Migrera miniatyrbilder för resurser och ansikten till den senaste mappstrukturen", + "nightly_tasks_cluster_faces_setting_description": "Kör ansiktsigenkänning på nyligen upptäckta ansikten", + "nightly_tasks_database_cleanup_setting": "Uppgifter för databassanering", + "nightly_tasks_database_cleanup_setting_description": "Rensa bort gammal, utgången data från databasen", + "nightly_tasks_generate_memories_setting": "Generera minnen", "no_paths_added": "Inga vägar tillagda", "no_pattern_added": "Inga mönster tillagda", "note_apply_storage_label_previous_assets": "Obs: Om du vill använda lagringsetiketten på tidigare uppladdade tillgångar kör du", "note_cannot_be_changed_later": "OBS: Detta kan inte ändras i efterhand!", "notification_email_from_address": "Från adress", - "notification_email_from_address_description": "Avsändarens epost, t.ex.: \"Immich Fotoserver \"", + "notification_email_from_address_description": "Avsändarens e-post, till exempel: \"Immich Fotoserver \". Säkerställ att du använder en adress som du har tillåtelse att skicka e-post från.", "notification_email_host_description": "Värd för epostservern (t.ex. smtp.immich.app)", "notification_email_ignore_certificate_errors": "Ignorera certifikatfel", "notification_email_ignore_certificate_errors_description": "Ignorera valideringsfel för TLS-certifikat (rekommenderas ej)", @@ -194,6 +200,8 @@ "oauth_mobile_redirect_uri": "Telefonomdirigernings-URI", "oauth_mobile_redirect_uri_override": "Telefonomdirigerings-URI överrskridning", "oauth_mobile_redirect_uri_override_description": "Aktivera om OAuth-leverantören inte tillåter mobila URI:er, så som ''{callback}''", + "oauth_role_claim": "Rollanspråk", + "oauth_role_claim_description": "Bevilja administratörsåtkomst automatiskt baserat på förekomsten av detta påstående. Påståendet kan innehålla antingen 'user' eller 'admin'.", "oauth_settings": "OAuth", "oauth_settings_description": "Hantera OAuth-logininställningar", "oauth_settings_more_details": "För ytterligare detaljer om denna funktion, se dokumentationen.", @@ -202,7 +210,7 @@ "oauth_storage_quota_claim": "Användaranknuten lagringskvot", "oauth_storage_quota_claim_description": "Sätter automatiskt angiven användares lagringskvot.", "oauth_storage_quota_default": "Standardlagringskvot (GiB)", - "oauth_storage_quota_default_description": "Kvot i GiB som används när ingen fordran angetts (Ange 0 för obegränsad kvot).", + "oauth_storage_quota_default_description": "Kvot i GiB som används när ingen fordran angetts.", "oauth_timeout": "Begäran tog för lång tid", "oauth_timeout_description": "Timeout för förfrågningar i millisekunder", "password_enable_description": "Logga in med epost och lösenord", @@ -242,6 +250,7 @@ "storage_template_migration_info": "Lagringsmallen kommer konvertera alla filändelser till gemena bokstäver. Ändringar gäller endast för nya resurser, för att retoaktivt tillämpa mallen på befintliga resurser kör {job}.", "storage_template_migration_job": "Lagringsmall migreringsjobb", "storage_template_more_details": "För mer information om den här funktionen se Lagringsmall och dess konsekvenser", + "storage_template_onboarding_description_v2": "Denna funktion kommer när den är aktiverad att auto-organisera filer baserat på en användardefinierad mall. För mer information se dokumentationen.", "storage_template_path_length": "Uppskattad längdbegränsning på sökväg: {length, number}/{limit, number}", "storage_template_settings": "Lagringsmall", "storage_template_settings_description": "Hantera mappstruktur och filnamn för uppladdade resurser", @@ -401,6 +410,9 @@ "album_with_link_access": "Låt alla med länken se foton och personer i det här albumet.", "albums": "Album", "albums_count": "{count, plural, one {{count, number} Album} other {{count, number} Album}}", + "albums_default_sort_order": "Standard sorteringsordning för album", + "albums_default_sort_order_description": "Standard sorteringsordning för mediefiler vid skapande av nytt album.", + "albums_feature_description": "Samlingar av mediefiler som kan delas med andra användare.", "all": "Allt", "all_albums": "Alla album", "all_people": "Alla personer", @@ -421,6 +433,7 @@ "app_settings": "Appinställningar", "appears_in": "Visas i", "archive": "Arkiv", + "archive_action_prompt": "{count} adderade till Arkiv", "archive_or_unarchive_photo": "Arkivera eller oarkivera fotot", "archive_page_no_archived_assets": "Inga arkiverade objekt hittade", "archive_page_title": "Arkiv ({count})", @@ -458,7 +471,6 @@ "assets": "Objekt", "assets_added_count": "La till {count, plural, one {# asset} other {# assets}}", "assets_added_to_album_count": "Lade till {count, plural, one {# asset} other {# assets}} i albumet", - "assets_added_to_name_count": "Lade till {count, plural, one {# objekt} other {# objekt}} till {hasName, select, true {{name}} other {nytt album}}", "assets_count": "{count, plural, one {# objekt} other {# objekt}}", "assets_deleted_permanently": "{count} objekt har tagits bort permanent", "assets_deleted_permanently_from_server": "{count} objekt har tagits bort permanent från Immich-servern", @@ -480,6 +492,7 @@ "back_close_deselect": "Tillbaka, stäng eller avmarkera", "background_location_permission": "Tillåtelse för bakgrundsplats", "background_location_permission_content": "För att kunna byta nätverk när appen körs i bakgrunden måste Immich *alltid* ha åtkomst till exakt plats så att appen kan läsa av Wi-Fi-nätverkets namn", + "backup": "Säkerhetskopiera", "backup_album_selection_page_albums_device": "Album på enhet ({count})", "backup_album_selection_page_albums_tap": "Tryck en gång för att inkludera, tryck två gånger för att exkludera", "backup_album_selection_page_assets_scatter": "Objekt kan vara utspridda över flera album. Därför kan album inkluderas eller exkluderas under säkerhetskopieringsprocessen.", @@ -639,6 +652,7 @@ "confirm_password": "Bekräfta lösenord", "confirm_tag_face": "Vill du tagga det här ansiktet som {name}?", "confirm_tag_face_unnamed": "Vill du tagga detta ansikte?", + "connected_device": "Ansluten enhet", "connected_to": "Ansluten till", "contain": "Anpassa", "context": "Sammanhang", @@ -691,7 +705,6 @@ "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Mörk", - "darkTheme": "Växla till mörkt tema", "date_after": "Datum efter", "date_and_time": "Datum och Tid", "date_before": "Datum före", @@ -707,6 +720,7 @@ "default_locale": "Standardplats", "default_locale_description": "Formatera datum och siffror baserat på din webbläsares språkversion", "delete": "Radera", + "delete_action_prompt": "{count, plural, one {# permanent raderad} other {# permanent raderade}}", "delete_album": "Ta bort album", "delete_api_key_prompt": "Är du säker på att du vill ta bort denna API-nyckel?", "delete_dialog_alert": "Dessa objekt kommer att raderas permanent från Immich och din enhet", @@ -739,6 +753,7 @@ "disallow_edits": "Tillåt inte redigeringar", "discord": "Discord", "discover": "Upptäck", + "discovered_devices": "Funna enheter", "dismiss_all_errors": "Avvisa alla fel", "dismiss_error": "Avvisa fel", "display_options": "Visningsalternativ", @@ -1083,6 +1098,7 @@ "invite_to_album": "Bjuder in till album", "ios_debug_info_last_sync_at": "Senaste synkning {dateTime}", "ios_debug_info_no_processes_queued": "Inga bakgrundsprocesser köade", + "ios_debug_info_processes_queued": "{count, plural, one {{count} bakgrundsprocess köad} other {{count} bakgrundsprocesser köade}}", "items_count": "{count, plural, one {# objekt} other {# objekt}}", "jobs": "Jobb", "keep": "Behåll", @@ -1091,6 +1107,8 @@ "kept_this_deleted_others": "Behåll denna tillgång och borttagna {count, plural, one {# asset} other {# assets}}", "keyboard_shortcuts": "Kortkommandon", "language": "Språk", + "language_no_results_title": "Inga språk funna", + "language_search_hint": "Sök språk…", "language_setting_description": "Välj önskat språk", "last_seen": "Senast sedd", "latest_version": "Senaste versionen", @@ -1110,7 +1128,6 @@ "light": "Ljus", "like_deleted": "Gilla borttagen", "link_motion_video": "Länka rörlig video", - "link_options": "Alternativ för länk", "link_to_oauth": "Länk till OAuth", "linked_oauth_account": "Länkat OAuth konto", "list": "Lista", @@ -1129,6 +1146,7 @@ "locked_folder": "Låst Mapp", "log_out": "Logga ut", "log_out_all_devices": "Logga ut alla enheter", + "logged_in_as": "Inloggad som {user}", "logged_out_all_devices": "Loggat ut från alla enheter", "logged_out_device": "Loggat ut enheten", "login": "Logga in", @@ -1171,7 +1189,6 @@ "manage_your_devices": "Hantera dina inloggade enheter", "manage_your_oauth_connection": "Hantera din OAuth-anslutning", "map": "Karta", - "map_assets_in_bound": "{count} foto", "map_assets_in_bounds": "{count} foton", "map_cannot_get_user_location": "Kan inte hämta användarens plats", "map_location_dialog_yes": "Ja", @@ -1224,6 +1241,7 @@ "more": "Mer", "move": "Flytta", "move_off_locked_folder": "Flytta från låst mapp", + "move_to_lock_folder_action_prompt": "{count} adderades till låst mapp", "move_to_locked_folder": "Flytta till låst mapp", "move_to_locked_folder_confirmation": "Dessa foton och videor kommer tas bort från alla album och går endast se i låsta mappen", "moved_to_archive": "Flyttade {count, plural, one {# resurs} other {# assets}} till arkivet", @@ -1825,6 +1843,7 @@ "unable_to_setup_pin_code": "Kunde inte konfigurera pinkod", "unarchive": "Ångra arkivering", "unarchived_count": "{count, plural, one {# borttagen från arkiv} other {# borttagna från arkiv}}", + "undo": "Ångra", "unfavorite": "Avfavorisera", "unhide_person": "Visa person", "unknown": "Okänd", diff --git a/i18n/ta.json b/i18n/ta.json index ce0768982d..6e1ff10484 100644 --- a/i18n/ta.json +++ b/i18n/ta.json @@ -392,7 +392,6 @@ "assets": "சொத்துக்கள்", "assets_added_count": "சேர்க்கப்பட்டது {எண்ணிக்கை, பன்மை, ஒன்று {# சொத்து} மற்ற {# சொத்துக்கள்}}", "assets_added_to_album_count": "ஆல்பத்தில் {எண்ணிக்கை, பன்மை, ஒன்று {# சொத்து} மற்ற {# சொத்துக்கள்}}", - "assets_added_to_name_count": "சேர்க்கப்பட்டது {எண்ணிக்கை, பன்மை, ஒன்று {# சொத்து} மற்ற {# சொத்துக்கள்}} {hasname, தேர்ந்தெடுக்கவும், உண்மை { {name} } பிற {new album}}", "assets_count": "{எண்ணிக்கை, பன்மை, ஒன்று {# சொத்து} மற்ற {# சொத்துக்கள்}}", "assets_moved_to_trash_count": "நகர்த்தப்பட்டது {எண்ணிக்கை, பன்மை, ஒன்று {# சொத்து} மற்ற {# சொத்துக்கள்}}}", "assets_permanently_deleted_count": "நிரந்தரமாக நீக்கப்பட்டது {எண்ணிக்கை, பன்மை, ஒன்று {# சொத்து} பிற {# சொத்துக்கள்}}", @@ -784,7 +783,6 @@ "light": "ஒளி", "like_deleted": "நீக்கப்பட்டதைப் போல", "link_motion_video": "இணைப்பு இயக்க வீடியோ", - "link_options": "இணைப்பு விருப்பங்கள்", "link_to_oauth": "OAUTH உடன் இணைப்பு", "linked_oauth_account": "இணைக்கப்பட்ட OAUTH கணக்கு", "list": "பட்டியல்", diff --git a/i18n/te.json b/i18n/te.json index 8bd57bc3bc..0d5242891e 100644 --- a/i18n/te.json +++ b/i18n/te.json @@ -23,6 +23,8 @@ "add_photos": "ఫోటోలను జోడించండి", "add_to": "జోడించండి…", "add_to_album": "ఆల్బమ్‌కు జోడించండి", + "add_to_album_bottom_sheet_added": "ఆల్బమ్కు జోడించబడింది", + "add_to_album_bottom_sheet_already_exists": "ఆల్బమ్‌లో ఇప్పటికే జోడించబడింది", "add_to_shared_album": "భాగస్వామ్య ఆల్బమ్‌కు జోడించండి", "add_url": "URLని జోడించండి", "added_to_archive": "ఆర్కైవ్‌కి జోడించబడింది", @@ -30,17 +32,18 @@ "added_to_favorites_count": "ఇష్టమైన వాటికి {count, number} జోడించబడింది", "admin": { "add_exclusion_pattern_description": "మినహాయింపు నమూనాలను జోడించండి. *, ** మరియు ?ని ఉపయోగించి గ్లోబింగ్‌కు మద్దతు ఉంది. \"Raw\" అనే పేరు గల ఏదైనా డైరెక్టరీలోని అన్ని ఫైల్‌లను విస్మరించడానికి, \"**/Raw/**\"ని ఉపయోగించండి. \".tif\"తో ముగిసే అన్ని ఫైల్‌లను విస్మరించడానికి, \"**/*.tif\"ని ఉపయోగించండి. సంపూర్ణ మార్గాన్ని విస్మరించడానికి, \"/path/to/ignore/**\"ని ఉపయోగించండి.", + "admin_user": "నిర్వాహకుడు", "asset_offline_description": "ఈ బాహ్య లైబ్రరీ ఫైల్ ఇకపై డిస్క్‌లో కనుగొనబడలేదు మరియు ట్రాష్‌కు తరలించబడింది. ఫైల్ లైబ్రరీలోకి తరలించబడితే, కొత్త సంబంధిత ఫైల్ కోసం మీ టైమ్‌లైన్‌ను తనిఖీ చేయండి. ఈ ఫైల్ని పునరుద్ధరించడానికి, దయచేసి దిగువన ఉన్న ఫైల్ పాత్‌ను Immich యాక్సెస్ చేయగలదని నిర్ధారించుకోండి మరియు లైబ్రరీని స్కాన్ చేయండి.", "authentication_settings": "ప్రమాణీకరణ సెట్టింగ్‌లు", "authentication_settings_description": "పాస్‌వర్డ్, OAuth మరియు ఇతర ప్రమాణీకరణ సెట్టింగ్‌లను నిర్వహించండి", "authentication_settings_disable_all": "మీరు ఖచ్చితంగా అన్ని లాగిన్ పద్ధతులను నిలిపివేయాలనుకుంటున్నారా? లాగిన్ పూర్తిగా నిలిపివేయబడుతుంది.", "authentication_settings_reenable": "మళ్లీ ప్రారంబించటానికి, Server Commandని ఉపయోగించండి.", "background_task_job": "నేపథ్య పనులు", - "backup_database": "బ్యాకప్ డేటాబేస్", - "backup_database_enable_description": "డేటాబేస్ బ్యాకప్‌లను ప్రారంభించండి", - "backup_keep_last_amount": "ఉంచుకోవాల్సిన మునుపటి బ్యాకప్‌ల మొత్తం", - "backup_settings": "బ్యాకప్ సెట్టింగ్‌లు", - "backup_settings_description": "డేటాబేస్ బ్యాకప్ సెట్టింగ్‌లను నిర్వహించండి", + "backup_database": "డేటాబేస్ ను సృష్టించు", + "backup_database_enable_description": "డేటాబేస్ పడవెయ్యడాన్నీ ప్రారంభించండి", + "backup_keep_last_amount": "ఉంచుకోవాల్సిన మునుపటి పడవెయ్యడాల్లా మొత్తం", + "backup_settings": "డేటాబేస్ పడవెసే సెట్టింగ్‌లు", + "backup_settings_description": "డేటాబేస్ పడవెసే సెట్టింగ్‌లను నిర్వహించండి", "cleared_jobs": "దీని కోసం ఉద్యోగాలు క్లియర్ చేయబడ్డాయి: {job}", "config_set_by_file": "కాన్ఫిగరేషన్ ప్రస్తుతం కాన్ఫిగరేషన్ ఫైల్ ద్వారా సెట్ చేయబడింది", "confirm_delete_library": "మీరు ఖచ్చితంగా {library} లైబ్రరీని తొలగించాలనుకుంటున్నారా?", @@ -48,6 +51,7 @@ "confirm_email_below": "నిర్ధారించడానికి, క్రింద \"{email}\" టైప్ చేయండి", "confirm_reprocess_all_faces": "మీరు ఖచ్చితంగా అన్ని ముఖాలను రీప్రాసెస్ చేయాలనుకుంటున్నారా? ఇది పేరున్న వ్యక్తులను కూడా క్లియర్ చేస్తుంది.", "confirm_user_password_reset": "మీరు ఖచ్చితంగా {user} పాస్‌వర్డ్‌ని రీసెట్ చేయాలనుకుంటున్నారా?", + "confirm_user_pin_code_reset": "మీరు ఖచ్చితంగా {user} యొక్క పిన్ కోడ్ నీ రీసెట్ చేద్దామనిఅనుకుంటున్నారా?", "create_job": "పనిని సృష్టించండి", "cron_expression": "క్రాన్ వ్యక్తీకరణ", "cron_expression_description": "క్రాన్ ఫార్మాట్ ఉపయోగించి స్కానింగ్ విరామాన్ని సెట్ చేయండి. మరిన్ని వివరాల కోసం దయచేసి ఉదా. క్రోంటాబ్ గురు చూడండి", @@ -402,7 +406,6 @@ "assets": "ఆస్తులు", "assets_added_count": "జోడించబడినవి {count, plural, one {# ఆస్తి} other {# ఆస్తులు}}", "assets_added_to_album_count": "{count, plural, one {# ఆస్తి} other {# ఆస్తులు}} ఆల్బమ్‌కి జోడించబడినవి", - "assets_added_to_name_count": "{count, plural, one {# ఆస్తి} other {# ఆస్తులు}} {hasName, select, true {{name}} other {కొత్త ఆల్బమ్}}కి జోడించబడినవి", "assets_count": "{count, plural, one {# ఆస్తి} other {# ఆస్తులు}}", "assets_moved_to_trash_count": "{count, plural, one {# ఆస్తి} other {# ఆస్తులు}} చెత్తబుట్టలోకి తరలించారు", "assets_permanently_deleted_count": "{count, plural, one {# ఆస్తి} other {# ఆస్తులు}} శాశ్వతంగా తొలగించబడినవి", @@ -807,7 +810,6 @@ "light": "వెలుతురు", "like_deleted": "ఇస్టం తొలగించబడింది", "link_motion_video": "మోషన్ వీడియో లింక్ చేయండి", - "link_options": "లింక్ ఎంపికలు", "link_to_oauth": "OAuth కి లింక్ చేయండి", "linked_oauth_account": "లింక్ చేయబడిన OAuth ఖాతా", "list": "జాబితా", diff --git a/i18n/th.json b/i18n/th.json index 13d9514d63..8da7819ac5 100644 --- a/i18n/th.json +++ b/i18n/th.json @@ -14,14 +14,15 @@ "add_a_location": "เพิ่มตำแหน่ง", "add_a_name": "เพิ่มชื่อ", "add_a_title": "เพิ่มหัวข้อ", - "add_endpoint": "เพิ่มอุปกรณ์ปลายทาง", + "add_endpoint": "เพิ่มปลายทาง", "add_exclusion_pattern": "เพิ่มข้อยกเว้น", "add_import_path": "เพิ่มเส้นทางนำเข้า", "add_location": "เพิ่มตำแหน่ง", "add_more_users": "เพิ่มผู้ใช้งาน", - "add_partner": "เพิ่มพันธมิตร", + "add_partner": "เพิ่มคู่หู", "add_path": "เพิ่มพาทที่ตั้ง", "add_photos": "เพิ่มรูปภาพ", + "add_tag": "เพิ่มแท็ก", "add_to": "เพิ่มไปยัง …", "add_to_album": "เพิ่มไปอัลบั้ม", "add_to_album_bottom_sheet_added": "เพิ่มไปยัง {album}", @@ -33,7 +34,8 @@ "added_to_favorites_count": "{count, number} รูปถูกเพิ่มเข้ารายการโปรด", "admin": { "add_exclusion_pattern_description": "เพิ่มรูปแบบข้อยกเว้น รองรับการใช้ *, ** และ ? หากต้องการละเว้นไฟล์ทั้งหมดในไดเร็กทอรีที่ชื่อว่า \"Raw\" ให้ใช้ \"**/Raw/**\" ถ้าต้องการละเว้นไฟล์ทั้งหมดที่ลงท้ายด้วย \".tif\" ให้ใช้ \"**/*.tif\" ถ้าต้องการละเว้นพาธที่เริ่มจากไดเรกทอรีบนสุดให้ใช้ \"/พาธ/ที่ต้องการ/ละเว้น/**\"", - "asset_offline_description": "ไฟล์ Asset ของไลบรารีภายนอกนี้ไม่พบในดิสก์แล้ว และถูกย้ายไปที่ถังขยะ หากไฟล์ถูกย้ายภายในไลบรารี โปรดตรวจสอบไทม์ไลน์ของคุณเพื่อหาแอสเซ็ตที่เกี่ยวข้องใหม่ หากต้องการกู้คืน Asset นี้ โปรดตรวจสอบให้แน่ใจว่า Immich สามารถเข้าถึงเส้นทางไฟล์ด้านล่างได้ และทำการสแกนไลบรารีอีกครั้ง", + "admin_user": "ผู้ดูแล", + "asset_offline_description": "ไม่พบไฟล์สื่อของไลบรารีภายนอกนี้ในดิสก์ และถูกย้ายไปที่ถังขยะแล้ว หากไฟล์ถูกย้ายภายในไลบรารี โปรดตรวจสอบไทม์ไลน์ของคุณเพื่อหาสื่อที่เกี่ยวข้องใหม่ หากต้องการกู้คืนสื่อนี้ โปรดตรวจสอบว่า Immich สามารถเข้าถึงไฟล์ด้านล่างได้ และทำการสแกนไลบรารีอีกครั้ง", "authentication_settings": "การตั้งค่าการเข้าถึง", "authentication_settings_description": "จัดการรหัสผ่าน, OAuth, และตั้งค่าการเข้าถึงอื่นๆ", "authentication_settings_disable_all": "คุณแน่ใจว่าต้องการปิดวิธีการล็อกอินทั้งหมดหรือไม่? ล็อกอินจะถูกปิดทั้งหมด", @@ -43,7 +45,7 @@ "backup_database_enable_description": "เปิดใช้งานสำรองฐานข้อมูล", "backup_keep_last_amount": "จำนวนข้อมูลสำรองก่อนหน้าที่ต้องเก็บไว้", "backup_settings": "ตั้งค่าการสำรองข้อมูล", - "backup_settings_description": "จัดการการตั้งค่าการสำรองฐานข้อมูล งานสำรองนี้จะไม่ถูกตรวจสอบและคุณจะไม่ได้รับการแจ้งเมื่อมันสำรองไม่สำเร็จ", + "backup_settings_description": "จัดการการตั้งค่าการสำรองฐานข้อมูล", "cleared_jobs": "เคลียร์งานสำหรับ: {job}", "config_set_by_file": "การตั้งค่าคอนฟิกกำลังถูกกำหนดโดยไฟล์คอนฟิก", "confirm_delete_library": "คุณแน่ใจว่าอยากลบคลังภาพ {library} หรือไม่?", @@ -169,7 +171,7 @@ "note_apply_storage_label_previous_assets": "หากต้องการใช้ Storage Label กับไฟล์ที่อัปโหลดก่อนหน้านี้ ให้รันคำสั่งนี้", "note_cannot_be_changed_later": "หมายเหตุ: ไม่สามารถเปลี่ยนภายหลังได้!", "notification_email_from_address": "จากที่อยู่", - "notification_email_from_address_description": "อีเมลผู้ส่ง อย่างเช่น \"Immich Photo Server \"", + "notification_email_from_address_description": "ที่อยู่อีเมลผู้ส่ง ตัวอย่าง \"Immich Photo Server \" (กรุณายีนยันว่าสามารถส่งเมลจากที่อยู่นี้ได้)", "notification_email_host_description": "ที่อยู่เซิร์ฟเวอร์อีเมล (เช่น smtp.immich.app)", "notification_email_ignore_certificate_errors": "ไม่สนใจข้อผิดพลาดเกี่ยวกับใบรับรอง", "notification_email_ignore_certificate_errors_description": "ไม่สนใจการยืนยันใบรับรอง TLS ผิดพลาด (ไม่แนะนำ)", @@ -193,7 +195,7 @@ "oauth_enable_description": "ล็อกอินผ่าน OAuth", "oauth_mobile_redirect_uri": "URI เปลี่ยนเส้นทางบนโทรศัพท์", "oauth_mobile_redirect_uri_override": "แทนที่ URI เปลี่ยนเส้นทางบนโทรศัพท์", - "oauth_mobile_redirect_uri_override_description": "เปิดเมื่อ 'app.immich:/' เป็น URI ที่เปลี่ยนเส้นทางไม่ถูกต้อง", + "oauth_mobile_redirect_uri_override_description": "เปิดเมื่อผู้ให้บริการ OAuth ไม่อนุญาต URI เช่น \"{callback}\"", "oauth_settings": "OAuth", "oauth_settings_description": "จัดการการตั้งค่าล็อกอินผ่าน OAuth", "oauth_settings_more_details": "สำหรับรายละเอียดเพิ่มเติม ให้อ้างถึงเอกสาร", @@ -202,7 +204,7 @@ "oauth_storage_quota_claim": "สิทธิ์ที่ใช้อ้างถึงโควต้าพื้นที่จัดเก็บ", "oauth_storage_quota_claim_description": "ตั้งโควต้าพื้นที่จัดเก็บของผู้ใช้งานตามสิทธิ์ที่ใช้อ้างถึงโดยอัตโนมัติ", "oauth_storage_quota_default": "โควต้าพื้นที่เก็บข้อมูลเริ่มต้น (GiB)", - "oauth_storage_quota_default_description": "โควต้าในหน่วย GiB ที่จะใช้เมื่อไม่มีการอ้างสิทธิ์ (ป้อน 0 สำหรับโควต้าไม่จำกัด)", + "oauth_storage_quota_default_description": "โควต้าในหน่วย GiB ที่จะใช้เมื่อไม่มีการอ้างสิทธิ์", "oauth_timeout": "หมดเวลาการร้องขอ", "oauth_timeout_description": "ระยะเวลาหมดเวลาสำหรับการร้องขอ (หน่วยเป็นมิลลิวินาที)", "password_enable_description": "ล็อกอินกับอีเมลและรหัสผ่าน", @@ -242,6 +244,7 @@ "storage_template_migration_info": "เทมเพลตของการจัดเก็บข้อมูลจะเปลี่ยนตัวอักษรเป็นตัวพิมพ์เล็กทั้งหมด การเปลี่ยนแปลงเทมเพลตจะมีผลกับแอสเซ็ตใหม่เท่านั้น หากต้องการนำเทมเพลตไปใช้กับ Asset ที่อัปโหลดก่อนหน้านี้ ให้รัน {job}.", "storage_template_migration_job": "เทมเพลตการ Migration ข้อมูล", "storage_template_more_details": "สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับฟีเจอร์นี้ โปรดดูที่ Storage Template และ ผลกระทบ", + "storage_template_onboarding_description_v2": "เมื่อเปิด ฟีเจอร์จะจัดเก็บข้อมูลตามเทมเพลตที่ผู้ใช้กำหนด อ่านเพิ่มเติม", "storage_template_path_length": "ขีดจำกัดของความยาวพาธโดยประมาณ: {length, number}/{limit, number}", "storage_template_settings": "เทมเพลตการจัดเก็บข้อมูล", "storage_template_settings_description": "จัดการโครงสร้างโฟลเดอร์และชื่อไฟล์ที่อัปโหลด", @@ -256,7 +259,7 @@ "template_email_update_album": "อัปเดตเทมเพลตอัลบั้ม", "template_email_welcome": "เทมเพลตสำหรับอีเมลต้อนรับ", "template_settings": "เทมเพลตการแจ้งเตือน", - "template_settings_description": "ปรับแต่งเทมเพลตแจ้งเตือน", + "template_settings_description": "ปรับแต่งเทมเพลตการแจ้งเตือน", "theme_custom_css_settings": "CSS กําหนดเอง", "theme_custom_css_settings_description": "Cascading Style Sheets ช่วยให้ปรับแต่งเค้าโครง Immich ได้", "theme_settings": "การตั้งค่าธีม", @@ -362,7 +365,7 @@ "advanced_settings_proxy_headers_subtitle": "กำหนด proxy headers ที่ Immich ควรส่งพร้อมกับแต่ละคำขอเครือข่าย", "advanced_settings_proxy_headers_title": "พ็อกซี่ เฮดเดอร์", "advanced_settings_self_signed_ssl_subtitle": "ข้ามการตรวจสอบใบรับรอง SSL จำเป็นสำหรับใบรับรองแบบ self-signed", - "advanced_settings_self_signed_ssl_title": "อนุญาตใบรับรอง SSL แบบ self-signed ", + "advanced_settings_self_signed_ssl_title": "อนุญาตใบรับรอง SSL แบบ self-signed", "advanced_settings_sync_remote_deletions_subtitle": "บหรือกู้คืนไฟล์บนอุปกรณ์นี้โดยอัตโนมัติเมื่อดำเนินการดังกล่าวผ่านเว็บ", "advanced_settings_sync_remote_deletions_title": "ซิงก์การลบจากระยะไกล [คุณสมบัติทดลอง]", "advanced_settings_tile_subtitle": "ตั้งค่าผู้ใช้งานขั้นสูง", @@ -401,6 +404,9 @@ "album_with_link_access": "อนุญาตให้ทุกคนที่มีลิงก์สามารถดูรูปภาพและผู้คนที่อยู่ในอัลบั้มนี้", "albums": "อัลบั้ม", "albums_count": "{count, plural, one {{count, number} อัลบั้ม} other {{count, number} อัลบั้ม}}", + "albums_default_sort_order": "การจัดเรียงอัลบั้มเริ่มต้น", + "albums_default_sort_order_description": "การจัดเรียงแอสเซ็ตเริ่มต้นเมื่อสร้างอัลบั้มใหม่", + "albums_feature_description": "กลุ่มของแอสเซ็ตที่สามารถส่งให้ผู้ใช้อื่นได้", "all": "ทั้งหมด", "all_albums": "อัลบั้มทั้งหมด", "all_people": "ทุกคน", @@ -413,8 +419,8 @@ "anti_clockwise": "ทวนเข็มนาฬิกา", "api_key": "API key", "api_key_description": "ค่านี้จะแสดงเพียงครั้งเดียว โปรดคัดลอกก่อนปิดหน้าต่าง", - "api_key_empty": "ชื่อคีย์ API ของคุณไม่ควรว่างเปล่า", - "api_keys": "API คีย์", + "api_key_empty": "ชื่อ API Key ของคุณไม่ควรว่างเปล่า", + "api_keys": "API Key", "app_bar_signout_dialog_content": "คุณแน่ใจว่าอยากออกจากระบบ", "app_bar_signout_dialog_ok": "ใช่", "app_bar_signout_dialog_title": "ออกจากระบบ", @@ -428,7 +434,7 @@ "archive_size_description": "ตั้งค่าขนาดสูงสุดสำหรับการดาวน์โหลด (GiB)", "archived": "เก็บถาวรแล้ว", "archived_count": "{count, plural, other {เก็บถาวร # รายการ}}", - "are_these_the_same_person": "เป็นคนเดียวกันหรือไม่?", + "are_these_the_same_person": "เป็นบุคคลเดียวกันหรือไม่?", "are_you_sure_to_do_this": "คุณแน่ใจว่าต้องการทำสิ่งนี้หรือไม่?", "asset_action_delete_err_read_only": "ไม่สามารถลบทรัพยากรแบบอ่านอย่างเดียวได้ กำลังข้าม", "asset_action_share_err_offline": "ไม่สามารถดึงข้อมูลทรัพยากรออฟไลน์ กำลังข้าม", @@ -448,24 +454,42 @@ "asset_list_settings_title": "ตารางรูปภาพ", "asset_offline": "สื่อออฟไลน์", "asset_offline_description": "ไม่พบทรัพยากรภายนอกนี้ในดิสก์อีกต่อไป โปรดติดต่อผู้ดูแลระบบ Immich ของคุณเพื่อขอความช่วยเหลือ", + "asset_restored_successfully": "กู้คืนสื่อสำเร็จ", "asset_skipped": "ข้ามแล้ว", "asset_skipped_in_trash": "ในถังขยะ", "asset_uploaded": "อัปโหลดแล้ว", "asset_uploading": "กำลังอัปโหลด…", + "asset_viewer_settings_subtitle": "ตั้งค่าการแสดงแกลเลอรี", "asset_viewer_settings_title": "ตัวดูทรัพยากร", "assets": "สื่อ", + "assets_added_count": "เพิ่ม {count, plural, one{# สื่อ} other {# สื่อ}} แล้ว", "assets_added_to_album_count": "เพิ่ม {count, plural, one {# asset} other {# assets}} ไปยังอัลบั้ม", - "assets_added_to_name_count": "เพิ่ม {count, plural, one {# asset} other {# assets}} ไปยัง {hasName, select, true {{name}} other {new album}}", + "assets_cannot_be_added_to_album_count": "ไม่สามารถเพิ่ม {count, plural, one {สื่อ} other {สื่อ}} ไปยังอัลบั้ม", + "assets_count": "{count, plural, one { สื่อ} other { สื่อ}}", + "assets_deleted_permanently": "{count} สื่อถูกลบอย่างถาวร", + "assets_deleted_permanently_from_server": "ลบ {count} สื่อออกจาก Immich อย่างถาวร", + "assets_downloaded_failed": "ดาวน์โหลด {count, plural, one {ไฟล์} other {ไฟล์}} ไม่สำเร็จ - {error}", + "assets_downloaded_successfully": "ดาวน์โหลด {count, plural, one {ไฟล์} other {ไฟล์}} สำเร็จ", "assets_moved_to_trash_count": "ย้าย {count, plural, one {# asset} other {# assets}} ไปยังถังขยะแล้ว", "assets_permanently_deleted_count": "ลบ {count, plural, one {# asset} other {# assets}} ทิ้งถาวร", "assets_removed_count": "{count, plural, one {# asset} other {# assets}} ถูกลบแล้ว", + "assets_removed_permanently_from_device": "นำ {count} สื่อออกจากอุปกรณ์อย่างถาวร", "assets_restore_confirmation": "คุณแน่ใจหรือไม่ว่าต้องการกู้คืนสื่อที่ทิ้งทั้งหมด? คุณไม่สามารถย้อนกลับการดำเนินการนี้ได้! โปรดทราบว่าสื่อออฟไลน์ใดๆ ไม่สามารถกู้คืนได้ด้วยวิธีนี้", "assets_restored_count": "{count, plural, one {# asset} other {# assets}} คืนค่า", + "assets_restored_successfully": "กู้คืน {count} สื่อสำเร็จ", + "assets_trashed": "ย้าย {count} สื่อไปยังถังขยะ", "assets_trashed_count": "{count, plural, one {# asset} other {# assets}} ถูกลบ", + "assets_trashed_from_server": "ย้าย {count} สื่อจาก Immich ไปยังถังขยะ", "assets_were_part_of_album_count": "{count, plural, one {Asset was} other {Assets were}} อยู่ในอัลบั้มอยู่แล้ว", "authorized_devices": "อุปกรณ์ที่ได้รับอนุญาต", + "automatic_endpoint_switching_subtitle": "เชื่อมต่อด้วย LAN ภายในวง Wi-Fi ที่ระบุไว้ และเชื่อมต่อด้วยวิธีอื่นเมื่ออยู่นอก Wi-Fi ที่ระบุไว้", + "automatic_endpoint_switching_title": "สลับ URL อัตโนมัติ", + "autoplay_slideshow": "เล่นสไลด์โชว์", "back": "กลับ", "back_close_deselect": "ย้อนกลับ, ปิด, หรือยกเลิกการเลือก", + "background_location_permission": "การอนุญาตระบุตำแหน่งพื้นหลัง", + "background_location_permission_content": "เพื่อที่จะสลับการเชื่อมต่อขณะที่รันในพื้นหลัง Immich ต้องรู้ตำแหน่งที่แม่ยำตลอดเวลา เพื่อจะสามารถอ่านชื่อ Wi-Fi", + "backup": "สำรองข้อมูล", "backup_album_selection_page_albums_device": "อัลบั้มบนเครื่อง ({count})", "backup_album_selection_page_albums_tap": "กดเพื่อรวม กดสองครั้งเพื่อยกเว้น", "backup_album_selection_page_assets_scatter": "ทรัพยาการสามารถกระจายไปในหลายอัลบั้ม ดังนั้นอัลบั้มสามารถถูกรวมหรือยกเว้นในกระบวนการสำรองข้อมูล", @@ -496,15 +520,16 @@ "backup_controller_page_background_is_on": "การสำรองข้อมูลอัตโนมัติเปิดอยู่", "backup_controller_page_background_turn_off": "ปิดบริการเบื้องหลัง", "backup_controller_page_background_turn_on": "เปิดบริการเบื้องหลัง", - "backup_controller_page_background_wifi": "บน WiFi เท่านั้น", + "backup_controller_page_background_wifi": "บน Wi-Fi เท่านั้น", "backup_controller_page_backup": "สำรองข้อมูล", "backup_controller_page_backup_selected": "ที่เลือก: ", "backup_controller_page_backup_sub": "รูปภาพและวิดีโอที่สำรองแล้ว", "backup_controller_page_created": "สร้างเมื่อ: {date}", "backup_controller_page_desc_backup": "เปิดการสำรองข้อมูลในฉากหน้าเพื่อที่จะอัพโหลดทรัพยากรใหม่ไปยังเซิร์ฟเวอร์เมื่อเปิดแอพ", - "backup_controller_page_excluded": "ถูกยกเว้น: ", + "backup_controller_page_excluded": "ยกเว้น: ", "backup_controller_page_failed": "ล้มเหลว ({count})", "backup_controller_page_filename": "ชื่อไฟล์: {filename} [{size}]", + "backup_controller_page_id": "ID: {id}", "backup_controller_page_info": "ข้อมูลเกี่ยวกับการสำรองข้อมูล", "backup_controller_page_none_selected": "ไม่มีที่เลือก", "backup_controller_page_remainder": "ที่เหลือ", @@ -526,7 +551,12 @@ "backup_manual_success": "สำเร็จ", "backup_manual_title": "สถานะอัพโหลด", "backup_options_page_title": "ตัวเลือกการสำรองข้อมูล", + "backup_setting_subtitle": "ตั้งค่าการอัพโหลดในฉากหน้า และพื้นหลัง", "backward": "กลับหลัง", + "biometric_auth_enabled": "การพิสูจน์อัตลักษณ์เพื่อยืนยันตัวบุคคลถูกเปิด", + "biometric_locked_out": "การพิสูจน์อัตลักษณ์เพื่อยืนยันตัวบุคคลถูกล็อค", + "biometric_no_options": "ไม่มีตัวเลือกการพิสูจน์อัตลักษณ์เพื่อยืนยันตัวบุคคล", + "biometric_not_available": "ไม่สามารถใช้งานการพิสูจน์อัตลักษณ์เพื่อยืนยันตัวบุคคลได้บนอุปกรณ์นี้", "birthdate_saved": "บันทึกวันเกิดแล้ว", "birthdate_set_description": "วันที่เกิดจะนำมาใช้ในการคำนวณอายุของบุคคลนี้ในขณะที่ถ่ายรูป", "blurred_background": "พื้นหลังแบบเบลอ", @@ -556,14 +586,19 @@ "camera_model": "รุ่นกล้อง", "cancel": "ยกเลิก", "cancel_search": "ยกเลิกการค้นหา", + "canceled": "ยกเลิก", "cannot_merge_people": "ไม่สามารถรวมกลุ่มคนได้", "cannot_undo_this_action": "การกระทำนี้ไม่สามารถย้อนกลับได้!", "cannot_update_the_description": "ไม่สามารถอัพเดทรายละเอียดได้", + "cast": "แคสต์", + "cast_description": "ตั้งค่าปลายทางแคสต์", "change_date": "เปลี่ยนวันที่", + "change_description": "แก้ไขคำอธิบาย", + "change_display_order": "เปลี่ยนลำดับการแสดงผล", "change_expiration_time": "เปลี่ยนเวลาหมดอายุ", "change_location": "เปลี่ยนตําแหน่ง", "change_name": "เปลี่ยนชื่อ", - "change_name_successfully": "เปลี่ยนชื่อเรียบร้อยแล้ว", + "change_name_successfully": "เปลี่ยนชื่อสำเร็จ", "change_password": "เปลี่ยนรหัสผ่าน", "change_password_description": "การเข้าสู่ระบบครั้งแรก จำเป็นจต้องเปลี่ยนรหัสผ่านของคุณเพื่อความปลอดภัย โปรดป้อนรหัสผ่านใหม่ด้านล่าง", "change_password_form_confirm_password": "ยืนยันรหัสผ่าน", @@ -574,6 +609,9 @@ "change_pin_code": "เปลี่ยนรหัสประจำตัว (PIN)", "change_your_password": "เปลี่ยนรหัสผ่านของคุณ", "changed_visibility_successfully": "เปลี่ยนการมองเห็นเรียบร้อยแล้ว", + "check_corrupt_asset_backup": "ตรวจสอบสำรองสื่อที่ผิดปกติ", + "check_corrupt_asset_backup_button": "ตรวจสอบ", + "check_corrupt_asset_backup_description": "ตรวจสอบเมื่อเชื่อมต่อ Wi-Fi และสื่อทั้งหมดถูกสำรองข้อมูลแล้วเท่านั้น การตรวจสอบอาจใช้เวลาหลายนาที", "check_logs": "ตรวจสอบบันทึก", "choose_matching_people_to_merge": "เลือกคนที่ตรงกันเพื่อรวมเข้าด้วยกัน", "city": "เมือง", @@ -582,6 +620,14 @@ "clear_all_recent_searches": "ล้างประวัติการค้นหา", "clear_message": "ล้างข้อความ", "clear_value": "ล้างค่า", + "client_cert_dialog_msg_confirm": "เสร็จ", + "client_cert_enter_password": "ใส่รหัสผ่าน", + "client_cert_import": "นำเข้า", + "client_cert_import_success_msg": "นำเข้าใบรับรองสำเร็จ", + "client_cert_invalid_msg": "ใบรับรอง หรือรหัสผ่านไม่ถูกต้อง", + "client_cert_remove_msg": "ลบใบรับรองสำเร็จ", + "client_cert_subtitle": "รองรับเฉพาะ PKCS12 (.p12, .pfx) เท่านั้น การนำเข้า/ลบใบรับรองสามารถทำได้ก่อนล็อคอินเท่านั้น", + "client_cert_title": "ใบรับรอง SSL ไคลเอนต์", "clockwise": "ตามเข็มนาฬิกา", "close": "ปิด", "collapse": "ย่อ", @@ -602,6 +648,10 @@ "confirm_keep_this_delete_others": "จะลบทั้งหมดในรายการ และยกเว้นสื่อนี้หรือไม่ คุณแน่ใจใช่ไหมที่ต้องการดำเนินการต่อ?", "confirm_new_pin_code": "ยืนยันรหัสประจำตัว (PIN)", "confirm_password": "ยืนยันรหัสผ่าน", + "confirm_tag_face": "คุณต้องการแท็กใบหน้านี้ด้วยชื่อ {name} หรือไม่", + "confirm_tag_face_unnamed": "คุณต้องการแท็กใบหน้านี้หรือไม่", + "connected_device": "อุปกรณ์ที่เชื่อมต่อแล้ว", + "connected_to": "เชื่อมต่อไปยัง", "contain": "มีอยู่", "context": "บริบท", "continue": "ต่อไป", @@ -610,6 +660,7 @@ "control_bottom_app_bar_delete_from_local": "ลบจากเรื่อง", "control_bottom_app_bar_edit_location": "แก้ไขตำแหน่ง", "control_bottom_app_bar_edit_time": "แก้ไขวันและเวลา", + "control_bottom_app_bar_share_link": "แชร์ลิงค์", "control_bottom_app_bar_share_to": "แชร์ให้", "control_bottom_app_bar_trash_from_immich": "ย้ายเข้าถังขยะ", "copied_image_to_clipboard": "คัดลอกภาพไปยังคลิปบอร์ดแล้ว", @@ -631,6 +682,7 @@ "create_link": "สร้างลิงก์", "create_link_to_share": "สร้างลิงก์เพื่อแชร์", "create_link_to_share_description": "ผู้ที่มีลิงก์ สามารถดูรูปที่เลือกได้", + "create_new": "สร้างใหม่", "create_new_person": "สร้างคนใหม่", "create_new_person_hint": "กำหนดสื่อที่เลือกให้กับคนใหม่", "create_new_user": "สร้างผู้ใช้งานใหม่", @@ -640,9 +692,12 @@ "create_tag_description": "สร้างแท็กใหม่ สำหรับแท็กที่ซ้อนกัน โปรดป้อนเส้นทางทั้งหมดของแท็ก รวมถึงเครื่องหมายทับ", "create_user": "สร้างผู้ใช้", "created": "สร้างแล้ว", + "created_at": "สร้างเมื่อ", + "crop": "ครอป", "curated_object_page_title": "สิ่งของ", "current_device": "อุปกรณ์ปัจจุบัน", "current_pin_code": "รหัสประจำตัว (PIN) ปัจจุบัน", + "current_server_address": "ที่อยู่เซิร์ฟเวอร์ปัจจุบัน", "custom_locale": "ปรับภาษาท้องถิ่นเอง", "custom_locale_description": "ใช้รูปแบบวันที่และตัวเลขจากภาษาและขอบเขต", "daily_title_text_date": "E dd MMM", @@ -694,6 +749,7 @@ "disallow_edits": "ไม่อนุญาตให้แก้ไข", "discord": "ดิสคอร์ด", "discover": "ค้นพบ", + "discovered_devices": "ค้นหาอุปกรณ์", "dismiss_all_errors": "ปฏิเสธข้อผิดพลาดทั้งหมด", "dismiss_error": "ปฏิเสธข้อผิดพลาด", "display_options": "ตัวเลือกการแสดง", @@ -704,12 +760,25 @@ "documentation": "เอกสาร", "done": "ดำเนินการสำเร็จ", "download": "ดาวน์โหลด", + "download_canceled": "การดาวน์โหลดยกเลิก", + "download_complete": "การดาวน์โหลดเสร็จสิ้น", + "download_enqueue": "การดาวน์โหลดอยู่ในคิว", + "download_error": "ดาวน์โหลดผิดพลาด", + "download_failed": "ดาวน์โหลดไม่สำเร็จ", + "download_finished": "ดาวน์โหลดเสร็จสิ้น", "download_include_embedded_motion_videos": "รวมวิดีโอที่ฝังอยู่ในภาพเคลื่อนไหว", "download_include_embedded_motion_videos_description": "รวมวิดีโอที่ฝังอยู่ในภาพเคลื่อนไหวเมื่อดาวน์โหลดอัลบั้ม", + "download_notfound": "ไม่พบดาวน์โหลด", + "download_paused": "หยุดการดาวน์โหลดชั่วคราว", "download_settings": "การตั้งค่าการดาวน์โหลด", "download_settings_description": "จัดการการตั้งค่าการดาวน์โหลด", + "download_started": "เริ่มการดาวน์โหลด", + "download_sucess": "ดาวน์โหลดสำเร็จ", + "download_sucess_android": "สื่อถูกดาวน์โหลดไปยัง DCIM/Immich", + "download_waiting_to_retry": "รอลองใหม่", "downloading": "กำลังดาวน์โหลด", "downloading_asset_filename": "กำลังดาวน์โหลด {filename}", + "downloading_media": "กำลังดาวน์โหลดสื่อ", "drop_files_to_upload": "วางไฟล์ในช่องอัปโหลด", "duplicates": "รายการที่ซ้ำกัน", "duplicates_description": "แก้ไขแต่ละกลุ่มโดยระบุว่ากลุ่มใดซ้ำกันหากมี", @@ -719,6 +788,8 @@ "edit_avatar": "แก้ไขตัวละคร", "edit_date": "แก้ไขวันที่", "edit_date_and_time": "แก้ไขวันที่และเวลา", + "edit_description": "แก้ไขคำอธิบาย", + "edit_description_prompt": "โปรดเลื่อกคำอธิบายใหม่", "edit_exclusion_pattern": "แก้ไขข้อยกเว้น", "edit_faces": "แก้ไขหน้า", "edit_import_path": "แก้ไขพาธนําเข้า", @@ -739,15 +810,24 @@ "editor_crop_tool_h2_aspect_ratios": "อัตราส่วนภาพ", "editor_crop_tool_h2_rotation": "การหมุน", "email": "อีเมล", + "email_notifications": "แจ้งเตือนผ่านอีเมล", + "empty_folder": "โฟลเดอร์นี้ว่างเปล่า", "empty_trash": "ทิ้งจากถังขยะ", "empty_trash_confirmation": "คุณแน่ใจหรือไม่ว่าต้องการล้างถังขยะ การดำเนินการนี้จะลบทรัพยากรทั้งหมดในถังขยะออกจาก Immich อย่างถาวร\nคุณไม่สามารถย้อนกลับการดำเนินการนี้ได้!", "enable": "เปิดใช้งาน", + "enable_biometric_auth_description": "ใส่พินเพื่อเปิดการพิสูจน์อัตลักษณ์เพื่อยืนยันตัวบุคคล", "enabled": "เปิดใช้งาน", "end_date": "วันสิ้นสุด", - "enter_wifi_name": "Enter WiFi name", + "enqueued": "รอคิว", + "enter_wifi_name": "ใส่ชื่อ Wi-Fi", + "enter_your_pin_code": "ใส่พินโค้ด", + "enter_your_pin_code_subtitle": "ใส่พินโค้ดเพื่อเข้าถึงโฟลเดอร์ล็อค", "error": "เกิดข้อผิดพลาด", + "error_change_sort_album": "เปลี่ยนการเรียงลำดับอัลบั้มไม่สำเร็จ", "error_delete_face": "เกิดเออเรอร์ ไม่สามารถลบใบหน้าออกได้", "error_loading_image": "เกิดข้อผิดพลาดระหว่างโหลดภาพ", + "error_saving_image": "เกิดข้อผิดพลาดระหว่างเซฟภาพ: {error}", + "error_tag_face_bounding_box": "การแท็กใบหน้าผิดพลาด - ไม่สามารถตีกรอบใบหน้าได้", "error_title": "เกิดข้อผิดพลาด", "errors": { "cannot_navigate_next_asset": "ไม่สามารถเปลี่ยนเส้นทางได้", @@ -755,7 +835,7 @@ "cant_apply_changes": "เกิดข้อผิดพลาดในการเปลี่ยนแปลง", "cant_change_activity": "Can't {enabled, select, true {disable} other {enable}} activity", "cant_change_asset_favorite": "ไม่สามารถเปลี่ยนสื่อที่ชื่นชอบได้", - "cant_change_metadata_assets_count": "Can't change metadata of {count, plural, one {# asset} other {# assets}}", + "cant_change_metadata_assets_count": "ไม่สามารถแก้ไขข้อมูล metadata ของ {count, plural, one {# สื่อ} other {# สื่อ}}", "cant_get_faces": "เกิดข้อผิดพลาดในการเรียกดูใบหน้า", "cant_get_number_of_comments": "ไม่สามารถเรียกดูจำนวนความคิดเห็นได้", "cant_search_people": "ไม่สามารถค้นหาบุคคลคนได้", @@ -775,10 +855,12 @@ "failed_to_keep_this_delete_others": "ไม่สามารถเก็บหรือลบได้", "failed_to_load_asset": "ไม่สามารถโหลดสื่อได้", "failed_to_load_assets": "ไม่สามารถโหลดสื่อได้", + "failed_to_load_notifications": "โหลดการแจ้งเตือนไม่สำเร็จ", "failed_to_load_people": "ไม่สามารถโหลดบุคคลได้", "failed_to_remove_product_key": "ไม่สามารถลบ product key ได้", "failed_to_stack_assets": "Failed to stack assets", "failed_to_unstack_assets": "Failed to un-stack assets", + "failed_to_update_notification_status": "อัพเดทสถานะการแจ้งเตือนไม่สำเร็จ", "import_path_already_exists": "พาธนำเข้านี้มีอยู่แล้ว", "incorrect_email_or_password": "อีเมลหรือรหัสผ่านไม่ถูกต้อง", "paths_validation_failed": "การตรวจสอบ {paths, plural, one {# path} other {# paths}} ล้มเหลว", @@ -795,6 +877,7 @@ "unable_to_archive_unarchive": "ไม่สามารถทำรายการ {archived, select, true {archive} other {unarchive}}", "unable_to_change_album_user_role": "ไม่สามารถเปลี่ยนบทบาทผู้ใช้ในอัลบั้มได้", "unable_to_change_date": "ไม่สามารถเปลี่ยนวันที่ได้", + "unable_to_change_description": "ไม่สามารถเปลี่ยนคำอธิบาย", "unable_to_change_favorite": "ไม่สามารถเปลี่ยนแปลงสื่อรายการโปรดได้", "unable_to_change_location": "ไม่สามารถเปลี่ยนตําแหน่งได้", "unable_to_change_password": "ไม่สามารถเปลี่ยนรหัสผ่านได้", @@ -838,6 +921,7 @@ "unable_to_remove_partner": "ไม่สามารถลบคู่หูได้", "unable_to_remove_reaction": "ไม่สามารถลบ reaction ได้", "unable_to_reset_password": "ไม่สามารถตั้งรหัสผ่านใหม่ได้", + "unable_to_reset_pin_code": "ไม่สามารถรีเซ็ตพินโค้ด", "unable_to_resolve_duplicate": "ไม่สามารถแก้ไขของซ้ำได้", "unable_to_restore_assets": "ไม่สามารถเรียกคืนสื่อได้", "unable_to_restore_trash": "ไม่สามารถเรียกคืนถังขยะได้", @@ -865,11 +949,15 @@ "unable_to_update_user": "ไม่สามารถอัพเดทผู้ใช้ได้", "unable_to_upload_file": "ไม่สามารถอัปโหลดได้" }, + "exif": "Exif", "exif_bottom_sheet_description": "เพิ่มคำอธิบาย", "exif_bottom_sheet_details": "รายละเอียด", "exif_bottom_sheet_location": "ตำแหน่ง", "exif_bottom_sheet_people": "คน", "exif_bottom_sheet_person_add_person": "เพิ่มชื่อ", + "exif_bottom_sheet_person_age_months": "อายุ {months} เดือน", + "exif_bottom_sheet_person_age_year_months": "อายุ 1 ปี {months} เดือน", + "exif_bottom_sheet_person_age_years": "อายุ {years} ปี", "exit_slideshow": "ออกจากการนำเสนอ", "expand_all": "ขยายทั้งหมด", "experimental_settings_new_asset_list_subtitle": "กำลังพัฒนา", @@ -886,9 +974,13 @@ "extension": "ส่วนต่อขยาย", "external": "ภายนอก", "external_libraries": "ภายนอกคลังภาพ", - "external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom", + "external_network": "การเชื่อมต่อภายนอก", + "external_network_sheet_info": "เมื่อไม่ได้เชื่อมต่อ Wi-Fi ที่เลือกไว้ แอพจะเชื่อมต่อเซิร์ฟเวอร์ผ่าน URL ด้านล่างตามลำดับ", "face_unassigned": "ไม่กำหนดมอบหมาย", + "failed": "ล้มเหลว", + "failed_to_authenticate": "การยืนยันตัวตนไม่สำเร็จ", "failed_to_load_assets": "เกิดข้อผิดพลาดในการโหลดสื่อ", + "failed_to_load_folder": "โหลดโฟลเดอร์ไม่สำเร็จ", "favorite": "รายการโปรด", "favorite_or_unfavorite_photo": "โปรดหรือไม่โปรดภาพ", "favorites": "รายการโปรด", @@ -900,18 +992,26 @@ "file_name_or_extension": "นามสกุลหรือชื่อไฟล์", "filename": "ชื่อไฟล์", "filetype": "ชนิดไฟล์", + "filter": "ตัวกรอง", "filter_people": "กรองผู้คน", + "filter_places": "กรองสถานที่", "find_them_fast": "ค้นหาโดยชื่ออย่างรวดเร็ว", "fix_incorrect_match": "แก้ไขการจับคู่ที่ไม่ถูกต้อง", + "folder": "โฟลเดอร์", + "folder_not_found": "ไม่พบโฟลเดอร์", "folders": "โฟล์เดอร์", "folders_feature_description": "การเรียกดูมุมมองโฟลเดอร์สำหรับภาพถ่ายและวิดีโอในระบบไฟล์", "forward": "ไปข้างหน้า", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "ฟีเจอร์นี้ต้องโหลดทรัพยากรจาก Google เพื่อทำงาน", "general": "ทั่วไป", "get_help": "ขอความช่วยเหลือ", + "get_wifiname_error": "ไม่สามารถรับชื่อ Wi-Fi กรุณายืนยันการให้อนุญาตแอพ และยืนยันว่า Wi-Fi เชื่อมต่ออยู่", "getting_started": "เริ่มต้นใช้งาน", "go_back": "กลับ", "go_to_folder": "ไปที่โฟล์เดอร์", "go_to_search": "กลับไปยังการค้นหา", + "grant_permission": "ให้อนุญาต", "group_albums_by": "จัดกลุ่มอัลบั้มตาม", "group_country": "จัดเรียงกลุ่มตามประเทศ", "group_no": "ไม่จัดกลุ่ม", @@ -921,6 +1021,11 @@ "haptic_feedback_switch": "เปิดการตอบสนองแบบสัมผัส", "haptic_feedback_title": "การตอบสนองแบบสัมผัส", "has_quota": "เหลือพื้นที่", + "header_settings_add_header_tip": "เพิ่ม Header", + "header_settings_field_validator_msg": "ค่าต้องไม่ว่างเปล่า", + "header_settings_header_name_input": "ชื่อ Header", + "header_settings_header_value_input": "ค่า Header", + "headers_settings_tile_title": "ปรับแต่ง proxy headers", "hi_user": "สวัสดีคุณ {name} {email}", "hide_all_people": "ซ่อนบุคคลทั้งหมด", "hide_gallery": "ซ่อนคลังภาพ", @@ -929,22 +1034,28 @@ "hide_person": "ซ่อนบุคคล", "hide_unnamed_people": "ซ่อนบุคคลที่ไม่ได้ระบุชื่อ", "home_page_add_to_album_conflicts": "เพิ่ม {added} ทรัพยากรเข้าอัลบั้ม {album}. {failed} ทรัพยากรอยู่ในอัลบั้มอยู่แล้ว", - "home_page_add_to_album_err_local": " ยังไม่สามารถเพิ่มทรัพยากรบนเครื่องเข้าอัลบั้ม กำลังข้าม", + "home_page_add_to_album_err_local": "ยังไม่สามารถเพิ่มสื่อบนอุปกรณ์เข้าอัลบั้ม ข้าม", "home_page_add_to_album_success": "เพิ่มทรัพยากร {added} เข้าอัลบั้ม {album}", - "home_page_album_err_partner": "ยังไม่สามารถเพิ่มทรัพยากรของพันธมิตรได้ กำลังข้าม", + "home_page_album_err_partner": "ยังไม่สามารถเพิ่มสื่อของคู่หูได้ กำลังข้าม", "home_page_archive_err_local": "ยังไม่สามารถเก็บถาวรได้ กำลังข้าม", - "home_page_archive_err_partner": "ไม่สามารถเก็บทรัพยากรของพันธมิตรได้ กำลังข้าม", + "home_page_archive_err_partner": "ไม่สามารถเก็บสื่อของคู่หูได้ กำลังข้าม", "home_page_building_timeline": "กำลังสร้าง timeline", - "home_page_delete_err_partner": "ไม่สามารถลบทรัพยากรของพันธมิตรได้ กำลังข้าม", + "home_page_delete_err_partner": "ไม่สามารถลบสื่อของคู่ได้ กำลังข้าม", "home_page_delete_remote_err_local": "ทรัพยากรบนเครื่องอยู่ในลบจากรีโมท กำลังข้าม", "home_page_favorite_err_local": "ยังไม่สามารถตั้งทรัพยากรบนเครื่องเป็นรายการโปรด กำลังข้าม", - "home_page_favorite_err_partner": "ยังไม่สามารถเพิ่มทรัพยากรของพันธมิตรในรายการโปรดได้ กำลังข้าม", + "home_page_favorite_err_partner": "ยังไม่สามารถเพิ่มสื่อของคู่หูในรายการโปรดได้ กำลังข้าม", "home_page_first_time_notice": "ถ้าครั้งนี้เป็นครั้งแรกที่ใช้แอปนี้ กรุณาเลือกอัลบั้มที่จะสำรองข้อมูล ไทม์ไลน์จะได้เพิ่มรูปภาพและวิดีโอที่อยู่ในอัลบั้ม", + "home_page_locked_error_local": "ไม่สามารถย้ายสื่อบนอุปกรณ์ไปยังโฟลเดอร์ล็อค ข้าม", + "home_page_locked_error_partner": "ยังไม่สามารถเพิ่มสื่อของคู่หูไปยังโฟลเดอร์ล็อคได้ กำลังข้าม", "home_page_share_err_local": "ไม่สามารถแชร์ผ่านลิงค์ได้ กำลังข้าม", "home_page_upload_err_limit": "สามารถอัพโหลดได้มากสุดครั้งละ 30 ทรัพยากร กำลังข้าม", "host": "โฮสต์", "hour": "ชั่วโมง", + "id": "ไอดี", + "ignore_icloud_photos": "ข้ามภาพบน iCloud", + "ignore_icloud_photos_description": "ภาพที่ถูกเก็บบน iCloud จะไม่ถูกอัพโหลดขึ้น Immich", "image": "รูปภาพ", + "image_alt_text_date": "{isVideo, select, true {วิดีโอ} other {รูปภาพ}}ถูกถ่ายเมื่อ {date}", "image_alt_text_date_1_person": "{isVideo, select, true {Video} other {Image}} ถ่ายกับ {person1} วันที่ {date}", "image_alt_text_date_2_people": "{isVideo, select, true {Video} other {Image}} ถ่ายกับ {person1} และ {person2} วันที่ {date}", "image_alt_text_date_3_people": "{isVideo, select, true {Video} other {Image}} ถ่ายกับ {person1}, {person2},และ {person3} วันที่ {date}", @@ -954,6 +1065,7 @@ "image_alt_text_date_place_2_people": "{isVideo, select, true {Video} other {Image}} ถ่ายใน {city}, {country} กับ {person1} และ {person2} วันที่ {date}", "image_alt_text_date_place_3_people": "{isVideo, select, true {Video} other {Image}} ถ่ายใน {city}, {country} กับ {person1}, {person2},และ {person3} วันที่ {date}", "image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Video} other {Image}} ถ่ายใน {city}, {country} กับ {person1}, {person2}, และ {additionalCount, number} ในวันที่ {date}", + "image_saved_successfully": "รูปภาพถูกเซฟ", "image_viewer_page_state_provider_download_started": "ดาวน์โหลดเริ่มต้น", "image_viewer_page_state_provider_download_success": "ดาวน์โหลดสำเร็จ", "image_viewer_page_state_provider_share_error": "แชร์ผิดพลาด", @@ -974,8 +1086,16 @@ "night_at_midnight": "ทุกเที่ยงคืน", "night_at_twoam": "ทุกวันเวลาตี 2" }, + "invalid_date": "วันที่ไม่ถูกต้อง", + "invalid_date_format": "รูปแบบวันที่ไม่ถูกต้อง", "invite_people": "เชิญผู้คน", "invite_to_album": "เชิญเข้าอัลบั้ม", + "ios_debug_info_fetch_ran_at": "รับข้อมูลเมื่อ {dateTime}", + "ios_debug_info_last_sync_at": "ซิงค์ล่าสุด {dateTime}", + "ios_debug_info_no_processes_queued": "ไม่มีคิวในพื้นหลัง", + "ios_debug_info_no_sync_yet": "ยังไม่มีงานซิงค์รันในพื้นหลัง", + "ios_debug_info_processes_queued": "{count} โพรเซสรอคิวในพื้นหลัง", + "ios_debug_info_processing_ran_at": "โพรเซสรันเมื่อ {dateTime}", "items_count": "{count, plural, one {# รายการ} other {#รายการ}}", "jobs": "งาน", "keep": "เก็บ", @@ -984,6 +1104,9 @@ "kept_this_deleted_others": "เก็บเนื้อหานี้และลบ {count, plural, one {# Asset} other {# Asset}}", "keyboard_shortcuts": "ปุ่มพิมพ์ลัด", "language": "ภาษา", + "language_no_results_subtitle": "กรุณาปรับเปลี่ยนคำค้นหา", + "language_no_results_title": "ไม่พบภาษา", + "language_search_hint": "ค้นหาภาษา...", "language_setting_description": "เลือกภาษาที่ต้องการ", "last_seen": "เห็นล่าสุด", "latest_version": "เวอร์ชันล่าสุด", @@ -1003,20 +1126,26 @@ "light": "สว่าง", "like_deleted": "ลบที่ถูกใจแล้ว", "link_motion_video": "ลิงก์วิดีโอเคลื่อนไหว", - "link_options": "ตัวเลือกลิงก์", "link_to_oauth": "ลิงก์ไปยัง OAuth", "linked_oauth_account": "ลิงก์บัญชีผู้ใช้ OAuth", "list": "รายการ", "loading": "กำลังโหลด", "loading_search_results_failed": "โหลดผลการค้นหาล้มเหลว", - "location_permission_content": "In order to use the auto-switching feature, Immich needs precise location permission so it can read the current WiFi network's name", + "local_asset_cast_failed": "ไม่สามารถแคสสื่อที่ไม่ถูกอัพโหลดไปยังเซิร์ฟเวอร์", + "local_network": "เครือข่ายระยะใกล้", + "local_network_sheet_info": "แอพจะทำการเชื่อมต่อไปยังเซิร์ฟเวอร์ผ่าน URL นี้เมื่อเชื่อต่อกับ Wi-Fi ที่เลือกไว้", + "location_permission": "การอนุญาตตำแหน่ง", + "location_permission_content": "เพื่อใช้ฟีเจอร์การสับโดยอัตโนมัติ Immich ต้องการการอนุญาตเข้าถึงต่ำแหน่งที่แม่นยำเพื่ออ่านชื่อ Wi-Fi ที่เชื่อมต่ออยู่", "location_picker_choose_on_map": "เลือกบนแผนที่", "location_picker_latitude_error": "กรุณาเพิ่มละติจูตที่ถูกต้อง", "location_picker_latitude_hint": "เพิ่มละติจูตตรงนี้", "location_picker_longitude_error": "กรุณาเพิ่มลองจิจูตที่ถูกต้อง", "location_picker_longitude_hint": "เพิ่มลองจิจูตตรงนี้", + "lock": "ล็อค", + "locked_folder": "โฟลเดอร์ล็อค", "log_out": "ออกจากระบบ", "log_out_all_devices": "ให้ทุกอุปกรณ์ออกจากระบบทั้งหมด", + "logged_in_as": "{user} กำลังล็อคอิน", "logged_out_all_devices": "ออกจากระบบทั้งหมดแล้ว", "logged_out_device": "ออกจากระบบแล้ว", "login": "เข้าสู่ระบบ", @@ -1059,7 +1188,6 @@ "manage_your_devices": "จัดการอุปกรณ์ของคุณ", "manage_your_oauth_connection": "จัดการการเชื่อมต่อ OAuth ของคุณ", "map": "แผนที่", - "map_assets_in_bound": "{count} รูปภาพ", "map_assets_in_bounds": "{count} รูปภาพ", "map_cannot_get_user_location": "ไม่สามารถหาตำแหน่งผู้ใช้งานได้", "map_location_dialog_yes": "ใช่", @@ -1079,7 +1207,7 @@ "map_settings_date_range_option_years": "{years} ปีผ่านมา", "map_settings_dialog_title": "ตั้งค่าแผนที่", "map_settings_include_show_archived": "รวมเก็บถาวร", - "map_settings_include_show_partners": "รามพันธมิตร", + "map_settings_include_show_partners": "รวมคู่หู", "map_settings_only_show_favorites": "แสดงรายการโปรดเท่านั้น", "map_settings_theme_settings": "ธีมแผนที่", "map_zoom_to_see_photos": "ซูมออกเพื่อดูรูป", @@ -1106,12 +1234,17 @@ "model": "โมเดล", "month": "เดือน", "more": "เพิ่มเติม", + "move": "ย้าย", + "move_off_locked_folder": "ย้ายออกจากโฟลเดอร์ล็อค", + "move_to_locked_folder": "ย้ายไปโฟลเดอร์ล็อค", "moved_to_trash": "ทิ้งลงถังขยะแล้ว", "multiselect_grid_edit_date_time_err_read_only": "ไม่สามารถแก้ไขวันที่ทรัพยากรแบบอ่านอย่างเดียว กำลังข้าม", "multiselect_grid_edit_gps_err_read_only": "ไม่สามารถแก้ตำแหน่งของทรัพยากรแบบอ่านอย่างเดียว กำลังข้าม", "my_albums": "อัลบั้มของฉัน", "name": "ชื่อ", "name_or_nickname": "ชื่อหรือชื่อเล่น", + "networking_settings": "การเชื่อมต่อ", + "networking_subtitle": "ตั้งค่าปลายทางเซิร์ฟเวอร์", "never": "ไม่เคย", "new_album": "อัลบั้มใหม่", "new_api_key": "สร้าง API คีย์ใหม่", @@ -1155,7 +1288,7 @@ "ok": "ตกลง", "oldest_first": "เรียงเก่าสุดก่อน", "onboarding": "การเริ่มต้นใช้งาน", - "onboarding_privacy_description": "คุณลักษณะ (ไม่จำเป็น) ต่อไปนี้ต้องอาศัยบริการภายนอก และสามารถปิดใช้งานได้ตลอดเวลาในการตั้งค่าการดูแลระบบ", + "onboarding_privacy_description": "ฟีเจอร์ (ตัวเลือก) ต่อไปนี้ต้องอาศัยบริการภายนอก และสามารถปิดใช้งานได้ตลอดเวลาในการตั้งค่าการ", "onboarding_theme_description": "เลือกธีมสี คุณสามารถเปลี่ยนแปลงได้ในภายหลังในการตั้งค่าของคุณ", "onboarding_welcome_user": "ยินดีต้อนรับคุณ {user}", "online": "ออนไลน์", @@ -1172,20 +1305,20 @@ "other_variables": "ตัวแปรอื่น", "owned": "เป็นเจ้าของ", "owner": "เจ้าของ", - "partner": "พาร์ทเนอร์", + "partner": "คู่หู", "partner_can_access": "{partner} สามารถเข้าถึง", "partner_can_access_assets": "รูปภาพและวิดีโอทั้งหมดยกเว้นที่อยู่ในเก็บถาวรและถูกลบทิ้ง", "partner_can_access_location": "ตำแหน่งที่รูปถูกถ่าย", "partner_list_user_photos": "รูปภาพของ {user}", "partner_list_view_all": "ดูทั้งหมด", - "partner_page_empty_message": "รูปภาพของคุณยังไม่ถูกแชร์กับพันธมิตร", + "partner_page_empty_message": "รูปภาพของคุณยังไม่ถูกแชร์กับคู่หู", "partner_page_no_more_users": "ไม่มีผู้ใช้งานให้เพิ่ม", - "partner_page_partner_add_failed": "การเพิ่มพันธมิตรล้มเหลว", - "partner_page_select_partner": "เลือกพันธมิตร", + "partner_page_partner_add_failed": "การเพิ่มคู่หูล้มเหลว", + "partner_page_select_partner": "เลือกคู่หู", "partner_page_shared_to_title": "แชร์กับ", "partner_page_stop_sharing_content": "{partner} จะไม่สามารถเข้าถึงรูปภาพของคุณ", - "partner_sharing": "แชร์สำหรับพาร์ทเนอร์", - "partners": "พาร์ทเนอร์", + "partner_sharing": "แชร์สำหรับคู่หู", + "partners": "คู่หู", "password": "รหัสผ่าน", "password_does_not_match": "รหัสผ่านไม่ตรงกัน", "password_required": "จำเป็นต้องมีรหัสผ่าน", @@ -1276,7 +1409,7 @@ "purchase_lifetime_description": "ซื้อตลอดชีพ", "purchase_option_title": "ตัวเลือกการซื้อ", "purchase_panel_info_1": "ทางทีม Immich ต้องใช้เวลาและความพยายามอย่างมากในการพัฒนาระบบนี้ขึ้นมา และเรามีวิศวกรที่ทำงานเต็มเวลาเพื่อพัฒนาให้ดีที่สุดเท่าที่จะทำได้ ภารกิจของเราคือการทำให้ซอฟต์แวร์โอเพ่นซอร์สและแนวทางปฏิบัติทางธุรกิจที่ถูกต้องตามจริยธรรมกลายเป็นแหล่งรายได้ที่ยั่งยืนสำหรับนักพัฒนา และสร้างระบบนิเวศที่เคารพความเป็นส่วนตัวพร้อมทางเลือกอื่นที่เป็นรูปธรรมแทนบริการคลาวด์ที่เอารัดเอาเปรียบ", - "purchase_panel_info_2": "เนื่องจากเราให้คำมั่นว่า จะไม่เพิ่มระบบชำระเงินในระบบของเรา ดังนั้นการซื้อครั้งนี้จะไม่ทำให้คุณได้รับฟีเจอร์เพิ่มเติมใน Immich เป็นพิเศษ เราอาศัยผู้คนแบบท่านในการสนับสนุนการพัฒนาอย่างต่อเนื่องของ Immich", + "purchase_panel_info_2": "เนื่องจากเราให้คำมั่นว่า จะไม่เพิ่มระบบชำระเงินในระบบของเรา ดังนั้นการซื้อครั้งนี้จะไม่ทำให้คุณได้รับฟีเจอร์เพิ่มเติมใน Immich เป็นพิเศษ เราอาศัยผู้คนแบบคุณในการสนับสนุนการพัฒนาอย่างต่อเนื่องของ Immich", "purchase_panel_title": "สนับสนุนโครงการนี้", "purchase_per_server": "ต่อเซิร์ฟเวอร์", "purchase_per_user": "ต่อผู้ใช้งาน", @@ -1421,6 +1554,7 @@ "select_keep_all": "เลือกเก็บทั้งหมด", "select_library_owner": "เลือกเจ้าของคลังภาพ", "select_new_face": "เลือกใบหน้าใหม่", + "select_person_to_tag": "เลือกบุคคล", "select_photos": "เลือกรูปภาพ", "select_trash_all": "เลือกในถังขยะทั้งหมด", "select_user_for_sharing_page_err_album": "สร้างอัลบั้มล้มเหลว", @@ -1428,12 +1562,14 @@ "selected_count": "{count, plural, other {# เลือกแล้ว}}", "send_message": "ส่งข้อความ", "send_welcome_email": "ส่งอีเมลต้อนรับ", + "server_endpoint": "ปลายทางเซิร์ฟเวอร์", "server_info_box_app_version": "เวอร์ชันแอพ", "server_info_box_server_url": "URL เซิร์ฟเวอร์", "server_offline": "Server ออฟไลน์", "server_online": "Server ออนไลน์", + "server_privacy": "ความเป็นส่วนตัวเซิร์ฟเวอร์", "server_stats": "สถิติเซิร์ฟเวอร์", - "server_version": "เวอร์ชันของ Server", + "server_version": "เวอร์ชันของเซิร์ฟเวอร์", "set": "ตั้ง", "set_as_album_cover": "ตั้งเป็นภาพปกอัลบั้ม", "set_as_featured_photo": "ตั้งเป็นรูปสำคัญ", @@ -1518,7 +1654,7 @@ "sharing_page_empty_list": "รายการว่างเปล่า", "sharing_sidebar_description": "แสดงลิงก์ที่แชร์ในแถบด้านข้าง", "sharing_silver_appbar_create_shared_album": "อัลบั้มที่แชร์ใหม่", - "sharing_silver_appbar_share_partner": "แชร์กับพันธมิตร", + "sharing_silver_appbar_share_partner": "แชร์กับคู่หู", "shift_to_permanent_delete": "กด ⇧ to สำหรับลบสื่อถาวร", "show_album_options": "แสดงตัวเลือกอัลบั้ม", "show_albums": "แสดงอัลบั้ม", @@ -1570,7 +1706,7 @@ "status": "สถานะ", "stop_motion_photo": "ภาพวัตถุเคลื่อนไหว", "stop_photo_sharing": "หยุดแชร์รูปภาพ?", - "stop_photo_sharing_description": "{partner}จะไม่สามารถเข้าถึงรูปของคุณได้อีก", + "stop_photo_sharing_description": "{partner} จะไม่สามารถเข้าถึงรูปของคุณได้อีก", "stop_sharing_photos_with_user": "หยุดการแชร์รูปภาพของคุณกับผู้ใช้นี้", "storage": "พื้นที่จัดเก็บ", "storage_label": "เนื้อที่จัดเก็บ", @@ -1644,6 +1780,7 @@ "unselect_all": "ยกเลิกการเลือกทั้งหมด", "unstack": "หยุดซ้อน", "up_next": "ต่อไป", + "updated_at": "อัพเดท", "updated_password": "รหัสผ่านเปลี่ยนแล้ว", "upload": "อัปโหลด", "upload_concurrency": "อัปโหลดพร้อมกัน", @@ -1653,7 +1790,9 @@ "upload_status_errors": "ข้อผิดพลาด", "upload_status_uploaded": "อัปโหลดแล้ว", "upload_success": "อัปโหลดสำเร็จ, รีเฟรชหน้านี้ใหม่คุณจะเห็นสื่อที่เพิ่มล่าสุด", + "uploading": "กำลังอัพโหลด", "usage": "การใช้งาน", + "use_biometric": "ใช้การพิสูจน์อัตลักษณ์", "use_custom_date_range": "ใช้การปรับแต่งช่วงเวลา", "user": "ผู้ใช้", "user_id": "ไอดีผู้ใช้", @@ -1686,6 +1825,7 @@ "view_links": "ดูลิงก์", "view_next_asset": "ดูสื่อถัดไป", "view_previous_asset": "ดูสื่อก่อนหน้า", + "view_qr_code": "ดูคิวอาร์โค้ด", "viewer_remove_from_stack": "เอาออกจากที่ซ้อน", "viewer_stack_use_as_main_asset": "ใช้เป็นทรัพยากรหลัก", "viewer_unstack": "หยุดซ้อน", @@ -1695,11 +1835,11 @@ "week": "สัปดาห์", "welcome": "ยินดีต้อนรับ", "welcome_to_immich": "ยินดีต้อนรับสู่ immich", - "wifi_name": "WiFi Name", + "wifi_name": "ชื่อ Wi-Fi", "year": "ปี", "years_ago": "{years, plural, one {# ปี} other {# ปี}} ที่แล้ว", "yes": "ใช่", "you_dont_have_any_shared_links": "คุณไม่ได้มีลิงก์ที่แชร์", - "your_wifi_name": "Your WiFi name", + "your_wifi_name": "ชื่อ Wi-Fi", "zoom_image": "ซูมรูปภาพ" } diff --git a/i18n/tr.json b/i18n/tr.json index ddb99ca019..c7a327472b 100644 --- a/i18n/tr.json +++ b/i18n/tr.json @@ -14,6 +14,7 @@ "add_a_location": "Lokasyon ekle", "add_a_name": "İsim ekle", "add_a_title": "Başlık ekle", + "add_birthday": "Doğum günü ekle", "add_endpoint": "Uç nokta ekle", "add_exclusion_pattern": "Hariç tutma deseni ekle", "add_import_path": "İçe aktarma yolu ekle", @@ -34,6 +35,7 @@ "added_to_favorites_count": "{count, number} fotoğraf favorilere eklendi", "admin": { "add_exclusion_pattern_description": "Hariç tutma desenleri ekleyin. *, ** ve ? kullanılarak Globbing (temsili yer doldurucu karakter) desteklenir. Farzedelim \"Raw\" adlı bir dizininiz var, içinde ki tüm dosyaları yoksaymak için \"**/Raw/**\" şeklinde yazabilirsiniz. \".tif\" ile biten tüm dosyaları yoksaymak için \"**/*.tif\" yazabilirsiniz. Mutlak yolu yoksaymak için \"/yoksayılacak/olan/yol/**\" şeklinde yazabilirsiniz.", + "admin_user": "Yönetici kullanıcısı", "asset_offline_description": "Bu harici kütüphane varlığı artık diskte bulunmuyor ve çöp kutusuna taşındı. Dosya kütüphane içinde taşındıysa, yeni karşılık gelen varlık için zaman çizelgenizi kontrol edin. Bu varlığı geri yüklemek için lütfen aşağıdaki dosya yolunun Immich tarafından erişilebilir olduğundan emin olun ve kütüphaneyi tarayın.", "authentication_settings": "Yetkilendirme Ayarları", "authentication_settings_description": "Şifre, OAuth, ve diğer yetkilendirme ayarlarını yönet", @@ -43,8 +45,15 @@ "backup_database": "Veritabanı yığını oluştur", "backup_database_enable_description": "Veritabanı yığınlarını etkinleştir", "backup_keep_last_amount": "Tutulması gereken geçmiş yığını miktarı", + "backup_onboarding_1_description": "bulutta veya başka bir fiziksel konumda bulunan yedek kopya.", + "backup_onboarding_2_description": "farklı cihazlarda yerel kopyalar. Bu, ana dosyaları ve bu dosyaların yerel yedeklerini içerir.", + "backup_onboarding_3_description": "Verilerinizin toplam kopyaları, orijinal dosyalar dahil. Bu, 1 adet dış mekan kopyası ve 2 adet yerel kopya içerir.", + "backup_onboarding_description": "Verilerinizi korumak için 3-2-1 yedekleme stratejisi önerilir. Kapsamlı bir yedekleme çözümü için, yüklediğiniz fotoğrafların/videoların yanı sıra Immich veritabanının kopyalarını da saklamalısınız.", + "backup_onboarding_footer": "Immich'i yedekleme hakkında daha fazla bilgi için lütfen belgelere bakın.", + "backup_onboarding_parts_title": "3-2-1 yedekleme şunları içerir:", + "backup_onboarding_title": "Yedeklemeler", "backup_settings": "Veritabanı yığını ayarları", - "backup_settings_description": "Veritabanı Yedekleme Ayarlarını Yönet", + "backup_settings_description": "Veritabanı döküm ayarlarını yönet.", "cleared_jobs": "{job} için işler temizlendi", "config_set_by_file": "Ayarlar şuanda config dosyası tarafından ayarlanmıştır", "confirm_delete_library": "{library} kütüphanesini silmek istediğinize emin misiniz?", @@ -68,7 +77,9 @@ "force_delete_user_warning": "UYARI: Bu işlem kullanıcıyı ve tüm varlıkları anında kaldıracaktır. Bu geri alınamaz ve dosyalar geri getirilemez.", "image_format": "Biçim", "image_format_description": "WebP, JPEG'e göre daha küçük dosya boyutu sunar fakat işlemesi daha uzun sürer.", + "image_fullsize_description": "Yakınlaştırıldığında kullanılan, meta verileri kaldırılmış tam boyutlu görüntü", "image_fullsize_enabled": "Tam boyutlu görüntü üretimini etkinleştir", + "image_fullsize_enabled_description": "Yerleşik önizlemeyi tercih et” seçeneği etkinleştirildiğinde, yerleşik önizlemeler dönüştürme yapılmadan doğrudan kullanılır. JPEG gibi web dostu formatlar bu ayardan etkilenmez.", "image_fullsize_quality_description": "1-100 arasında tam boyutlu görüntü kalitesi. Daha yüksek kalitelidir, ancak daha büyük dosyalar üretir.", "image_fullsize_title": "Tam boyutlu görüntü ayarları", "image_prefer_embedded_preview": "Gömülü önizlemeyi tercih et", @@ -163,12 +174,26 @@ "metadata_settings_description": "Metaveri ayarlarını yönet", "migration_job": "Birleştirme", "migration_job_description": "Varlıklar ve yüzler için resim çerçeve önizlemelerini en yeni klasör yapısına aktar", + "nightly_tasks_cluster_faces_setting_description": "Yeni algılanan yüzlerde yüz tanıma işlemini çalıştırın", + "nightly_tasks_cluster_new_faces_setting": "Yeni yüzleri bir araya getirin", + "nightly_tasks_database_cleanup_setting": "Veritabanı temizleme görevleri", + "nightly_tasks_database_cleanup_setting_description": "Veritabanından eski, süresi dolmuş verileri temizleyin", + "nightly_tasks_generate_memories_setting": "Anılar oluşturun", + "nightly_tasks_generate_memories_setting_description": "Varlıklardan yeni anılar yaratın", + "nightly_tasks_missing_thumbnails_setting": "Eksik küçük resimleri oluştur", + "nightly_tasks_missing_thumbnails_setting_description": "Küçük resim oluşturmak için küçük resim içermeyen varlıkları sıraya alın", + "nightly_tasks_settings": "Gece Görevleri Ayarları", + "nightly_tasks_settings_description": "Gece görevlerini yönet", + "nightly_tasks_start_time_setting": "Başlangıç saati", + "nightly_tasks_start_time_setting_description": "Sunucunun gece görevlerini çalıştırmaya başladığı saat", + "nightly_tasks_sync_quota_usage_setting": "Kota kullanımını senkronize et", + "nightly_tasks_sync_quota_usage_setting_description": "Mevcut kullanıma göre kullanıcı depolama kotasını güncelle", "no_paths_added": "Yol eklenmedi", "no_pattern_added": "Desen eklenmedi", "note_apply_storage_label_previous_assets": "Not: Daha önce yüklenen varlıklara Depolama Etiketi uygulamak için şu komutu çalıştırın", "note_cannot_be_changed_later": "NOT: Bu daha sonra değiştirilemez!", "notification_email_from_address": "Şu adresten", - "notification_email_from_address_description": "Göndericinin email adresi, örnek: \"Immich Fotoğraf Sunucusu \"", + "notification_email_from_address_description": "Gönderen e-posta adresi, örneğin: \"Immich Görsel Sunucusu \". E-posta gönderilmesine izin verdiğiniz bir adres kullandığınızdan emin olun.", "notification_email_host_description": "E-posta sunucusunun ana bilgisayarı (örneğin, smtp.immich.app)", "notification_email_ignore_certificate_errors": "Sertifika hatalarını görmezden gel", "notification_email_ignore_certificate_errors_description": "TLS sertifika doğrulama ayarlarını görmezden gel (Önerilmez)", @@ -188,10 +213,13 @@ "oauth_auto_register": "Otomatik kayıt", "oauth_auto_register_description": "OAuth ile giriş yapan yeni kullanıcıları otomatik kaydet", "oauth_button_text": "Buton yazısı", + "oauth_client_secret_description": "OAuth sağlayıcısı PKCE (Kod Değişimi İçin Kanıt Anahtarı) desteği sunmuyorsa gereklidir", "oauth_enable_description": "OAuth ile giriş yap", "oauth_mobile_redirect_uri": "Mobil yönlendirme URL'si", "oauth_mobile_redirect_uri_override": "Mobilde zorla kullanılacak Yönlendirme Adresi", "oauth_mobile_redirect_uri_override_description": "Mobil URI'ye izin vermeyen OAuth sağlayıcısı olduğunda etkinleştir, örneğin ''{callback}''", + "oauth_role_claim": "Rol Talebi", + "oauth_role_claim_description": "Bu iddianın varlığına göre otomatik olarak yönetici erişimi verin. İddia, 'kullanıcı' veya 'yönetici' olabilir.", "oauth_settings": "OAuth", "oauth_settings_description": "OAuth giriş ayarlarını yönet", "oauth_settings_more_details": "Bu özellik hakkında daha fazla bilgi için bu sayfayı ziyaret edin Dökümanlar.", @@ -200,7 +228,7 @@ "oauth_storage_quota_claim": "Depolama kotası talebi", "oauth_storage_quota_claim_description": "Kullanıcıya depolama kotası koymak için kullanılacak değer (en: OAuth claim).", "oauth_storage_quota_default": "Varsayılan depolama kotası (GiB)", - "oauth_storage_quota_default_description": "Değer (en: OAuth claim) mevcut değilse konulacak kota. GiB cinsinden, sınırsız kota için 0 kullanın.", + "oauth_storage_quota_default_description": "Değer (en: OAuth claim) mevcut değilse GiB cinsinden konulacak kota.", "oauth_timeout": "İstek zaman aşımı", "oauth_timeout_description": "Milisaniye cinsinden istek zaman aşımı", "password_enable_description": "Email ve şifre ile giriş yap", @@ -237,9 +265,10 @@ "storage_template_hash_verification_enabled_description": "Hash doğrulamayı etkinleştirir, eğer ne işe yaradığını bilmiyorsanız bunu devre dışı bırakmayın", "storage_template_migration": "Depolama şablonu birleştirme", "storage_template_migration_description": "Geçerli {template} ayarlarını daha önce yüklenmiş olan varlıklara uygula", - "storage_template_migration_info": "Şablon ayarlarındaki değişiklikler sadece yeni varlıklara uygulanacak. Şablon ayarlarını daha önce yüklenmiş olan varlıklara uygulamak için {job} çalıştırın.", + "storage_template_migration_info": "Depolama şablonu tüm dosya uzantılarını küçük harfe dönüştürecektir. Şablon ayarlarındaki değişiklikler sadece yeni varlıklara uygulanacak. Şablon ayarlarını daha önce yüklenmiş olan varlıklara uygulamak için {job} çalıştırın.", "storage_template_migration_job": "Depolama Adreslerini Değiştirme Görevi", "storage_template_more_details": "Bu özellik hakkında daha fazla bilgi için, Depolama Şablonu ve onun etkileri kısmına bakın", + "storage_template_onboarding_description_v2": "Etkinleştirildiğinde, bu özellik dosyaları kullanıcı tanımlı bir şablona göre otomatik olarak organize eder. Daha fazla bilgi için lütfen belgelere bakın.", "storage_template_path_length": "Tahmini dosya adresi uzunluğu: {length, number}/{limit, number}", "storage_template_settings": "Depolama Şablonu", "storage_template_settings_description": "Yüklenen dosyanın ismini ve klasör yapısını düzenle", @@ -254,7 +283,7 @@ "template_email_update_album": "Albüm Şablonunu Güncelle", "template_email_welcome": "Hoş geldiniz e-posta şablonu", "template_settings": "Bildirim Şablonları", - "template_settings_description": "Bildirim şablonlarını yönet.", + "template_settings_description": "Bildirim şablonlarını yönet", "theme_custom_css_settings": "Özel CSS", "theme_custom_css_settings_description": "CSS (Cascading Style Sheets) kullanılarak Immich'in tasarımı değiştirilebilir.", "theme_settings": "Tema ayarları", @@ -286,13 +315,13 @@ "transcoding_encoding_options": "Kodlama Seçenekleri", "transcoding_encoding_options_description": "Kodlanmış videolar için kodekleri, çözünürlüğü, kaliteyi ve diğer seçenekleri ayarlayın", "transcoding_hardware_acceleration": "Donanım Hızlandırma", - "transcoding_hardware_acceleration_description": "Deneysel; daha hızlı, fakat aynı bitrate ayarlarında daha düşük kaliteye sahip", + "transcoding_hardware_acceleration_description": "Deneysel: daha hızlı dönüştürme, ancak aynı bit hızında kaliteyi düşürebilir", "transcoding_hardware_decoding": "Donanım çözücü", "transcoding_hardware_decoding_setting_description": "Uçtan uca hızlandırmayı, sadece kodlamayı hızlandırmanın yerine etkinleştirir. Tüm videolarda çalışmayabilir.", "transcoding_max_b_frames": "Maksimum B-kareler", "transcoding_max_b_frames_description": "Daha yüksek değerler sıkıştırma verimliliğini artırır, ancak kodlamayı yavaşlatır. Eski cihazlarda donanım hızlandırma ile uyumlu olmayabilir. 0, B-çerçevelerini devre dışı bırakır, -1 ise bu değeri otomatik olarak ayarlar.", "transcoding_max_bitrate": "Maksimum bitrate", - "transcoding_max_bitrate_description": "Maksimum bit hızı ayarlamak, kaliteye küçük bir maliyetle dosya boyutlarını daha öngörülebilir hale getirebilir.", + "transcoding_max_bitrate_description": "Maksimum bit hızı ayarlamak, kaliteyi az bir maliyetle düşürerek dosya boyutlarını daha öngörülebilir hale getirebilir. 720p çözünürlükte, tipik değerler VP9 veya HEVC için 2600 kbit/s, H.264 için ise 4500 kbit/s’dir. 0 olarak ayarlanırsa devre dışı bırakılır.", "transcoding_max_keyframe_interval": "Maksimum ana kare aralığı", "transcoding_max_keyframe_interval_description": "Ana kareler arasındaki maksimum kare mesafesini ayarlar. Düşük değerler sıkıştırma verimliliğini kötüleştirir, ancak arama sürelerini iyileştirir ve hızlı hareket içeren sahnelerde kaliteyi artırabilir. 0 bu değeri otomatik olarak ayarlar.", "transcoding_optimal_description": "Hedef çözünürlükten yüksek veya kabul edilen formatta olmayan videolar", @@ -352,14 +381,18 @@ "admin_password": "Yönetici Şifresi", "administration": "Yönetim", "advanced": "Gelişmiş", + "advanced_settings_beta_timeline_subtitle": "Yeni uygulama deneyimini deneyin", + "advanced_settings_beta_timeline_title": "Beta Zaman Çizelgesi", + "advanced_settings_enable_alternate_media_filter_subtitle": "Eşleme sırasında medyayı alternatif ölçütlere göre süzgeçten geçirmek için bu seçeneği kullanın. Uygulamanın tüm albümleri algılamasında sorun yaşıyorsanız yalnızca bu durumda deneyin.", "advanced_settings_enable_alternate_media_filter_title": "[DENEYSEL] Alternatif cihaz albüm eşleme süzgeci kullanın", "advanced_settings_log_level_title": "Günlük düzeyi: {level}", - "advanced_settings_prefer_remote_subtitle": "Bazı cihazlar, cihazdaki öğelerin küçük resimlerini göstermekte çok yavaştır. Bunun yerine sunucudaki küçük resimleri göstermek için bu ayarı etkinleştirin.", + "advanced_settings_prefer_remote_subtitle": "Bazı cihazlar yerel varlıklardan küçük resimleri yüklerken çok yavaş çalışır. Bu ayarı etkinleştirerek uzak görüntüleri yükleyin.", "advanced_settings_prefer_remote_title": "Uzak görüntüleri tercih et", "advanced_settings_proxy_headers_subtitle": "Immich'in her ağ isteğiyle birlikte göndermesi gereken proxy header'ları tanımlayın", "advanced_settings_proxy_headers_title": "Proxy Header'lar", "advanced_settings_self_signed_ssl_subtitle": "Sunucu uç noktası için SSL sertifika doğrulamasını atlar. Kendinden imzalı sertifikalar için gereklidir.", "advanced_settings_self_signed_ssl_title": "Kendi kendine imzalanmış SSL sertifikalarına izin ver", + "advanced_settings_sync_remote_deletions_subtitle": "Web üzerinde işlem yapıldığında, bu aygıttaki varlığı otomatik olarak sil veya geri yükle", "advanced_settings_sync_remote_deletions_title": "Uzaktan silinmeleri eşle [DENEYSEL]", "advanced_settings_tile_subtitle": "Gelişmiş kullanıcı ayarları", "advanced_settings_troubleshooting_subtitle": "Sorun giderme için ek özellikleri etkinleştirin", @@ -372,6 +405,7 @@ "album_cover_updated": "Albüm Kapağı güncellendi", "album_delete_confirmation": "{album} albümünü silmek istediğinize emin misiniz?", "album_delete_confirmation_description": "Albüm paylaşılıyorsa, diğer kullanıcılar artık bu albüme erişemeyecektir.", + "album_deleted": "Albüm silindi", "album_info_card_backup_album_excluded": "HARİÇ", "album_info_card_backup_album_included": "DAHİL", "album_info_updated": "Albüm bilgisi güncellendi", @@ -381,6 +415,7 @@ "album_options": "Albüm seçenekleri", "album_remove_user": "Kullanıcıyı kaldır?", "album_remove_user_confirmation": "{user} kullanıcısını kaldırmak istediğinize emin misiniz?", + "album_search_not_found": "Aramanızla eşleşen albüm bulunamadı", "album_share_no_users": "Görünüşe göre bu albümü tüm kullanıcılarla paylaştınız veya paylaşacak herhangi bir başka kullanıcınız yok.", "album_updated": "Albüm güncellendi", "album_updated_setting_description": "Paylaşılan bir albüme yeni bir varlık eklendiğinde email bildirimi alın", @@ -392,11 +427,15 @@ "album_viewer_appbar_share_err_remove": "Albümden öğeleri kaldırmada sorunlar var", "album_viewer_appbar_share_err_title": "Albüm başlığı değiştirilemedi", "album_viewer_appbar_share_leave": "Albümden çık", - "album_viewer_appbar_share_to": "Paylaş:", + "album_viewer_appbar_share_to": "Paylaşma", "album_viewer_page_share_add_users": "Kullanıcı ekle", "album_with_link_access": "Link'e sahip olan herhangi bir kişinin bu albümdeki fotoğrafları ve kişileri görmesine izin ver.", "albums": "Albümler", "albums_count": "{count, plural, one {{count, number} Albüm} other {{count, number} Albüm}}", + "albums_default_sort_order": "Varsayılan albüm sıralama düzeni", + "albums_default_sort_order_description": "Yeni albüm oluştururken kullanılacak başlangıç varlık sıralama düzeni.", + "albums_feature_description": "Diğer kullanıcılarla paylaşılabilen varlık koleksiyonları.", + "albums_on_device_count": "Cihazdaki albümler ({count})", "all": "Tümü", "all_albums": "Tüm Albümler", "all_people": "Tüm Kişiler", @@ -417,6 +456,7 @@ "app_settings": "Uygulama Ayarları", "appears_in": "Şurada görünür", "archive": "Arşiv", + "archive_action_prompt": "{count} arşive eklendi", "archive_or_unarchive_photo": "Fotoğrafı arşivle/arşivden çıkar", "archive_page_no_archived_assets": "Arşivlenmiş öğe bulunamadı", "archive_page_title": "Arşiv ({count})", @@ -454,10 +494,12 @@ "assets": "Varlıklar", "assets_added_count": "{count, plural, one {# varlık eklendi} other {# varlık eklendi}}", "assets_added_to_album_count": "{count, plural, one {# varlık} other {# varlık}} albüme eklendi", - "assets_added_to_name_count": "{count, plural, one {# varlık} other {# varlık}} {hasName, select, true {{name}} other {yeni albüm}} içine eklendi", + "assets_cannot_be_added_to_album_count": "{count, plural, one {Varlık} other {Varlıklar}} albüme eklenemiyor", "assets_count": "{count, plural, one {# varlık} other {# varlıklar}}", "assets_deleted_permanently": "{count} öğe kalıcı olarak silindi", "assets_deleted_permanently_from_server": "{count} öğe kalıcı olarak Immich sunucusundan silindi", + "assets_downloaded_failed": "{count, plural, one {# dosya indirildi – {error} dosya indirilemedi} other {# dosya indirildi – {error} dosya indirilemedi}}", + "assets_downloaded_successfully": "{count, plural, one {# dosya başarıyla indirildi} other {# dosya başarıyla indirildi}}", "assets_moved_to_trash_count": "{count, plural, one {# varlık} other {# varlık}} çöpe taşındı", "assets_permanently_deleted_count": "Kalıcı olarak silindi {count, plural, one {# varlık} other {# varlıklar}}", "assets_removed_count": "Kaldırıldı {count, plural, one {# varlık} other {# varlıklar}}", @@ -477,6 +519,7 @@ "back_close_deselect": "Geri, kapat veya seçimi kaldır", "background_location_permission": "Arka plan konum izni", "background_location_permission_content": "Arka planda çalışırken ağ değiştirmek için Immich'in *her zaman* tam konum erişimine sahip olması gerekir, böylece uygulama Wi-Fi ağının adını okuyabilir", + "backup": "Yedekle", "backup_album_selection_page_albums_device": "Cihazdaki albümler ({count})", "backup_album_selection_page_albums_tap": "Seçmek için dokunun, hariç tutmak için çift dokunun", "backup_album_selection_page_assets_scatter": "Varlıklar birden fazla albüme dağılabilir. Bu nedenle, yedekleme işlemi sırasında albümler dahil edilebilir veya hariç tutulabilir.", @@ -484,12 +527,12 @@ "backup_album_selection_page_selection_info": "Seçim Bilgileri", "backup_album_selection_page_total_assets": "Toplam eşsiz öğeler", "backup_all": "Tümü", - "backup_background_service_backup_failed_message": "Yedekleme başarısız. Tekrar deneniyor...", - "backup_background_service_connection_failed_message": "Sunucuya bağlanılamadı. Tekrar deneniyor...", + "backup_background_service_backup_failed_message": "Yedekleme başarısız. Tekrar deneniyor…", + "backup_background_service_connection_failed_message": "Sunucuya bağlanılamadı. Tekrar deneniyor…", "backup_background_service_current_upload_notification": "{filename} yükleniyor", "backup_background_service_default_notification": "Yeni öğeler kontrol ediliyor…", "backup_background_service_error_title": "Yedekleme hatası", - "backup_background_service_in_progress_notification": "Öğeleriniz yedekleniyor...", + "backup_background_service_in_progress_notification": "Öğeleriniz yedekleniyor…", "backup_background_service_upload_failure_notification": "{filename} yüklemesi başarısız oldu", "backup_controller_page_albums": "Yedekleme Albümleri", "backup_controller_page_background_app_refresh_disabled_content": "Arka planda yedeklemeyi kullanabilmek için Ayarlar > Genel > Arka Planda Uygulama Yenileme bölümünden arka planda uygulama yenilemeyi etkinleştirin.", @@ -540,6 +583,8 @@ "backup_options_page_title": "Yedekleme seçenekleri", "backup_setting_subtitle": "Arka planda ve ön planda yükleme ayarlarını düzenle", "backward": "Geriye doğru", + "beta_sync": "Beta Senkronizasyon Durumu", + "beta_sync_subtitle": "Yeni senkronizasyon sistemini yönetin", "biometric_auth_enabled": "Biyometrik kimlik doğrulama etkin", "biometric_locked_out": "Biyometrik kimlik doğrulaması kilitli", "biometric_no_options": "Biyometrik seçenek yok", @@ -557,7 +602,7 @@ "cache_settings_clear_cache_button": "Önbelleği temizle", "cache_settings_clear_cache_button_title": "Uygulamanın önbelleğini temizleyin. Önbellek yeniden oluşturulana kadar uygulamanın performansını önemli ölçüde etkileyecektir.", "cache_settings_duplicated_assets_clear_button": "TEMİZLE", - "cache_settings_duplicated_assets_subtitle": "Uygulama tarafından kara listeye alınan öğeler", + "cache_settings_duplicated_assets_subtitle": "Uygulama tarafından yok sayılan fotoğraflar ve videolar", "cache_settings_duplicated_assets_title": "Yinelenen Öğeler ({count})", "cache_settings_statistics_album": "Kütüphane küçük resimleri", "cache_settings_statistics_full": "Tam çözünürlükte resimler", @@ -574,9 +619,12 @@ "cancel": "İptal", "cancel_search": "Aramayı iptal et", "canceled": "İptal edildi", + "canceling": "Vazgeçmek", "cannot_merge_people": "Kişiler birleştirilemiyor", "cannot_undo_this_action": "Bu işlem geri alınamaz!", "cannot_update_the_description": "Açıklama güncellenemiyor", + "cast": "Yansıt", + "cast_description": "Kullanılabilir yansıtma hedeflerini yapılandır", "change_date": "Tarihi değiştir", "change_description": "Açıklamayı değiştir", "change_display_order": "Görüntüleme sırasını değiştir", @@ -636,6 +684,7 @@ "confirm_tag_face": "Bu yüzü {name} olarak etiketlemek ister misiniz?", "confirm_tag_face_unnamed": "Bu yüzü etiketlemek ister misin?", "connected_device": "Cihaz bağlandı", + "connected_to": "Bağlı", "contain": "İçermek", "context": "Bağlam", "continue": "Devam et", @@ -644,8 +693,8 @@ "control_bottom_app_bar_delete_from_local": "Cihazdan sil", "control_bottom_app_bar_edit_location": "Konumu Düzenle", "control_bottom_app_bar_edit_time": "Tarih ve Saati Düzenle", - "control_bottom_app_bar_share_link": "İlişimi paylaş", - "control_bottom_app_bar_share_to": "Paylaş:", + "control_bottom_app_bar_share_link": "Bağlantıyı Paylaş", + "control_bottom_app_bar_share_to": "Paylaşma", "control_bottom_app_bar_trash_from_immich": "Çöp Kutusuna At", "copied_image_to_clipboard": "Resim, panoya kopyalandı.", "copied_to_clipboard": "Panoya kopyalandı!", @@ -684,9 +733,11 @@ "current_server_address": "Mevcut sunucu adresi", "custom_locale": "Özel Yerel Ayar", "custom_locale_description": "Tarihleri ve sayıları dile ve bölgeye göre biçimlendirin", + "custom_url": "Özel URL", "daily_title_text_date": "dd MMM E", "daily_title_text_date_year": "dd MMM yyyy E", "dark": "Koyu", + "dark_theme": "Karanlık temaya geç", "date_after": "Sonraki tarih", "date_and_time": "Tarih ve Zaman", "date_before": "Önceki tarih", @@ -702,6 +753,8 @@ "default_locale": "Varsayılan Yerel Ayar", "default_locale_description": "Tarihleri ve sayıları tarayıcınızın yerel ayarına göre biçimlendirin", "delete": "Sil", + "delete_action_confirmation_message": "Bu varlığı silmek istediğinizden emin misiniz? Bu işlem, varlığı sunucunun çöp kutusuna taşıyacak ve yerel olarak silmek isteyip istemediğinizi soracaktır", + "delete_action_prompt": "{count} silindi", "delete_album": "Albümü sil", "delete_api_key_prompt": "Bu API anahtarını silmek istediğinizden emin misiniz?", "delete_dialog_alert": "Bu öğeler cihazınızdan ve Immich'ten kalıcı olarak silinecektir", @@ -715,9 +768,12 @@ "delete_key": "Anahtarı sil", "delete_library": "Kütüphaneyi sil", "delete_link": "Bağlantıyı sil", + "delete_local_action_prompt": "{count} yerel olarak silindi", "delete_local_dialog_ok_backed_up_only": "Sadece Yedeklenmişleri Sil", "delete_local_dialog_ok_force": "Yine de Sil", "delete_others": "Diğerlerini sil", + "delete_permanently": "Kalıcı olarak sil", + "delete_permanently_action_prompt": "{count} kalıcı olarak silindi", "delete_shared_link": "Paylaşılmış linki sil", "delete_shared_link_dialog_title": "Paylaşılan Bağlantı Sil", "delete_tag": "Etiketi sil", @@ -728,12 +784,14 @@ "description": "Açıklama", "description_input_hint_text": "Açıklama ekle...", "description_input_submit_error": "Açıklama güncellenirken hata oluştu, daha fazla ayrıntı için günlüğü kontrol edin", + "deselect_all": "Tümünü Seçimi Kaldır", "details": "Detaylar", "direction": "Yön", "disabled": "Devre dışı bırakıldı", "disallow_edits": "Değişikliklere izin verme", "discord": "Discord", "discover": "Keşfet", + "discovered_devices": "Keşfedilen aygıtlar", "dismiss_all_errors": "Tüm hataları yoksay", "dismiss_error": "Hatayı yoksay", "display_options": "Görüntüleme seçenekleri", @@ -744,6 +802,7 @@ "documentation": "Dokümantasyon", "done": "Bitti", "download": "İndir", + "download_action_prompt": "{count} varlık indiriliyor", "download_canceled": "İndirme iptal edildi", "download_complete": "İndirme tamamlandı", "download_enqueue": "İndirme sıraya alındı", @@ -770,6 +829,7 @@ "edit": "Düzenle", "edit_album": "Albümü düzenle", "edit_avatar": "Avatarı Düzenle", + "edit_birthday": "Doğum Günü Düzenle", "edit_date": "Tarihi Düzenle", "edit_date_and_time": "Tarih ve zamanı düzenleyin", "edit_description": "Açıklamayı düzenle", @@ -781,6 +841,7 @@ "edit_key": "Anahtarı düzenle", "edit_link": "Bağlantıyı düzenle", "edit_location": "Lokasyonu düzenleyin", + "edit_location_action_prompt": "{count} konum düzenlendi", "edit_location_dialog_title": "Konum", "edit_name": "İsmi düzenleyin", "edit_people": "Kişileri düzenle", @@ -799,9 +860,11 @@ "empty_trash": "Çöpü boşalt", "empty_trash_confirmation": "Çöp kutusunu boşaltmak istediğinizden emin misiniz? Bu işlem, Immich'teki çöp kutusundaki tüm varlıkları kalıcı olarak silecektir.\nBu işlemi geri alamazsınız!", "enable": "Etkinleştir", + "enable_backup": "Yedeklemeyi Etkinleştir", "enable_biometric_auth_description": "Biyometrik kimlik doğrulamasını etkinleştirmek için PIN kodu girin", "enabled": "Etkinleştirildi", "end_date": "Bitiş tarihi", + "enqueued": "Kuyruğa alındı", "enter_wifi_name": "Wi-Fi adını girin", "enter_your_pin_code": "Pin kodu girin", "enter_your_pin_code_subtitle": "Kilitli klasöre erişmek için PIN kodunuzu girin", @@ -810,6 +873,7 @@ "error_delete_face": "Yüzü varlıktan silme hatası", "error_loading_image": "Resim yüklenirken hata oluştu", "error_saving_image": "Hata: {error}", + "error_tag_face_bounding_box": "Yüz etiketleme hatası – sınırlayıcı kutu koordinatları alınamadı", "error_title": "Bir Hata Oluştu - Bir şeyler ters gitti", "errors": { "cannot_navigate_next_asset": "Sonraki varlığa geçiş yapılamıyor", @@ -859,6 +923,7 @@ "unable_to_archive_unarchive": "{archived, select, true {Arşivleme} other {Arşivden çıkarma}} işlemi yapılamıyor", "unable_to_change_album_user_role": "Albüm kullanıcı rolü değiştirilemiyor", "unable_to_change_date": "Tarih değiştirilemiyor", + "unable_to_change_description": "Açıklama değiştirilemiyor", "unable_to_change_favorite": "Favori durumu değiştirilemiyor", "unable_to_change_location": "Konum değiştirilemiyor", "unable_to_change_password": "Şifre değiştirilemiyor", @@ -902,6 +967,7 @@ "unable_to_remove_partner": "Ortak kaldırılamıyor", "unable_to_remove_reaction": "Reaksiyon kaldırılamıyor", "unable_to_reset_password": "Şifre sıfırlanamıyor", + "unable_to_reset_pin_code": "Pin kodunu sıfırlanamıyor", "unable_to_resolve_duplicate": "Çiftler çözümlenemiyor", "unable_to_restore_assets": "Varlıklar geri yüklenemiyor", "unable_to_restore_trash": "Çöp geri yüklenemiyor", @@ -931,10 +997,14 @@ }, "exif": "EXIF", "exif_bottom_sheet_description": "Açıklama Ekle...", + "exif_bottom_sheet_description_error": "Açıklama güncelleme hatası", "exif_bottom_sheet_details": "DETAYLAR", "exif_bottom_sheet_location": "KONUM", "exif_bottom_sheet_people": "KİŞİLER", "exif_bottom_sheet_person_add_person": "İsim ekle", + "exif_bottom_sheet_person_age_months": "Yaş: {months} ay", + "exif_bottom_sheet_person_age_year_months": "Yaş: 1 yıl, {months} ay", + "exif_bottom_sheet_person_age_years": "Yaş: {years}", "exit_slideshow": "Slayt gösterisinden çık", "expand_all": "Hepsini genişlet", "experimental_settings_new_asset_list_subtitle": "Çalışmalar devam ediyor", @@ -948,17 +1018,23 @@ "explorer": "Geçmiş", "export": "Dışa Aktar", "export_as_json": "JSON olarak Dışa Aktar", + "export_database": "Veritabanını Dışa Aktar", + "export_database_description": "SQLite veritabanını dışa aktarın", "extension": "Uzantı", "external": "Harici", "external_libraries": "Harici kütüphaneler", "external_network": "Harici ağlar", - "external_network_sheet_info": "Belirlenmiş WiFi ağına bağlı olmadığında uygulama, yukarıdan aşağıya doğru ulaşabileceği aşağıdaki URL'lerden ilki aracılığıyla sunucuya bağlanacaktır", + "external_network_sheet_info": "Belirlenmiş Wi-Fi ağına bağlı olmadığında uygulama, yukarıdan aşağıya doğru ulaşabileceği aşağıdaki URL'lerden ilki aracılığıyla sunucuya bağlanacaktır", "face_unassigned": "Yüz atanmadı", + "failed": "Başarısız", + "failed_to_authenticate": "Kimlik doğrulaması yapılamadı", "failed_to_load_assets": "Varlıklar yüklenemedi", - "favorite": "Favori", - "favorite_or_unfavorite_photo": "Favoriye ekle veya çıkar", - "favorites": "Favoriler", - "favorites_page_no_favorites": "Favori öğe bulunamadı", + "failed_to_load_folder": "Klasör yüklenemedi", + "favorite": "Gözde", + "favorite_action_prompt": "{count} gözdelere eklendi", + "favorite_or_unfavorite_photo": "Gözdeye ekle veya çıkar", + "favorites": "Gözdeler", + "favorites_page_no_favorites": "Gözde öge bulunamadı", "feature_photo_updated": "Özellikli fotoğraf güncellendi", "features": "Özellikler", "features_setting_description": "Uygulamanın özelliklerini yönet", @@ -968,11 +1044,16 @@ "filetype": "Dosya tipi", "filter": "Filtre", "filter_people": "Kişileri filtrele", + "filter_places": "Yerleri süz", "find_them_fast": "Adlarına göre hızlıca bul", "fix_incorrect_match": "Yanlış eşleştirmeyi düzelt", + "folder": "Klasör", + "folder_not_found": "Klasör bulunamadı", "folders": "Klasörler", "folders_feature_description": "Dosya sistemindeki fotoğraf ve videoları klasör görünümüyle keşfedin", "forward": "İleri", + "gcast_enabled": "Google Cast", + "gcast_enabled_description": "Bu özellik, çalışabilmek için Google'dan harici kaynaklar yükler.", "general": "Genel", "get_help": "Yardım Al", "get_wifiname_error": "Wi-Fi adı alınamadı. Gerekli izinleri verdiğinizden ve bir Wi-Fi ağına bağlı olduğunuzdan emin olun", @@ -990,6 +1071,9 @@ "haptic_feedback_switch": "Dokunsal geri bildirimi aç", "haptic_feedback_title": "Dokunsal Geri Bildirim (Haptic Feedback)", "has_quota": "Kota var", + "hash_asset": "Hash varlığı", + "hashed_assets": "Hashlenmiş varlıklar", + "hashing": "Hashleme", "header_settings_add_header_tip": "Header Ekle", "header_settings_field_validator_msg": "Değer boş olamaz", "header_settings_header_name_input": "Header adı", @@ -1012,12 +1096,17 @@ "home_page_building_timeline": "Zaman çizelgesi oluşturuluyor", "home_page_delete_err_partner": "Partner öğeleri silinemez, atlanıyor", "home_page_delete_remote_err_local": "Uzaktan silme seçimindeki yerel öğeler atlanıyor", - "home_page_favorite_err_local": "Yerel öğeler henüz favorilere eklenemiyor, atlanıyor", - "home_page_favorite_err_partner": "Partner öğeleri henüz favorilere eklenemiyor, atlanıyor", + "home_page_favorite_err_local": "Yerel ögeler henüz gözdelere eklenemiyor, atlanıyor", + "home_page_favorite_err_partner": "Ortak ögeleri henüz gözdelere eklenemiyor, atlanıyor", "home_page_first_time_notice": "Uygulamayı ilk kez kullanıyorsanız, zaman çizelgesinin albümlerdeki fotoğraf ve videolar ile oluşturulabilmesi için lütfen yedekleme için albüm(ler) seçtiğinizden emin olun.", + "home_page_locked_error_local": "Yerel varlıklar kilitli klasöre taşınamıyor, atlanıyor", + "home_page_locked_error_partner": "Ortak varlıklar kilitli klasöre taşınamıyor, atlanıyor", "home_page_share_err_local": "Yerel öğeler bağlantı ile paylaşılamaz, atlanıyor", "home_page_upload_err_limit": "Aynı anda en fazla 30 öğe yüklenebilir, atlanabilir", + "host": "Ana bilgisayar", "hour": "Saat", + "id": "ID", + "idle": "Boşta", "ignore_icloud_photos": "iCloud Fotoğraflarını Yok Say", "ignore_icloud_photos_description": "iCloud'a yüklenmiş fotoğraflar Immich sunucusuna yüklenmesin", "image": "Resim", @@ -1057,6 +1146,12 @@ "invalid_date_format": "Geçersiz tarih formatı", "invite_people": "Kişileri Davet Et", "invite_to_album": "Albüme davet et", + "ios_debug_info_fetch_ran_at": "Veri çekme {dateTime} tarihinde çalıştırıldı", + "ios_debug_info_last_sync_at": "Son eşleme: {dateTime}", + "ios_debug_info_no_processes_queued": "Hiçbir arka plan işlemi kuyruğa alınmadı", + "ios_debug_info_no_sync_yet": "Henüz hiçbir arka plan eşleme görevi çalıştırılmadı", + "ios_debug_info_processes_queued": "{count, plural, one {{count} arka plan işlemi kuyruğa alındı} other {{count} arka plan işlemi kuyruğa alındı}}", + "ios_debug_info_processing_ran_at": "İşleme {dateTime} tarihinde çalıştırıldı", "items_count": "{count, plural, one {# Öğe} other {# Öğe}}", "jobs": "Görevler", "keep": "Koru", @@ -1065,7 +1160,11 @@ "kept_this_deleted_others": "Bu varlık tutuldu ve {count, plural, one {# varlık} other {# varlık}} silindi", "keyboard_shortcuts": "Klavye kısayolları", "language": "Dil", + "language_no_results_subtitle": "Arama teriminizi değiştirmeyi deneyin", + "language_no_results_title": "Dil bulunamadı", + "language_search_hint": "Dilleri ara...", "language_setting_description": "Tercih ettiğiniz dili seçiniz", + "large_files": "Büyük Dosyalar", "last_seen": "Son görülme", "latest_version": "En son versiyon", "latitude": "Enlem", @@ -1081,26 +1180,32 @@ "library_page_sort_created": "Oluşturma tarihi", "library_page_sort_last_modified": "Son düzenleme", "library_page_sort_title": "Albüm başlığı", + "licenses": "Lisanslar", "light": "Açık", "like_deleted": "Beğeni silindi", "link_motion_video": "Hareket videosunu bağla", - "link_options": "Bağlantı seçenekleri", "link_to_oauth": "OAuth'a bağla", "linked_oauth_account": "Bağlı OAuth hesabı", "list": "Liste", "loading": "Yükleniyor", "loading_search_results_failed": "Arama sonuçları yüklenemedi", + "local": "Yerel", + "local_asset_cast_failed": "Sunucuya yüklenmemiş bir varlık yansıtılamaz", + "local_assets": "Yerel Varlıklar", "local_network": "Yerel Wi-Fi", "local_network_sheet_info": "Uygulama belirlenmiş Wi-Fi ağını kullanırken bu URL üzerinden sunucuya bağlanacaktır", "location_permission": "Konum izni", - "location_permission_content": "Otomatik geçiş özelliğinin çalışabilmesi için Immich'in mevcut Wi-Fi ağının adını bilmesi, bunu sağlamak için de tam konum iznine ihtiyacı vardır.", + "location_permission_content": "Otomatik geçiş özelliğinin çalışabilmesi için Immich'in mevcut Wi-Fi ağının adını bilmesi, bunu sağlamak için de tam konum iznine ihtiyacı vardır", "location_picker_choose_on_map": "Haritada seç", "location_picker_latitude_error": "Geçerli bir enlem yazın", "location_picker_latitude_hint": "Buraya enlem yazın", "location_picker_longitude_error": "Geçerli bir boylam yazın", "location_picker_longitude_hint": "Buraya boylam yazın", + "lock": "Kilitle", + "locked_folder": "Kilitli Klasör", "log_out": "Oturumu kapat", "log_out_all_devices": "Tüm Cihazlarda Oturumu Kapat", + "logged_in_as": "{user} olarak oturum açıldı", "logged_out_all_devices": "Tüm cihazlarda oturum kapatıldı", "logged_out_device": "Oturum kapatılmış cihaz", "login": "Giriş yap", @@ -1124,7 +1229,7 @@ "login_form_server_empty": "Sunucu URL'si girin", "login_form_server_error": "Sunucuya bağlanılamadı.", "login_has_been_disabled": "Giriş devre dışı bırakıldı.", - "login_password_changed_error": "Parola güncellenirken bir hata oluştu.", + "login_password_changed_error": "Parolanız güncellenirken bir hata oluştu.", "login_password_changed_success": "Parola güncellendi", "logout_all_device_confirmation": "Tüm cihazlarda oturum kapatmak istediğinizden emin misiniz?", "logout_this_device_confirmation": "Bu cihazda oturum kapatmak istediğinizden emin misiniz?", @@ -1133,6 +1238,7 @@ "loop_videos": "Videoları döngüye al", "loop_videos_description": "Ayrıntı görünümünde videoların otomatik döngüye alınmasını etkinleştir.", "main_branch_warning": "Geliştirme sürümü kullanıyorsunuz. Yayınlanan bir sürüm kullanmanızı önemle tavsiye ederiz!", + "main_menu": "Ana menü", "make": "Marka", "manage_shared_links": "Paylaşılan bağlantıları yönet", "manage_sharing_with_partners": "Ortaklarla paylaşımı yönet", @@ -1142,7 +1248,6 @@ "manage_your_devices": "Cihazlarınızı yönetin", "manage_your_oauth_connection": "OAuth bağlantınızı yönetin", "map": "Harita", - "map_assets_in_bound": "{count} fotoğraf", "map_assets_in_bounds": "{count} fotoğraf", "map_cannot_get_user_location": "Kullanıcının konumu alınamıyor", "map_location_dialog_yes": "Evet", @@ -1163,9 +1268,12 @@ "map_settings_dialog_title": "Harita Ayarları", "map_settings_include_show_archived": "Arşivdekileri dahil et", "map_settings_include_show_partners": "Partnerleri Dahil Et", - "map_settings_only_show_favorites": "Sadece Favorileri Göster", + "map_settings_only_show_favorites": "Sadece Gözdeleri Göster", "map_settings_theme_settings": "Harita Teması", "map_zoom_to_see_photos": "Fotoğrafları görmek için uzaklaştırın", + "mark_all_as_read": "Tümünü okundu olarak işaretle", + "mark_as_read": "Okundu olarak işaretle", + "marked_all_as_read": "Tümü okundu olarak işaretlendi", "matches": "Eşleşenler", "media_type": "Medya türü", "memories": "Anılar", @@ -1186,8 +1294,17 @@ "minimize": "Küçült", "minute": "Dakika", "missing": "Eksik", + "model": "Model", "month": "Ay", + "monthly_title_text_date_format": "MMMM y", "more": "Daha fazla", + "move": "Taşı", + "move_off_locked_folder": "Kilitli klasörden taşı", + "move_to_lock_folder_action_prompt": "{count} kilitli klasöre eklendi", + "move_to_locked_folder": "Kilitli klasöre taşı", + "move_to_locked_folder_confirmation": "Bu fotoğraflar ve videolar tüm albümlerden kaldırılacak ve yalnızca kilitli klasörden görüntülenebilecektir", + "moved_to_archive": "{count, plural, one {# öğe arşive taşındı} other {# öğe arşive taşındı}}", + "moved_to_library": "{count, plural, one {# öğe kitaplığa taşındı} other {# öğe kitaplığa taşındı}}", "moved_to_trash": "Çöp kutusuna taşındı", "multiselect_grid_edit_date_time_err_read_only": "Salt okunur öğelerin tarihi düzenlenemedi, atlanıyor", "multiselect_grid_edit_gps_err_read_only": "Salt okunur öğelerin konumu düzenlenemedi, atlanıyor", @@ -1203,6 +1320,7 @@ "new_password": "Yeni şifre", "new_person": "Yeni kişi", "new_pin_code": "Yeni PIN kodu", + "new_pin_code_subtitle": "Kilitli klasöre ilk kez erişiyorsunuz. Bu sayfaya güvenli erişim için bir PIN kodu oluşturun", "new_user_created": "Yeni kullanıcı oluşturuldu", "new_version_available": "YENİ VERSİYON MEVCUT", "newest_first": "Önce en yeniler", @@ -1215,19 +1333,26 @@ "no_archived_assets_message": "Fotoğraf görünümünüzden kaldırmak için fotoğrafları ve videoları arşivleyin", "no_assets_message": "İLK FOTOĞRAFINIZI YÜKLEMEK İÇİN TIKLAYIN", "no_assets_to_show": "Gösterilecek öğe yok", + "no_cast_devices_found": "Yansıtılacak cihaz bulunamadı", "no_duplicates_found": "Çift bulunamadı.", "no_exif_info_available": "EXIF bilgisi mevcut değil", "no_explore_results_message": "Koleksiyonunuzu keşfetmek için daha fazla fotoğraf yükleyin.", - "no_favorites_message": "En sevdiğiniz fotoğraf ve videoları hızlıca bulmak için favoriler ekleyin", + "no_favorites_message": "En sevdiğiniz fotoğraf ve videoları hızlıca bulmak için gözdelere ekleyin", "no_libraries_message": "Fotoğraf ve videolarınızı görmek için bir harici kütüphane oluşturun", + "no_locked_photos_message": "Kilitli klasördeki fotoğraf ve videolar gizlidir; kitaplığınızda gezinirken veya arama yaparken görünmezler.", "no_name": "İsim yok", + "no_notifications": "Bildirim yok", + "no_people_found": "Eşleşen kişi bulunamadı", "no_places": "Yer yok", "no_results": "Sonuç bulunamadı", "no_results_description": "Eş anlamlı ya da daha genel anlamlı bir kelime deneyin", "no_shared_albums_message": "Fotoğrafları ve videoları ağınızdaki kişilerle paylaşmak için bir albüm oluşturun", + "no_uploads_in_progress": "Yükleme işlemi yok", "not_in_any_album": "Hiçbir albümde değil", + "not_selected": "Seçilmedi", "note_apply_storage_label_to_previously_uploaded assets": "Not: Daha önce yüklenen varlıklar için bir depolama yolu etiketi uygulamak üzere şunu başlatın", "notes": "Notlar", + "nothing_here_yet": "Burada henüz bir şey yok", "notification_permission_dialog_content": "Bildirimleri etkinleştirmek için cihaz ayarlarına gidin ve izin verin.", "notification_permission_list_tile_content": "Bildirimleri etkinleştirmek için izin verin.", "notification_permission_list_tile_enable_button": "Bildirimleri Etkinleştir", @@ -1235,17 +1360,22 @@ "notification_toggle_setting_description": "E-posta bildirimlerine izin ver", "notifications": "Bildirimler", "notifications_setting_description": "Bildirimleri yönetin", + "oauth": "OAuth", "official_immich_resources": "Resmi Immich Kaynakları", "offline": "Çevrim dışı", "ok": "Tamam", "oldest_first": "Eski olan önce", "on_this_device": "Bu cihazda", "onboarding": "Uyum Süreci", - "onboarding_privacy_description": "Şu (isteğe bağlı) özellikler harici hizmetlere dayanır ve yönetim ayarlarından herhangi bir zamanda devre dışı bırakılabilir.", + "onboarding_locale_description": "Tercih ettiğiniz dili seçin. Bu ayarı daha sonra değiştirebilirsiniz.", + "onboarding_privacy_description": "Şu (isteğe bağlı) özellikler harici hizmetlere dayanır ve ayarlardan herhangi bir zamanda devre dışı bırakılabilir.", + "onboarding_server_welcome_description": "Örneğinizi bazı yaygın ayarlarla ayarlayalım.", "onboarding_theme_description": "İnstance’ınız için bir renk teması seçin. Bunu daha sonra ayarlarınızdan değiştirebilirsiniz.", + "onboarding_user_welcome_description": "Haydi başlayalım!", "onboarding_welcome_user": "Hoş geldin, {user}", "online": "Çevrimiçi", - "only_favorites": "Sadece favoriler", + "only_favorites": "Sadece gözdeler", + "open": "Aç", "open_in_map_view": "Harita görünümünde aç", "open_in_openstreetmap": "OpenStreetMap'te Aç", "open_the_search_filters": "Arama filtrelerini aç", @@ -1255,6 +1385,7 @@ "original": "orijinal", "other": "Diğer", "other_devices": "Diğer cihazlar", + "other_entities": "Diğer kuruluşlar", "other_variables": "Diğer değişkenler", "owned": "Sahip olunan", "owner": "Sahip", @@ -1268,7 +1399,7 @@ "partner_page_no_more_users": "Eklenecek başka kullanıcı yok", "partner_page_partner_add_failed": "Partner eklenemedi", "partner_page_select_partner": "Partner seç", - "partner_page_shared_to_title": "Paylaşıldı:", + "partner_page_shared_to_title": "Paylaşıldı", "partner_page_stop_sharing_content": "{partner} artık fotoğraflarınıza erişemeyecek.", "partner_sharing": "Ortak paylaşımı", "partners": "Ortaklar", @@ -1298,15 +1429,18 @@ "permanently_delete_assets_prompt": "Bu {count, plural, one {dosyayı} other {# dosyaları}} kalıcı olarak silmek istediğinizden emin misiniz? Bu işlem {count, plural, one {bu dosyayı} other {bu dosyaları}} albümlerinizden de kaldırır.", "permanently_deleted_asset": "Kalıcı olarak silinmiş ögeler", "permanently_deleted_assets_count": "{count, plural, one {# dosya} other {# dosya}} kalıcı olarak silindi", + "permission": "İzin", + "permission_empty": "İzniniz boş olmamalı", "permission_onboarding_back": "Geri", "permission_onboarding_continue_anyway": "Yine de devam et", "permission_onboarding_get_started": "Haydi başlayalım", "permission_onboarding_go_to_settings": "Ayarlara git", "permission_onboarding_permission_denied": "İzin reddedildi. Immich'i kullanmak için Ayarlar'da fotoğraf ve video izinlerini verin.", - "permission_onboarding_permission_granted": "İzin verildi. Artık hazırsınız!", + "permission_onboarding_permission_granted": "İzin verildi! Artık hazırsınız.", "permission_onboarding_permission_limited": "Sınırlı izin. Immich'in tüm fotoğrav ve videolarınızı yedeklemesine ve yönetmesine izin vermek için Ayarlar'da fotoğraf ve video izinlerini verin.", "permission_onboarding_request": "Immich'in fotoğraflarınızı ve videolarınızı görüntüleyebilmesi için izne ihtiyacı var.", "person": "Kişi", + "person_birthdate": "{date} tarihinde doğdu", "person_hidden": "{name}{hidden, select, true { (gizli)} other {}}", "photo_shared_all_users": "Fotoğraflarınızı tüm kullanıcılarla paylaştınız gibi görünüyor veya paylaşacak kullanıcı bulunmuyor.", "photos": "Fotoğraflar", @@ -1317,6 +1451,7 @@ "pin_code_changed_successfully": "PIN kodu başarıyla değiştirildi", "pin_code_reset_successfully": "PIN kodu başarıyla sıfırlandı", "pin_code_setup_successfully": "PIN kodu başarıyla ayarlandı", + "pin_verification": "PIN kodu doğrulama", "place": "Konum", "places": "Konumlar", "places_count": "{count, plural, one {{count, number} yer} other {{count, number} yer}}", @@ -1324,19 +1459,26 @@ "play_memories": "Anıları oynat", "play_motion_photo": "Hareketli fotoğrafı oynat", "play_or_pause_video": "Videoyu oynat ya da durdur", + "please_auth_to_access": "Erişim için lütfen kimliğinizi doğrulayın", + "port": "Port", "preferences_settings_subtitle": "Uygulama tercihlerini düzenle", "preferences_settings_title": "Tercihler", "preset": "Ön ayar", "preview": "Önizleme", "previous": "Önceki", "previous_memory": "Önceki anı", - "previous_or_next_photo": "Önceki ya da sonraki fotoğraf", + "previous_or_next_day": "Gün ileri/geri", + "previous_or_next_month": "Ay ileri/geri", + "previous_or_next_photo": "Fotoğraf ileri/geri", + "previous_or_next_year": "Yıl ileri/geri", "primary": "Birincil", "privacy": "Gizlilik", + "profile": "Profil", "profile_drawer_app_logs": "Günlükler", "profile_drawer_client_out_of_date_major": "Mobil uygulama güncel değil. Lütfen en son ana sürüme güncelleyin.", "profile_drawer_client_out_of_date_minor": "Mobil uygulama güncel değil. Lütfen en son sürüme güncelleyin.", "profile_drawer_client_server_up_to_date": "Uygulama ve sunucu güncel", + "profile_drawer_github": "GitHub", "profile_drawer_server_out_of_date_major": "Sunucu güncel değil. Lütfen en son ana sürüme güncelleyin.", "profile_drawer_server_out_of_date_minor": "Sunucu güncel değil. Lütfen en son sürüme güncelleyin.", "profile_image_of_user": "{user} kullanıcısının profil resmi", @@ -1363,7 +1505,7 @@ "purchase_lifetime_description": "Ömür boyu geçerli", "purchase_option_title": "SATIN ALMA SEÇENEKLERİ", "purchase_panel_info_1": "Immich'in gelişimi zaman ve çaba gerektiriyor ve tam zamanlı geliştiricilerimiz var. Amacımız, açık kaynak yazılımı sürdürülebilir bir gelir kaynağı haline getirmek.", - "purchase_panel_info_2": "Bu satın alma işlemi Immich'te ek işlevsellik açmayacak. Immich'in gelişimini desteklemek için size güveniyoruz.", + "purchase_panel_info_2": "Ücretli özellikler (paywall) eklememeye kararlı olduğumuz için, bu satın alma işlemi Immich'te ek işlevsellik sağlamaz. Immich'in sürekli gelişimini desteklemek için sizin gibi kullanıcılara güveniyoruz.", "purchase_panel_title": "Projeyi destekleyin", "purchase_per_server": "Sunucu başına", "purchase_per_user": "Kullanıcı başına", @@ -1375,6 +1517,7 @@ "purchase_server_description_2": "Destekçi statüsü", "purchase_server_title": "Sunucu", "purchase_settings_server_activated": "Sunucu ürün anahtarı, yönetici tarafından yönetilir", + "queue_status": "Sırada {count}/{total}", "rating": "Derecelendirme", "rating_clear": "Derecelendirmeyi temizle", "rating_count": "{count, plural, one {# yıldız} other {# yıldız}}", @@ -1390,6 +1533,8 @@ "recent_searches": "Son aramalar", "recently_added": "Son eklenenler", "recently_added_page_title": "Son Eklenenler", + "recently_taken": "Son çekilenler", + "recently_taken_page_title": "Son Çekilenler", "refresh": "Yenile", "refresh_encoded_videos": "Kodlanmış videoları yenile", "refresh_faces": "Yüzleri yenile", @@ -1401,6 +1546,8 @@ "refreshing_faces": "Yüzler yenileniyor", "refreshing_metadata": "Meta veriler yenileniyor", "regenerating_thumbnails": "Küçük resimler yeniden oluşturuluyor", + "remote": "Uzaktan", + "remote_assets": "Uzak Varlıklar", "remove": "Kaldır", "remove_assets_album_confirmation": "{count, plural, one {# dosyayı} other {# dosyayı}} albümden çıkarmak istediğinizden emin misiniz?", "remove_assets_shared_link_confirmation": "{count, plural, one {# dosyayı} other {# dosyayı}} bu paylaşılan bağlantıdan çıkarmak istediğinizden emin misiniz?", @@ -1408,14 +1555,21 @@ "remove_custom_date_range": "Özel tarih aralığını kaldır", "remove_deleted_assets": "Çevrimdışı dosyaları kaldır", "remove_from_album": "Albümden çıkar", - "remove_from_favorites": "Favorilerden çıkar", + "remove_from_album_action_prompt": "{count} albümden kaldırıldı", + "remove_from_favorites": "Gözdelerden çıkar", + "remove_from_lock_folder_action_prompt": "{count} kilitli klasörden kaldırıldı", + "remove_from_locked_folder": "Kilitli klasörden kaldır", + "remove_from_locked_folder_confirmation": "Bu fotoğraf ve videoları kilitli klasörden çıkarmak istediğinizden emin misiniz? Çıkarıldıklarında kitaplığınızda görünür olacaklar.", "remove_from_shared_link": "Paylaşılan bağlantıdan çıkar", + "remove_memory": "Anıyı kaldır", + "remove_photo_from_memory": "Bu anıdan fotoğrafı kaldır", + "remove_tag": "Etiketi kaldır", "remove_url": "Bağlantıyı kaldır", "remove_user": "Kullanıcıyı çıkar", "removed_api_key": "API anahtarı {name} kaldırıldı", "removed_from_archive": "Arşivden çıkarıldı", - "removed_from_favorites": "Favorilerden kaldırıldı", - "removed_from_favorites_count": "{count, plural, other {#}} favorilerden çıkarıldı", + "removed_from_favorites": "Gözdelerden kaldırıldı", + "removed_from_favorites_count": "{count, plural, other {#}} gözdelerden çıkarıldı", "removed_memory": "Anı kaldırıldı", "removed_photo_from_memory": "Fotoğraf anıdan kaldırıldı", "removed_tagged_assets": "{count, plural, one {# dosya} other {# dosya}} etiketleri kaldırıldı", @@ -1431,19 +1585,25 @@ "reset_password": "Şifreyi sıfırla", "reset_people_visibility": "Kişilerin görünürlüğünü sıfırla", "reset_pin_code": "PIN kodunu sıfırlayın", + "reset_sqlite": "SQLite Veritabanını Sıfırla", + "reset_sqlite_confirmation": "SQLite veritabanını sıfırlamak istediğinizden emin misiniz? Verileri yeniden senkronize etmek için oturumu kapatıp tekrar oturum açmanız gerekecektir", + "reset_sqlite_success": "SQLite veritabanını başarıyla sıfırladınız", "reset_to_default": "Varsayılana sıfırla", "resolve_duplicates": "Çiftleri çöz", "resolved_all_duplicates": "Tüm çiftler çözüldü", "restore": "Geri yükle", "restore_all": "Tümünü geri yükle", + "restore_trash_action_prompt": "{count} çöp kutusundan geri yüklendi", "restore_user": "Kullanıcıyı geri yükle", "restored_asset": "Dosya geri yüklendi", "resume": "Devam et", "retry_upload": "Yeniden yüklemeyi dene", "review_duplicates": "Çiftleri gözden geçir", + "review_large_files": "Büyük dosyaları inceleyin", "role": "Rol", "role_editor": "Düzenleyici", "role_viewer": "Görüntüleyici", + "running": "Çalışıyor", "save": "Kaydet", "save_to_gallery": "Fotoğraflar'a kaydet", "saved_api_key": "API anahtarı kaydedildi", @@ -1460,7 +1620,7 @@ "search_by_context": "Bağlama göre ara", "search_by_description": "Açıklamaya göre ara", "search_by_description_example": "Sapa'da yürüyüş günü", - "search_by_filename": "Dosya adına göre ara", + "search_by_filename": "Dosya adına veya uzantısına göre ara", "search_by_filename_example": "Örn. IMG_1234.JPG veya PNG", "search_camera_make": "Kamera markasına göre ara...", "search_camera_model": "Kamera modeline göre ara...", @@ -1473,6 +1633,7 @@ "search_filter_date_title": "Tarih aralığı seç", "search_filter_display_option_not_in_album": "Albümde değil", "search_filter_display_options": "Görüntü Seçenekleri", + "search_filter_filename": "Dosya adına göre ara", "search_filter_location": "Konum", "search_filter_location_title": "Konum seç", "search_filter_media_type": "Medya Türü", @@ -1480,8 +1641,10 @@ "search_filter_people_title": "Kişi seç", "search_for": "Araştır", "search_for_existing_person": "Mevcut bir kişiyi ara", + "search_no_more_result": "Daha fazla sonuç yok", "search_no_people": "Kişi yok", "search_no_people_named": "\"{name}\" isimli bir kişi yok", + "search_no_result": "Sonuç bulunamadı. Farklı bir arama terimi veya kombinasyon deneyin", "search_options": "Arama seçenekleri", "search_page_categories": "Kategoriler", "search_page_motion_photos": "Canlı Fotoğraflar", @@ -1509,9 +1672,11 @@ "searching_locales": "Yerleri arıyor...", "second": "Saniye", "see_all_people": "Tüm kişileri gör", + "select": "Seç", "select_album_cover": "Albüm kapağı seç", "select_all": "Tümünü seç", "select_all_duplicates": "Tüm çiftleri seç", + "select_all_in": "{group} içindekilerin tümünü seç", "select_avatar_color": "Avatar rengini seç", "select_face": "Yüzü seç", "select_featured_photo": "Öne çıkan fotoğrafı seç", @@ -1519,6 +1684,7 @@ "select_keep_all": "Hepsini sakla", "select_library_owner": "Kütüphane sahibini seç", "select_new_face": "Yeni yüz seç", + "select_person_to_tag": "Etiketlemek için bir kişi seçin", "select_photos": "Fotoğrafları seç", "select_trash_all": "Hepsini çöpe at", "select_user_for_sharing_page_err_album": "Albüm oluşturulamadı", @@ -1531,6 +1697,7 @@ "server_info_box_server_url": "Sunucu URL", "server_offline": "Sunucu çevrimdışı", "server_online": "Sunucu çevrimiçi", + "server_privacy": "Sunucu Gizliliği", "server_stats": "Sunucu istatistikleri", "server_version": "Sunucu versiyonu", "set": "Ayarla", @@ -1540,6 +1707,7 @@ "set_date_of_birth": "Doğum tarihini ayarla", "set_profile_picture": "Profil resmini ayarla", "set_slideshow_to_fullscreen": "Slayt gösterisini tam ekran yap", + "set_stack_primary_asset": "Birincil varlık olarak ayarla", "setting_image_viewer_help": "Görüntüleyici önce küçük resmi gösterir, ardından orta boy önizlemeyi (etkinleştirilmişse) ve son olarak orijinali (etkinleştirilmişse) gösterir.", "setting_image_viewer_original_subtitle": "Orijinal tam çözünürlüklü görüntüyü göstermek için etkinleştirin. Veri kullanımını azaltmak için devre dışı bırakın (hem ağ hem de cihaz önbelleği).", "setting_image_viewer_original_title": "Orijinal görüntüyü göster", @@ -1560,14 +1728,18 @@ "setting_notifications_total_progress_subtitle": "Toplam yükleme ilerlemesi (tamamlanan/toplam)", "setting_notifications_total_progress_title": "Arkaplan yedeklemesi toplam ilerlemesini göster", "setting_video_viewer_looping_title": "Döngü", + "setting_video_viewer_original_video_subtitle": "Sunucudan video aktarılırken, transcode (dönüştürülmüş) sürüm mevcut olsa bile orijinal dosya oynatılır. Bu durum, arabelleğe alma (buffering) sorunlarına yol açabilir. Videolar yerel olarak mevcutsa, bu ayardan bağımsız olarak orijinal kalitede oynatılır.", + "setting_video_viewer_original_video_title": "Orijinal videoyu zorla", "settings": "Ayarlar", "settings_require_restart": "Bu ayarı uygulamak için lütfen Immich'i yeniden başlatın", "settings_saved": "Ayarlar kaydedildi", "setup_pin_code": "PIN kodunu ayarlayın", "share": "Paylaş", + "share_action_prompt": "Paylaşılan {count} varlık", "share_add_photos": "Fotoğraf ekle", "share_assets_selected": "{count} seçili", "share_dialog_preparing": "Hazırlanıyor...", + "share_link": "Bağlantıyı Paylaş", "shared": "Paylaşılan", "shared_album_activities_input_disable": "Yoruma kapalı", "shared_album_activity_remove_content": "Bu etkinliği silmek istiyor musunuz?", @@ -1580,10 +1752,12 @@ "shared_by_user": "{user} tarafından paylaşıldı", "shared_by_you": "Senin tarafından paylaşıldı", "shared_from_partner": "{partner} tarafından paylaşılan fotoğraflar", + "shared_intent_upload_button_progress_text": "{current} / {total} Yüklendi", "shared_link_app_bar_title": "Paylaşılan Bağlantılar", "shared_link_clipboard_copied_massage": "Panoya kopyalandı", "shared_link_clipboard_text": "Bağlantı: {link}\nParola: {password}", "shared_link_create_error": "Paylaşım bağlantısı oluşturulurken hata oluştu", + "shared_link_custom_url_description": "Özel bir URL ile bu paylaşılan bağlantıya erişin", "shared_link_edit_description_hint": "Açıklama yazın", "shared_link_edit_expire_after_option_day": "1 gün", "shared_link_edit_expire_after_option_days": "{count} gün", @@ -1606,8 +1780,10 @@ "shared_link_expires_second": "Süresi {count} saniye içinde doluyor", "shared_link_expires_seconds": "{count} sanyei içinde süresi doluyor", "shared_link_individual_shared": "Bireysel paylaşımlı", + "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "Paylaşılan Bağlantıları Yönet", "shared_link_options": "Paylaşılan bağlantı seçenekleri", + "shared_link_password_description": "Bu paylaşılan bağlantıya erişmek için şifre gerektirir", "shared_links": "Paylaşılan bağlantılar", "shared_links_description": "Fotoğraf ve videoları bir bağlantı ile paylaş", "shared_photos_and_videos_count": "{assetCount, plural, one {# paylaşılan fotoğraf veya video.} other {# paylaşılan fotoğraf & video.}}", @@ -1663,6 +1839,7 @@ "sort_title": "Başlık", "source": "Kaynak", "stack": "Yığın", + "stack_action_prompt": "{count} istiflenmiş", "stack_duplicates": "Çiftleri yığınla", "stack_select_one_photo": "Yığın için ana fotoğrafı seç", "stack_selected_photos": "Seçili fotoğrafları yığınla", @@ -1672,14 +1849,17 @@ "start_date": "Başlangıç tarihi", "state": "Eyalet/İl", "status": "Durum", + "stop_casting": "Yansıtmayı durdur", "stop_motion_photo": "Hareketli fotoğrafı durdur", "stop_photo_sharing": "Fotoğraflarınızı paylaşmayı durdurmak mı istiyorsunuz?", "stop_photo_sharing_description": "{partner} artık fotoğraflarınıza erişemeyecek.", "stop_sharing_photos_with_user": "Bu kullanıcı ile fotoğraflarınızı paylaşmayı durdurun", "storage": "Depolama alanı", "storage_label": "Depolama yolu", + "storage_quota": "Depolama Kotası", "storage_usage": "{used} / {available} kullanıldı", "submit": "Gönder", + "success": "Başarılı", "suggestions": "Öneriler", "sunrise_on_the_beach": "Plajda gün doğumu", "support": "Destek", @@ -1689,6 +1869,8 @@ "sync": "Senkronize et", "sync_albums": "Albümleri eşzamanla", "sync_albums_manual_subtitle": "Yüklenmiş fotoğraf ve videoları yedekleme için seçili albümler ile eşzamanlayın", + "sync_local": "Yerel Senkronizasyon", + "sync_remote": "Uzaktan Senkronizasyon", "sync_upload_album_setting_subtitle": "Seçili albümleri Immich'te oluşturun ve içindekileri Immich'e yükleyin.", "tag": "Etiket", "tag_assets": "Dosyaları etiketle", @@ -1699,6 +1881,7 @@ "tag_updated": "Etiket güncellendi: {tag}", "tagged_assets": "{count, plural, one {# dosya} other {# dosya}} etiketlendi", "tags": "Etiketler", + "tap_to_run_job": "Başlatmak için dokunun", "template": "Şablon", "theme": "Tema", "theme_selection": "Tema seçimi", @@ -1714,7 +1897,7 @@ "theme_setting_system_primary_color_title": "Sistem rengini kullan", "theme_setting_system_theme_switch": "Otomatik (sistem ayarına göre)", "theme_setting_theme_subtitle": "Uygulama teması seç", - "theme_setting_three_stage_loading_subtitle": "Üç aşamalı yükleme yükleme performansını artırabilir ancak önemli ölçüde daha yüksek ağ yüküne sebep olur.", + "theme_setting_three_stage_loading_subtitle": "Üç aşamalı yükleme, yükleme performansını artırabilir ancak önemli ölçüde daha yüksek ağ yüküne sebep olur.", "theme_setting_three_stage_loading_title": "Üç aşamalı yüklemeyi etkinleştir", "they_will_be_merged_together": "Birlikte birleştirilecekler", "third_party_resources": "Üçüncü taraf kaynaklar", @@ -1723,7 +1906,7 @@ "timezone": "Zaman dilimi", "to_archive": "Arşivle", "to_change_password": "Şifreyi değiştir", - "to_favorite": "Favorilere ekle", + "to_favorite": "Gözdelere ekle", "to_login": "Oturum aç", "to_parent": "Üst öğeye git", "to_trash": "Çöpe taşı", @@ -1731,6 +1914,7 @@ "total": "Toplam", "total_usage": "Toplam kullanım", "trash": "Çöp", + "trash_action_prompt": "{count} çöp kutusuna taşındı", "trash_all": "Hepsini sil", "trash_count": "Çöp kutusu {count, number}", "trash_delete_asset": "Ögeyi Sil/Çöpe gönder", @@ -1748,8 +1932,11 @@ "unable_to_change_pin_code": "PIN kodu değiştirilemedi", "unable_to_setup_pin_code": "PIN kodu ayarlanamadı", "unarchive": "Arşivden çıkar", + "unarchive_action_prompt": "{count} Arşivden kaldırıldı", "unarchived_count": "{count, plural, other {# arşivden çıkarıldı}}", - "unfavorite": "Favorilerden kaldır", + "undo": "Geri al", + "unfavorite": "Gözdelerden kaldır", + "unfavorite_action_prompt": "{count} Sık Kullanılanlar'dan kaldırıldı", "unhide_person": "Kişiyi göster", "unknown": "Bilinmeyen", "unknown_country": "Bilinmeyen ülke", @@ -1765,29 +1952,43 @@ "unsaved_change": "Kaydedilmemiş değişiklik", "unselect_all": "Tümünü seçimini kaldır", "unselect_all_duplicates": "Tüm çiftlerin seçimini kaldır", + "unselect_all_in": "{group} içindeki tüm seçimleri kaldır", "unstack": "Yığını kaldır", + "unstack_action_prompt": "{count} istiflenmemiş", "unstacked_assets_count": "{count, plural, one {# dosya} other {# dosya}} yığını kaldırıldı", + "untagged": "Etiketlenmemiş", "up_next": "Sıradaki", + "updated_at": "Güncellenme", "updated_password": "Şifreyi güncelle", "upload": "Yükle", + "upload_action_prompt": "{count} yükleme için sıraya alındı", "upload_concurrency": "Yükleme eşzamanlılığı", + "upload_details": "Yükleme Ayrıntıları", "upload_dialog_info": "Seçili öğeleri sunucuya yedeklemek istiyor musunuz?", "upload_dialog_title": "Öğe Yükle", "upload_errors": "{count, plural, one {# hata} other {# hatayla}} yükleme tamamlandı, yeni yüklenen dosyaları görmek için sayfayı güncelleyin.", + "upload_finished": "Yükleme tamamlandı", "upload_progress": "{remaining, number} kalan - {processed, number}/{total, number} işlendi", "upload_skipped_duplicates": "{count, plural, one {# çift dosya} other {# çift dosya}} atlandı", "upload_status_duplicates": "Çiftler", "upload_status_errors": "Hatalar", "upload_status_uploaded": "Yüklendi", "upload_success": "Yükleme başarılı, yüklenen yeni ögeleri görebilmek için sayfayı yenileyin.", + "upload_to_immich": "Immich'e Yükle ({count})", + "uploading": "Yükleniyor", + "uploading_media": "Medya yükleme", + "url": "URL", "usage": "Kullanım", + "use_biometric": "Biyometri kullan", "use_current_connection": "mevcut bağlantıyı kullan", "use_custom_date_range": "Bunun yerine özel tarih aralığını kullan", "user": "Kullanıcı", + "user_has_been_deleted": "Bu kullanıcı silindi.", "user_id": "Kullanıcı ID", "user_liked": "{type, select, photo {Bu fotoğraf} video {Bu video} asset {Bu dosya} other {Bu}} {user} tarafından beğenildi", "user_pin_code_settings": "PIN Kodu", "user_pin_code_settings_description": "PIN kodunuzu yönetin", + "user_privacy": "Kullanıcı Gizliliği", "user_purchase_settings": "Satın Alma", "user_purchase_settings_description": "Satın alma işlemlerini yönet", "user_role_set": "{user}, {role} olarak ayarlandı", @@ -1796,6 +1997,7 @@ "user_usage_stats_description": "hesap kullanım istatistiklerini göster", "username": "Kullanıcı adı", "users": "Kullanıcılar", + "users_added_to_album_count": "Albüme {count, plural, one {# user} other {# users}} eklendi", "utilities": "Yardımcılar", "validate": "Doğrula", "validate_endpoint_error": "Lütfen geçerli bir URL girin", @@ -1805,6 +2007,7 @@ "version_announcement_message": "Merhaba! Immich'in yeni bir sürümü mevcut. Lütfen yapılandırmanızın güncel olduğundan emin olmak için sürüm notlarını okumak için biraz zaman ayırın, özellikle WatchTower veya Immich kurulumunuzu otomatik olarak güncelleyen bir mekanizma kullanıyorsanız yanlış yapılandırmaların önüne geçmek adına bu önemlidir.", "version_history": "Versiyon geçmişi", "version_history_item": "{version}, {date} tarihinde kuruldu", + "video": "Video", "video_hover_setting": "Üzerinde durulduğunda video önizlemesi oynat", "video_hover_setting_description": "Öğe üzerinde fareyle durulduğunda video küçük resmini oynatır. Bu özellik devre dışıyken, oynatma simgesine fareyle gidilerek oynatma başlatılabilir.", "videos": "Videolar", @@ -1813,13 +2016,16 @@ "view_album": "Albümü görüntüle", "view_all": "Tümünü gör", "view_all_users": "Tüm kullanıcıları görüntüle", + "view_details": "Ayrıntıları Görüntüle", "view_in_timeline": "Zaman çizelgesinde görüntüle", "view_link": "Bağlantıyı göster", "view_links": "Bağlantıları göster", "view_name": "Göster", "view_next_asset": "Sonraki dosyayı görüntüle", "view_previous_asset": "Önceki dosyayı görüntüle", + "view_qr_code": "QR kodu görüntüle", "view_stack": "Yığını görüntüle", + "view_user": "Kullanıcıyı Görüntüle", "viewer_remove_from_stack": "Yığından Kaldır", "viewer_stack_use_as_main_asset": "Ana fotoğraf olarak kullan", "viewer_unstack": "Yığını Kaldır", @@ -1830,6 +2036,7 @@ "welcome": "Hoş geldiniz", "welcome_to_immich": "Immich'e hoş geldiniz", "wifi_name": "Wi-Fi Adı", + "wrong_pin_code": "Yanlış PIN kodu", "year": "Yıl", "years_ago": "{years, plural, one {bir yıl} other {# yıl}} önce", "yes": "Evet", diff --git a/i18n/uk.json b/i18n/uk.json index 7b843ceff9..ca5e834991 100644 --- a/i18n/uk.json +++ b/i18n/uk.json @@ -10,14 +10,14 @@ "activity": "Активність", "activity_changed": "Активність {enabled, select, true {увімкнено} other {вимкнено}}", "add": "Додати", - "add_a_description": "Додади опис", + "add_a_description": "Додати опис", "add_a_location": "Додати місцезнаходження", "add_a_name": "Додати ім'я", "add_a_title": "Додати назву", "add_endpoint": "Додати кінцеву точку", - "add_exclusion_pattern": "Додайте шаблон виключення", + "add_exclusion_pattern": "Додати шаблон виключення", "add_import_path": "Додати шлях імпорту", - "add_location": "Додайте місцезнаходження", + "add_location": "Додати місцезнаходження", "add_more_users": "Додати користувачів", "add_partner": "Додати партнера", "add_path": "Додати шлях", @@ -244,6 +244,7 @@ "storage_template_migration_info": "Шаблон зберігання конвертуватиме всі розширення у нижній регістр. Зміни шаблону застосовуватимуться лише до нових ресурсів. Щоб застосувати шаблон до раніше завантажених ресурсів, запустіть {job}.", "storage_template_migration_job": "Завдання міграції шаблону зберігання", "storage_template_more_details": "Для отримання детальнішої інформації про цю функцію, звертайтесь до Шаблону зберігання та його наслідків", + "storage_template_onboarding_description_v2": "Якщо цю функцію увімкнено, файли будуть автоматично впорядковуватися за шаблоном, визначеним користувачем. Докладніше дивіться в документації.", "storage_template_path_length": "Приблизна максимальна довжина шляху: {length, number}/{limit, number}", "storage_template_settings": "Шаблон сховища", "storage_template_settings_description": "Керуйте структурою тек та іменем завантаженого файлу", @@ -463,7 +464,6 @@ "assets": "елементи", "assets_added_count": "Додано {count, plural, one {# ресурс} few {# ресурси} other {# ресурсів}}", "assets_added_to_album_count": "Додано {count, plural, one {# ресурс} few {# ресурси} other {# ресурсів}} до альбому", - "assets_added_to_name_count": "Додано {count, plural, one {# елемент} other {# елементів}} до {hasName, select, true {{name}} other {нового альбому}}", "assets_cannot_be_added_to_album_count": "{count, plural, one {Ресурс} other {Ресурси}} не можна додати до альбому", "assets_count": "{count, plural, one {# ресурс} few {# ресурси} other {# ресурсів}}", "assets_deleted_permanently": "{count} елемент(и) остаточно видалено", @@ -489,6 +489,7 @@ "back_close_deselect": "Повернутися, закрити або скасувати вибір", "background_location_permission": "Дозвіл до місцезнаходження у фоні", "background_location_permission_content": "Щоб перемикати мережі у фоновому режимі, Immich має *завжди* мати доступ до точної геолокації, щоб зчитувати назву Wi-Fi мережі", + "backup": "Резервне копіювання", "backup_album_selection_page_albums_device": "Альбоми на пристрої ({count})", "backup_album_selection_page_albums_tap": "Торкніться, щоб включити, двічі, щоб виключити", "backup_album_selection_page_assets_scatter": "Елементи можуть належати до кількох альбомів водночас. Таким чином, альбоми можуть бути включені або вилучені під час резервного копіювання.", @@ -702,7 +703,6 @@ "daily_title_text_date": "Е, МММ дд", "daily_title_text_date_year": "Е, МММ дд, рррр", "dark": "Темний", - "darkTheme": "Перемкнути темну тему", "date_after": "Дата після", "date_and_time": "Дата і час", "date_before": "Дата до", @@ -1129,7 +1129,6 @@ "light": "Світла", "like_deleted": "Лайк видалено", "link_motion_video": "Посилання на рухоме відео", - "link_options": "Налаштування посилання", "link_to_oauth": "Приєднання до OAuth", "linked_oauth_account": "Приєднаний акаунт OAuth", "list": "Перелік", @@ -1192,7 +1191,6 @@ "manage_your_devices": "Керуйте пристроями, які увійшли в систему", "manage_your_oauth_connection": "Налаштування підключеного OAuth", "map": "Мапа", - "map_assets_in_bound": "{count} фото", "map_assets_in_bounds": "{count} фото", "map_cannot_get_user_location": "Не можу отримати місцезнаходження", "map_location_dialog_yes": "Так", @@ -1567,8 +1565,8 @@ "search_filter_filename": "Пошук за назвою файлу", "search_filter_location": "Місцезнаходження", "search_filter_location_title": "Виберіть місцезнаходження", - "search_filter_media_type": "Тип носія", - "search_filter_media_type_title": "Виберіть тип носія", + "search_filter_media_type": "Тип медіа", + "search_filter_media_type_title": "Виберіть тип медіа", "search_filter_people_title": "Виберіть людей", "search_for": "Шукати для", "search_for_existing_person": "Пошук існуючої особи", diff --git a/i18n/vi.json b/i18n/vi.json index 5890ef87db..ad1738908b 100644 --- a/i18n/vi.json +++ b/i18n/vi.json @@ -461,7 +461,6 @@ "assets": "Các tập tin", "assets_added_count": "Đã thêm {count, plural, one {# mục} other {# mục}}", "assets_added_to_album_count": "Đã thêm {count, plural, one {# mục} other {# mục}} vào album", - "assets_added_to_name_count": "Đã thêm {count, plural, one {# mục} other {# mục}} vào {hasName, select, true {{name}} other {album mới}}", "assets_count": "{count, plural, one {# mục} other {# mục}}", "assets_deleted_permanently": "Đã xoá vĩnh viễn {count} mục", "assets_deleted_permanently_from_server": "Đã xoá vĩnh viễn {count} mục khỏi máy chủ Immich", @@ -486,6 +485,7 @@ "back_close_deselect": "Quay lại, đóng, hoặc bỏ chọn", "background_location_permission": "Quyền truy cập vị trí ở nền", "background_location_permission_content": "Để chuyển đổi mạng khi chạy ở chế độ nền, Immich *luôn* phải có quyền truy cập vị trí chính xác để ứng dụng có thể đọc tên mạng Wi-Fi", + "backup": "Sao lưu", "backup_album_selection_page_albums_device": "Album trên thiết bị ({count})", "backup_album_selection_page_albums_tap": "Nhấn để chọn, nhấn đúp để bỏ qua", "backup_album_selection_page_assets_scatter": "Ảnh có thể có trong nhiều album khác nhau. Trong quá trình sao lưu, bạn có thể chọn để sao lưu tất cả các album hoặc chỉ một số album nhất định.", @@ -534,7 +534,7 @@ "backup_controller_page_start_backup": "Bắt đầu sao lưu", "backup_controller_page_status_off": "Sao lưu tự động khi ứng dụng hoạt động đang tắt", "backup_controller_page_status_on": "Sao lưu tự động khi ứng dụng hoạt động đang bật", - "backup_controller_page_storage_format": "Đã sử dụng {used} của {total}", + "backup_controller_page_storage_format": "Đã dùng {used} của {total}", "backup_controller_page_to_backup": "Các album cần được sao lưu", "backup_controller_page_total_sub": "Tất cả ảnh và video không trùng lập từ các album được chọn", "backup_controller_page_turn_off": "Tắt sao lưu khi ứng dụng hoạt động", @@ -698,7 +698,6 @@ "daily_title_text_date": "E, dd MMM", "daily_title_text_date_year": "E, dd MMM, yyyy", "dark": "Tối", - "darkTheme": "Chuyển đổi chủ đề tối", "date_after": "Ngày sau", "date_and_time": "Ngày và giờ", "date_before": "Ngày trước", @@ -1016,7 +1015,7 @@ "group_year": "Nhóm theo năm", "haptic_feedback_switch": "Bật phản hồi haptic", "haptic_feedback_title": "Phản hồi Hapic", - "has_quota": "Có hạn mức", + "has_quota": "Hạn mức", "header_settings_add_header_tip": "Thêm Header", "header_settings_field_validator_msg": "Trường này không được để trống", "header_settings_header_name_input": "Tên Header", @@ -1124,7 +1123,6 @@ "light": "Sáng", "like_deleted": "Đã xoá thích", "link_motion_video": "Liên kết video chuyển động", - "link_options": "Tùy chọn liên kết", "link_to_oauth": "Liên kết đến OAuth", "linked_oauth_account": "Tài khoản OAuth đã liên kết", "list": "Danh sách", @@ -1185,7 +1183,6 @@ "manage_your_devices": "Quản lý các thiết bị đã đăng nhập của bạn", "manage_your_oauth_connection": "Quản lý kết nối OAuth của bạn", "map": "Bản đồ", - "map_assets_in_bound": "{count} ảnh", "map_assets_in_bounds": "{count} ảnh", "map_cannot_get_user_location": "Không thể xác định vị trí của bạn", "map_location_dialog_yes": "Có", @@ -1426,7 +1423,7 @@ "purchase_button_activate": "Kích hoạt", "purchase_button_buy": "Mua", "purchase_button_buy_immich": "Mua Immich", - "purchase_button_never_show_again": "Không hiển thị lại", + "purchase_button_never_show_again": "Không hiện lại", "purchase_button_reminder": "Nhắc tôi trong 30 ngày", "purchase_button_remove_key": "Xóa khóa", "purchase_button_select": "Chọn", @@ -1504,7 +1501,7 @@ "rename": "Đổi tên", "repair": "Sửa chữa", "repair_no_results_message": "Các tập tin không được theo dõi và bị mất sẽ xuất hiện ở đây", - "replace_with_upload": "Thay thế bằng tập tin tải lên", + "replace_with_upload": "Thay thế tập tin khác", "repository": "Kho lưu trữ", "require_password": "Yêu cầu mật khẩu", "require_user_to_change_password_on_first_login": "Yêu cầu người dùng thay đổi mật khẩu ở lần đầu đăng nhập", @@ -1522,7 +1519,7 @@ "restored_asset": "Ảnh đã được khôi phục", "resume": "Tiếp tục", "retry_upload": "Thử tải lên lại", - "review_duplicates": "Xem xét các mục trùng lặp", + "review_duplicates": "Xem lại các mục trùng lặp", "role": "Vai trò", "role_editor": "Người chỉnh sửa", "role_viewer": "Người xem", @@ -1617,7 +1614,7 @@ "server_info_box_app_version": "Phiên bản ứng dụng", "server_info_box_server_url": "Địa chỉ máy chủ", "server_offline": "Máy chủ ngoại tuyến", - "server_online": "Máy chủ trực tuyến", + "server_online": "Phiên bản", "server_privacy": "Quyền riêng tư máy chủ", "server_stats": "Thống kê máy chủ", "server_version": "Phiên bản máy chủ", @@ -1772,7 +1769,7 @@ "storage": "Bộ nhớ", "storage_label": "Nhãn lưu trữ", "storage_quota": "Giới hạn Dung lượng", - "storage_usage": "Đã sử dụng {used} của {available}", + "storage_usage": "Đã dùng {used} của {available}", "submit": "Gửi", "suggestions": "Gợi ý", "sunrise_on_the_beach": "Bình minh trên bãi biển", @@ -1819,7 +1816,7 @@ "to_change_password": "Đổi mật khẩu", "to_favorite": "Yêu thích", "to_login": "Đăng nhập", - "to_parent": "Đi tới thư mục cha", + "to_parent": "Về thư mục gốc", "to_trash": "Xóa", "toggle_settings": "Chuyển đổi cài đặt", "total": "Tổng cộng", @@ -1894,7 +1891,7 @@ "user_purchase_settings_description": "Quản lý mục mua của bạn", "user_role_set": "Đặt {user} làm {role}", "user_usage_detail": "Chi tiết sử dụng của người dùng", - "user_usage_stats": "Thống kê sử dụng của tài khoản", + "user_usage_stats": "Thống kê sử dụng tài khoản", "user_usage_stats_description": "Xem thống kê sử dụng của tài khoản", "username": "Tên người dùng", "users": "Người dùng", diff --git a/i18n/zh_Hant.json b/i18n/zh_Hant.json index e5a8a65e25..b2acdc02eb 100644 --- a/i18n/zh_Hant.json +++ b/i18n/zh_Hant.json @@ -166,6 +166,19 @@ "metadata_settings_description": "管理詮釋資料設定", "migration_job": "遷移", "migration_job_description": "將項目和臉孔的縮圖移到新的延伸資料夾", + "nightly_tasks_cluster_faces_setting_description": "對新偵測到的臉孔進行辨識", + "nightly_tasks_database_cleanup_setting": "資料庫清理作業", + "nightly_tasks_database_cleanup_setting_description": "清除資料庫中舊的與已過期的資料", + "nightly_tasks_generate_memories_setting": "產生回憶", + "nightly_tasks_generate_memories_setting_description": "從項目建立新回憶", + "nightly_tasks_missing_thumbnails_setting": "產生缺少的縮圖", + "nightly_tasks_missing_thumbnails_setting_description": "將沒有縮圖的項目加入縮圖產生佇列", + "nightly_tasks_settings": "夜間任務設定", + "nightly_tasks_settings_description": "管理夜間任務", + "nightly_tasks_start_time_setting": "開始時間", + "nightly_tasks_start_time_setting_description": "伺服器開始執行夜間任務的時間", + "nightly_tasks_sync_quota_usage_setting": "同步配額使用量", + "nightly_tasks_sync_quota_usage_setting_description": "根據當前使用情況更新使用者儲存配額", "no_paths_added": "未添加路徑", "no_pattern_added": "未添加pattern", "note_apply_storage_label_previous_assets": "*註:執行套用儲存標籤前先上傳項目", @@ -357,6 +370,8 @@ "admin_password": "管理者密碼", "administration": "管理", "advanced": "進階", + "advanced_settings_beta_timeline_subtitle": "立即體驗新版應用程式", + "advanced_settings_beta_timeline_title": "測試版時間軸", "advanced_settings_enable_alternate_media_filter_subtitle": "使用此選項可在同步時依照替代條件篩選媒體。僅當應用程式在偵測所有相簿時出現問題時才建議使用。", "advanced_settings_enable_alternate_media_filter_title": "[實驗]使用其他的裝置相簿同步篩選器", "advanced_settings_log_level_title": "日誌等級:{level}", @@ -379,6 +394,7 @@ "album_cover_updated": "已更新相簿封面", "album_delete_confirmation": "確定要刪除「{album}」(相簿)嗎?", "album_delete_confirmation_description": "如果已分享此相簿,其他使用者就無法再存取這本相簿了。", + "album_deleted": "相簿已刪除", "album_info_card_backup_album_excluded": "已排除", "album_info_card_backup_album_included": "已選中", "album_info_updated": "已更新相簿資訊", @@ -388,6 +404,7 @@ "album_options": "相簿選項", "album_remove_user": "移除使用者?", "album_remove_user_confirmation": "確定要移除 {user} 嗎?", + "album_search_not_found": "找不到符合搜尋條件的相簿", "album_share_no_users": "看來您與所有使用者共享了這本相簿,或沒有其他使用者可供分享。", "album_updated": "更新相簿時", "album_updated_setting_description": "當共享相簿有新項目時用電子郵件通知我", @@ -464,7 +481,6 @@ "assets": "項目", "assets_added_count": "已新增 {count, plural, one {# 個項目} other {# 個項目}}", "assets_added_to_album_count": "已將 {count, plural, other {# 個項目}}加入相簿", - "assets_added_to_name_count": "已將 {count, plural, other {# 個項目}}加入{hasName, select, true {{name}} other {新相簿}}", "assets_cannot_be_added_to_album_count": "{count. plural, one {個} other {個}} 項目未能被添加至相簿", "assets_count": "{count, plural, one {# 個項目} other {# 個項目}}", "assets_deleted_permanently": "{count} 個項目已被永久刪除", @@ -487,6 +503,7 @@ "back_close_deselect": "返回、關閉及取消選取", "background_location_permission": "背景存取位置權限", "background_location_permission_content": "開啟背景執行時自動切換網路,請充許 Immich 一律充許使用精確位置權限,以確認 Wi-Fi 網路名稱", + "backup": "備份", "backup_album_selection_page_albums_device": "裝置上的相簿({count})", "backup_album_selection_page_albums_tap": "點擊選取,連續點擊兩次取消", "backup_album_selection_page_assets_scatter": "項目會分散在不同相簿。因此,可以設定要備份的相簿。", @@ -550,6 +567,8 @@ "backup_options_page_title": "備份選項", "backup_setting_subtitle": "管理背景與前景上傳設定", "backward": "倒轉", + "beta_sync": "測試版同步狀態", + "beta_sync_subtitle": "管理新的同步系統", "biometric_auth_enabled": "生物辨識驗證已啟用", "biometric_locked_out": "您已被鎖定無法使用生物辨識驗證", "biometric_no_options": "無生物辨識選項可用", @@ -584,6 +603,7 @@ "cancel": "取消", "cancel_search": "取消搜尋", "canceled": "已取消", + "canceling": "取消中", "cannot_merge_people": "無法合併人物", "cannot_undo_this_action": "此步驟無法取消喔!", "cannot_update_the_description": "無法更新描述", @@ -699,7 +719,7 @@ "daily_title_text_date": "E, MMM dd", "daily_title_text_date_year": "YYYY年M月D日 (E)", "dark": "深色", - "darkTheme": "切換爲深色主題", + "dark_theme": "切換深色主題", "date_after": "日期之後", "date_and_time": "日期與時間", "date_before": "日期之前", @@ -741,6 +761,7 @@ "description": "描述", "description_input_hint_text": "新增描述...", "description_input_submit_error": "更新描述時出錯,請檢查日誌以獲取更多詳細資訊", + "deselect_all": "取消全選", "details": "詳細資訊", "direction": "方向", "disabled": "停用", @@ -758,6 +779,7 @@ "documentation": "說明書", "done": "完成", "download": "下載", + "download_action_prompt": "正在下載 {count} 項目", "download_canceled": "下載已取消", "download_complete": "下載完成", "download_enqueue": "已加入下載隊列", @@ -813,6 +835,7 @@ "empty_trash": "清空垃圾桶", "empty_trash_confirmation": "確定要清空垃圾桶嗎?這會永久刪除 Immich 垃圾桶中所有的檔案。\n此步驟無法取消喔!", "enable": "啟用", + "enable_backup": "開啟備份", "enable_biometric_auth_description": "輸入您的 PIN 碼以啟用生物辨識驗證", "enabled": "己啟用", "end_date": "結束日期", @@ -969,6 +992,8 @@ "explorer": "總攬", "export": "匯出", "export_as_json": "匯出 JSON", + "export_database": "匯出資料庫", + "export_database_description": "匯出 SQLite 資料庫", "extension": "副檔名", "external": "外部", "external_libraries": "外部圖庫", @@ -980,6 +1005,7 @@ "failed_to_load_assets": "無法加載檔案", "failed_to_load_folder": "無法載入資料夾", "favorite": "收藏", + "favorite_action_prompt": "已新增 {count} 個到我的最愛", "favorite_or_unfavorite_photo": "收藏或取消收藏照片", "favorites": "收藏", "favorites_page_no_favorites": "未找到收藏項目", @@ -1018,6 +1044,8 @@ "haptic_feedback_switch": "啓用振動反饋", "haptic_feedback_title": "振動反饋", "has_quota": "配額", + "hash_asset": "雜湊項目", + "hashed_assets": "已雜湊項目", "header_settings_add_header_tip": "新增標頭", "header_settings_field_validator_msg": "設定不可為空", "header_settings_header_name_input": "標頭名稱", @@ -1116,12 +1144,13 @@ "light": "淺色", "like_deleted": "已刪除的收藏", "link_motion_video": "鏈結動態影片", - "link_options": "鏈結選項", "link_to_oauth": "連接 OAuth", "linked_oauth_account": "已連接 OAuth 帳號", "list": "列表", "loading": "載入中", "loading_search_results_failed": "載入搜尋結果失敗", + "local": "本地", + "local_assets": "本地項目", "local_network": "本地網路", "local_network_sheet_info": "當使用指定的 Wi-Fi 網路時,應用程式將透過此網址連線至伺服器", "location_permission": "位置權限", @@ -1177,7 +1206,6 @@ "manage_your_devices": "管理已登入的裝置", "manage_your_oauth_connection": "管理您的 OAuth 連接", "map": "地圖", - "map_assets_in_bound": "{count} 張照片", "map_assets_in_bounds": "{count} 張照片", "map_cannot_get_user_location": "無法獲取用戶位置", "map_location_dialog_yes": "確定", @@ -1276,6 +1304,7 @@ "no_results": "沒有結果", "no_results_description": "試試同義詞或更通用的關鍵字吧", "no_shared_albums_message": "建立相簿分享照片和影片", + "no_uploads_in_progress": "沒有正在上傳的項目", "not_in_any_album": "不在任何相簿中", "not_selected": "未選擇", "note_apply_storage_label_to_previously_uploaded assets": "*註:執行套用儲存標籤前先上傳項目", @@ -1467,6 +1496,8 @@ "refreshing_faces": "重整面部資料中", "refreshing_metadata": "正在重新整理詳細資料", "regenerating_thumbnails": "重新產生縮圖中", + "remote": "遠端", + "remote_assets": "遠端項目", "remove": "移除", "remove_assets_album_confirmation": "確定要從相簿中移除 {count, plural, other {# 個檔案}}嗎?", "remove_assets_shared_link_confirmation": "確定刪除共享連結中{count, plural, other {# 個項目}}嗎?", @@ -1502,6 +1533,8 @@ "reset_password": "重設密碼", "reset_people_visibility": "重設人物可見性", "reset_pin_code": "重置 PIN 碼", + "reset_sqlite": "重設 SQLite 資料庫", + "reset_sqlite_success": "已成功重設 SQLite 資料庫", "reset_to_default": "重設回預設", "resolve_duplicates": "解決重複項", "resolved_all_duplicates": "已解決所有重複項目", @@ -1866,13 +1899,14 @@ "upload_success": "上傳成功,要查看新上傳的檔案請重新整理頁面。", "upload_to_immich": "上傳至 Immich ({count})", "uploading": "上傳中", + "uploading_media": "媒體上傳中", "url": "網址", "usage": "用量", "use_biometric": "使用生物辨識", "use_current_connection": "使用目前的連線", "use_custom_date_range": "改用自訂日期範圍", "user": "使用者", - "user_has_been_deleted": "此用戶以被刪除", + "user_has_been_deleted": "此用戶已被刪除。", "user_id": "使用者 ID", "user_liked": "{user} 喜歡了 {type, select, photo {這張照片} video {這段影片} asset {這個檔案} other {它}}", "user_pin_code_settings": "PIN 碼", diff --git a/i18n/zh_SIMPLIFIED.json b/i18n/zh_SIMPLIFIED.json index 17c6954ab7..10aef204bd 100644 --- a/i18n/zh_SIMPLIFIED.json +++ b/i18n/zh_SIMPLIFIED.json @@ -53,7 +53,7 @@ "confirm_email_below": "请输入“{email}”以进行确认", "confirm_reprocess_all_faces": "确定要对全部照片重新进行面部识别吗?这将同时清除所有已命名人物。", "confirm_user_password_reset": "确定要重置用户“{user}”的密码吗?", - "confirm_user_pin_code_reset": "确定要重置{user}的PIN码吗?", + "confirm_user_pin_code_reset": "确定要重置用户“{user}”的PIN码吗?", "create_job": "创建任务", "cron_expression": "Cron 表达式", "cron_expression_description": "使用 Cron 格式设置扫描间隔。更多详细信息请参阅 Crontab Guru", @@ -166,6 +166,20 @@ "metadata_settings_description": "管理元数据设置", "migration_job": "迁移", "migration_job_description": "将项目和人脸识别的缩略图迁移到最新的文件夹结构", + "nightly_tasks_cluster_faces_setting_description": "对新检测到的面部进行面部识别", + "nightly_tasks_cluster_new_faces_setting": "群组新面孔", + "nightly_tasks_database_cleanup_setting": "数据库清理任务", + "nightly_tasks_database_cleanup_setting_description": "清理数据库中过期的旧数据", + "nightly_tasks_generate_memories_setting": "生成回忆", + "nightly_tasks_generate_memories_setting_description": "从项目中生成新的回忆", + "nightly_tasks_missing_thumbnails_setting": "生成缺失的缩略图", + "nightly_tasks_missing_thumbnails_setting_description": "为生成缩略图队列无缩略图的项目", + "nightly_tasks_settings": "夜间任务设置", + "nightly_tasks_settings_description": "管理夜间任务", + "nightly_tasks_start_time_setting": "开始时间", + "nightly_tasks_start_time_setting_description": "服务器开始运行夜间任务的时间", + "nightly_tasks_sync_quota_usage_setting": "同步配额使用情况", + "nightly_tasks_sync_quota_usage_setting_description": "根据当前使用情况更新用户存储配额", "no_paths_added": "无已添加路径", "no_pattern_added": "无已添加规则", "note_apply_storage_label_previous_assets": "提示:要将存储标签应用于之前上传的项目,需要运行", @@ -196,6 +210,8 @@ "oauth_mobile_redirect_uri": "移动端重定向 URI", "oauth_mobile_redirect_uri_override": "移动端重定向 URI 覆盖", "oauth_mobile_redirect_uri_override_description": "当 OAuth 提供商不允许使用移动 URI 时启用,如“''{callback}''”", + "oauth_role_claim": "角色声明", + "oauth_role_claim_description": "根据此声明的存在自动授予管理员访问权限。声明可以是“user”(用户)或“admin”(管理员)。", "oauth_settings": "OAuth", "oauth_settings_description": "管理 OAuth 登录设置", "oauth_settings_more_details": "关于此功能的更多详细信息,请查看相关文档。", @@ -357,6 +373,8 @@ "admin_password": "管理员密码", "administration": "系统管理", "advanced": "高级", + "advanced_settings_beta_timeline_subtitle": "体验全新的应用程序", + "advanced_settings_beta_timeline_title": "测试版时间线", "advanced_settings_enable_alternate_media_filter_subtitle": "使用此选项可在同步过程中根据备用条件筛选项目。仅当您在应用程序检测所有相册均遇到问题时才尝试此功能。", "advanced_settings_enable_alternate_media_filter_title": "[实验] 使用备用的设备相册同步筛选条件", "advanced_settings_log_level_title": "日志等级: {level}", @@ -371,7 +389,7 @@ "advanced_settings_tile_subtitle": "高级用户设置", "advanced_settings_troubleshooting_subtitle": "启用用于故障排除的额外功能", "advanced_settings_troubleshooting_title": "故障排除", - "age_months": "{months, plural, one {#月龄} other {#月龄}}", + "age_months": "{months, plural, one {#个月} other {#个月}}", "age_year_months": "1岁{months, plural, one {#个月} other {#个月}}", "age_years": "{years, plural, other {#岁}}", "album_added": "被添加到相册", @@ -379,6 +397,7 @@ "album_cover_updated": "相册封面已更新", "album_delete_confirmation": "确定要删除相册“{album}”吗?", "album_delete_confirmation_description": "如果该相册是共享的,其他用户将无法再访问它。", + "album_deleted": "相册已删除", "album_info_card_backup_album_excluded": "已排除", "album_info_card_backup_album_included": "已选中", "album_info_updated": "相册信息已更新", @@ -388,6 +407,7 @@ "album_options": "相册设置", "album_remove_user": "移除用户?", "album_remove_user_confirmation": "确定要移除“{user}”吗?", + "album_search_not_found": "未找到符合搜索条件的相册", "album_share_no_users": "看起来您已与所有用户共享了此相册,或者您根本没有任何用户可共享。", "album_updated": "相册有更新", "album_updated_setting_description": "当共享相册有新项目时接收邮件通知", @@ -400,13 +420,14 @@ "album_viewer_appbar_share_err_title": "修改相册标题失败", "album_viewer_appbar_share_leave": "退出共享", "album_viewer_appbar_share_to": "共享给", - "album_viewer_page_share_add_users": "创建用户", + "album_viewer_page_share_add_users": "邀请他人", "album_with_link_access": "拥有此链接的任何人均可查看本相册中的照片和人物。", "albums": "相册", "albums_count": "{count, plural, one {{count, number} 个相册} other {{count, number} 个相册}}", "albums_default_sort_order": "默认相册排序方式", "albums_default_sort_order_description": "创建新相册时的项目初始排序方式。", "albums_feature_description": "可与其他用户共享的项目收藏。", + "albums_on_device_count": "设备上的相册({count} 个)", "all": "全部", "all_albums": "所有相册", "all_people": "全部人物", @@ -427,12 +448,13 @@ "app_settings": "应用设置", "appears_in": "出现于", "archive": "归档", + "archive_action_prompt": "已将 {count} 项添加到归档", "archive_or_unarchive_photo": "归档或取消归档照片", "archive_page_no_archived_assets": "未找到归档项目", "archive_page_title": "归档({count})", "archive_size": "归档大小", "archive_size_description": "配置下载归档大小(GB)", - "archived": "已存档", + "archived": "已归档", "archived_count": "{count, plural, other {已归档 # 项}}", "are_these_the_same_person": "他们是同一位人吗?", "are_you_sure_to_do_this": "确定要这样做吗?", @@ -464,7 +486,6 @@ "assets": "项目", "assets_added_count": "已添加{count, plural, one {#个项目} other {#个项目}}", "assets_added_to_album_count": "已添加{count, plural, one {#个项目} other {#个项目}}到相册", - "assets_added_to_name_count": "已添加{count, plural, one {#个项目} other {#个项目}}到{hasName, select, true {{name}} other {新相册}}", "assets_cannot_be_added_to_album_count": "无法添加 {count, plural, one {个项目} other {个项目}} 到相册中", "assets_count": "{count, plural, one {#个项目} other {#个项目}}", "assets_deleted_permanently": "{count} 个项目已被永久删除", @@ -490,6 +511,7 @@ "back_close_deselect": "返回、关闭或反选", "background_location_permission": "后台定位权限", "background_location_permission_content": "为了在后台运行时切换网络,Immich 必须*始终*拥有精确的位置访问权限,这样应用程序才能读取 Wi-Fi 网络的名称", + "backup": "备份", "backup_album_selection_page_albums_device": "设备上的相册({count})", "backup_album_selection_page_albums_tap": "单击选中,双击取消", "backup_album_selection_page_assets_scatter": "项目会分散在多个相册中。因此,可以在备份过程中包含或排除相册。", @@ -553,6 +575,8 @@ "backup_options_page_title": "备份选项", "backup_setting_subtitle": "管理后台和前台上传设置", "backward": "后退", + "beta_sync": "测试版同步状态", + "beta_sync_subtitle": "管理新的同步系统", "biometric_auth_enabled": "生物识别身份验证已启用", "biometric_locked_out": "您被锁定在生物识别身份验证之外", "biometric_no_options": "没有可用的生物识别选项", @@ -570,7 +594,7 @@ "cache_settings_clear_cache_button": "清除缓存", "cache_settings_clear_cache_button_title": "清除应用缓存。在重新生成缓存之前,将显著影响应用的性能。", "cache_settings_duplicated_assets_clear_button": "清除", - "cache_settings_duplicated_assets_subtitle": "已加入黑名单的照片和视频", + "cache_settings_duplicated_assets_subtitle": "应用程序忽略的照片和视频", "cache_settings_duplicated_assets_title": "重复项目({count})", "cache_settings_statistics_album": "图库缩略图", "cache_settings_statistics_full": "完整图像", @@ -587,6 +611,7 @@ "cancel": "取消", "cancel_search": "取消搜索", "canceled": "已取消", + "canceling": "取消中", "cannot_merge_people": "无法合并人物", "cannot_undo_this_action": "注意:该操作无法被撤消!", "cannot_update_the_description": "无法更新描述", @@ -700,10 +725,11 @@ "current_server_address": "当前服务器地址", "custom_locale": "自定义地区", "custom_locale_description": "日期和数字显示格式跟随语言和地区", + "custom_url": "自定义URL", "daily_title_text_date": "MMM dd (E)", "daily_title_text_date_year": "YYYY年M月D日 (E)", "dark": "深色", - "darkTheme": "暗色主题", + "dark_theme": "切换深色主题", "date_after": "开始日期", "date_and_time": "日期与时间", "date_before": "结束日期", @@ -719,8 +745,10 @@ "default_locale": "默认地区", "default_locale_description": "根据您的浏览器地区设置日期和数字显示格式", "delete": "删除", + "delete_action_confirmation_message": "您确定要删除此资产吗?此操作会将资产移至服务器回收站,并会提示您是否要在本地删除它", + "delete_action_prompt": "已删除 {count} 项", "delete_album": "删除相册", - "delete_api_key_prompt": "确定删除此 API 密钥吗?", + "delete_api_key_prompt": "是否确认删除此 API 密钥?", "delete_dialog_alert": "这些项目将从 Immich 和您的设备中永久删除", "delete_dialog_alert_local": "这些项目将从您的移动设备中永久删除,但仍然可以从 Immich 服务器中再次获取", "delete_dialog_alert_local_non_backed_up": "部分项目还未备份至 Immich 服务器,将从您的移动设备中永久删除", @@ -732,9 +760,12 @@ "delete_key": "删除密钥", "delete_library": "删除图库", "delete_link": "删除链接", + "delete_local_action_prompt": "已删除本地项目{count}项", "delete_local_dialog_ok_backed_up_only": "仅删除已备份项目", "delete_local_dialog_ok_force": "确认删除", "delete_others": "删除其它", + "delete_permanently": "永久删除", + "delete_permanently_action_prompt": "已永久删除 {count} 项", "delete_shared_link": "删除共享链接", "delete_shared_link_dialog_title": "删除共享链接", "delete_tag": "删除标签", @@ -745,6 +776,7 @@ "description": "描述", "description_input_hint_text": "添加描述...", "description_input_submit_error": "更新描述时出错,请检查日志以获取更多详细信息", + "deselect_all": "取消全选", "details": "详情", "direction": "方向", "disabled": "已禁用", @@ -762,6 +794,7 @@ "documentation": "帮助文档", "done": "完成", "download": "下载", + "download_action_prompt": "正在下载 {count} 个项目", "download_canceled": "下载已取消", "download_complete": "下载完成", "download_enqueue": "已加入下载队列", @@ -799,6 +832,7 @@ "edit_key": "编辑 API 密钥", "edit_link": "编辑链接", "edit_location": "编辑位置", + "edit_location_action_prompt": "{count} 个位置已编辑", "edit_location_dialog_title": "位置", "edit_name": "编辑名称", "edit_people": "编辑人物", @@ -817,6 +851,7 @@ "empty_trash": "清空回收站", "empty_trash_confirmation": "确定要清空回收站?这将永久删除回收站中的所有项目。\n注意:该操作无法撤消!", "enable": "启用", + "enable_backup": "启用备份", "enable_biometric_auth_description": "输入您的PIN码以启用生物识别身份验证", "enabled": "已启用", "end_date": "结束日期", @@ -973,6 +1008,8 @@ "explorer": "资源管理器", "export": "导出", "export_as_json": "导出为 JSON", + "export_database": "导出数据库", + "export_database_description": "导出 SQLite 数据库", "extension": "扩展", "external": "外部的", "external_libraries": "外部图库", @@ -984,6 +1021,7 @@ "failed_to_load_assets": "加载项目失败", "failed_to_load_folder": "加载文件夹失败", "favorite": "收藏", + "favorite_action_prompt": "已将 {count} 项添加到收藏", "favorite_or_unfavorite_photo": "收藏或取消收藏照片", "favorites": "收藏夹", "favorites_page_no_favorites": "未找到收藏项目", @@ -1004,7 +1042,7 @@ "folders": "文件夹", "folders_feature_description": "在文件夹视图中浏览文件系统上的照片和视频", "forward": "向前", - "gcast_enabled": "Google Cast", + "gcast_enabled": "Google Cast 投屏", "gcast_enabled_description": "该功能需要加载来自 Google 的外部资源。", "general": "通用", "get_help": "获取帮助", @@ -1023,6 +1061,9 @@ "haptic_feedback_switch": "启用振动反馈", "haptic_feedback_title": "振动反馈", "has_quota": "配额大小", + "hash_asset": "哈希项目", + "hashed_assets": "已哈希的项目", + "hashing": "正在哈希", "header_settings_add_header_tip": "添加标头", "header_settings_field_validator_msg": "设置不可为空", "header_settings_header_name_input": "标头名称", @@ -1055,6 +1096,7 @@ "host": "服务器", "hour": "时", "id": "ID", + "idle": "空闲", "ignore_icloud_photos": "忽略 iCloud 照片", "ignore_icloud_photos_description": "存储在 iCloud 中的照片不会上传至 Immich 服务器", "image": "图片", @@ -1112,6 +1154,7 @@ "language_no_results_title": "未找到对应语言", "language_search_hint": "搜索语言...", "language_setting_description": "选择您的语言偏好", + "large_files": "大文件", "last_seen": "最后上线于", "latest_version": "最新版本", "latitude": "纬度", @@ -1127,16 +1170,18 @@ "library_page_sort_created": "创建日期", "library_page_sort_last_modified": "上次修改", "library_page_sort_title": "相册标题", + "licenses": "许可证", "light": "浅色", "like_deleted": "已删除的收藏", "link_motion_video": "链接动态视频", - "link_options": "链接选项", "link_to_oauth": "绑定 OAuth", "linked_oauth_account": "已绑定 OAuth 账户", "list": "列表", "loading": "加载中", "loading_search_results_failed": "加载搜索结果失败", + "local": "本地", "local_asset_cast_failed": "无法投放未上传至服务器的项目", + "local_assets": "本地项目", "local_network": "本地网络", "local_network_sheet_info": "当使用指定的 Wi-Fi 网络时,应用程序将通过此 URL 访问服务器", "location_permission": "定位权限", @@ -1193,8 +1238,7 @@ "manage_your_devices": "管理已登录设备", "manage_your_oauth_connection": "管理您的 OAuth 绑定", "map": "地图", - "map_assets_in_bound": "{count} 张照片", - "map_assets_in_bounds": "{count} 张照片", + "map_assets_in_bounds": "{count, plural, one {# 张照片} other {# 张照片}}", "map_cannot_get_user_location": "无法获取用户位置", "map_location_dialog_yes": "是", "map_location_picker_page_use_location": "使用此位置", @@ -1246,6 +1290,7 @@ "more": "更多", "move": "移动", "move_off_locked_folder": "移出锁定文件夹", + "move_to_lock_folder_action_prompt": "已将 {count} 项添加到锁定文件夹", "move_to_locked_folder": "移动到锁定文件夹", "move_to_locked_folder_confirmation": "这些照片和视频将从所有相册中移除,只能在锁定文件夹中查看", "moved_to_archive": "已归档 {count, plural, one {# 个项目} other {# 个项目}}", @@ -1292,6 +1337,7 @@ "no_results": "无结果", "no_results_description": "尝试使用同义词或更通用的关键词", "no_shared_albums_message": "创建相册以共享照片和视频", + "no_uploads_in_progress": "没有正在进行的上传", "not_in_any_album": "不在任何相册中", "not_selected": "未选择", "note_apply_storage_label_to_previously_uploaded assets": "提示:要将存储标签应用于之前上传的项目,需要运行", @@ -1329,6 +1375,7 @@ "original": "原图", "other": "其它", "other_devices": "其它设备", + "other_entities": "其他实体", "other_variables": "其它变量", "owned": "我的", "owner": "所有者", @@ -1460,6 +1507,7 @@ "purchase_server_description_2": "支持者状态", "purchase_server_title": "服务器", "purchase_settings_server_activated": "服务器产品密钥正在由管理员管理", + "queue_status": "排队中 {count}/{total}", "rating": "星级", "rating_clear": "删除星级", "rating_count": "{count, plural, one {#星} other {#星}}", @@ -1488,6 +1536,8 @@ "refreshing_faces": "正在面部重新识别", "refreshing_metadata": "正在刷新元数据", "regenerating_thumbnails": "正在重新生成缩略图", + "remote": "远程", + "remote_assets": "远程项目", "remove": "移除", "remove_assets_album_confirmation": "确定要从图库中移除{count, plural, one {#个项目} other {#个项目}}?", "remove_assets_shared_link_confirmation": "确定要从共享链接中移除{count, plural, one {#个项目} other {#个项目}}?", @@ -1495,7 +1545,9 @@ "remove_custom_date_range": "取消自定义日期范围", "remove_deleted_assets": "彻底删除文件", "remove_from_album": "从相册中移除", + "remove_from_album_action_prompt": "从相册中移除了 {count} 项", "remove_from_favorites": "移出收藏", + "remove_from_lock_folder_action_prompt": "已从锁定的文件夹中移除 {count} 项", "remove_from_locked_folder": "从锁定文件夹中移除", "remove_from_locked_folder_confirmation": "您确定要将这些照片和视频移出锁定文件夹吗?移出后它们将会在您的图库中可见。", "remove_from_shared_link": "从共享链接中移除", @@ -1523,19 +1575,25 @@ "reset_password": "重置密码", "reset_people_visibility": "重置人物识别", "reset_pin_code": "重置PIN码", + "reset_sqlite": "重置 SQLite 数据库", + "reset_sqlite_confirmation": "您确定要重置 SQLite 数据库吗?您需要注销并重新登录才能重新同步数据", + "reset_sqlite_success": "已成功重置 SQLite 数据库", "reset_to_default": "恢复默认值", "resolve_duplicates": "处理重复项", "resolved_all_duplicates": "处理所有重复项", "restore": "恢复", "restore_all": "恢复全部", + "restore_trash_action_prompt": "从回收站中恢复了 {count} 项", "restore_user": "恢复用户", "restored_asset": "已恢复项目", "resume": "继续", "retry_upload": "重新上传", "review_duplicates": "检查重复项", + "review_large_files": "查看大文件", "role": "选择用户权限", "role_editor": "可编辑", "role_viewer": "仅查看", + "running": "正在运行", "save": "保存", "save_to_gallery": "保存到图库", "saved_api_key": "已保存的 API 密钥", @@ -1667,6 +1725,7 @@ "settings_saved": "设置已保存", "setup_pin_code": "设置PIN码", "share": "共享", + "share_action_prompt": "已共享 {count} 项目", "share_add_photos": "添加项目", "share_assets_selected": "{count} 已选择", "share_dialog_preparing": "正在准备...", @@ -1688,6 +1747,7 @@ "shared_link_clipboard_copied_massage": "复制到剪贴板", "shared_link_clipboard_text": "链接:{link}\n密码:{password}", "shared_link_create_error": "创建共享链接出错", + "shared_link_custom_url_description": "使用自定义URL访问此共享链接", "shared_link_edit_description_hint": "编辑共享描述", "shared_link_edit_expire_after_option_day": "1天", "shared_link_edit_expire_after_option_days": "{count} 天", @@ -1713,6 +1773,7 @@ "shared_link_info_chip_metadata": "EXIF", "shared_link_manage_links": "管理共享链接", "shared_link_options": "共享链接选项", + "shared_link_password_description": "需要密码才能访问此共享链接", "shared_links": "共享链接", "shared_links_description": "通过链接分享照片和视频", "shared_photos_and_videos_count": "{assetCount, plural, other {#项已共享照片&视频。}}", @@ -1768,6 +1829,7 @@ "sort_title": "标题", "source": "GitHub 源代码", "stack": "堆叠", + "stack_action_prompt": "{count} 个已堆叠", "stack_duplicates": "堆叠重复项目", "stack_select_one_photo": "为堆叠选择一张展示图", "stack_selected_photos": "堆叠选定的照片", @@ -1787,6 +1849,7 @@ "storage_quota": "存储配额", "storage_usage": "已用:{used}/{available}", "submit": "提交", + "success": "成功", "suggestions": "建议", "sunrise_on_the_beach": "海滩上的日出", "support": "支持", @@ -1796,6 +1859,8 @@ "sync": "同步", "sync_albums": "同步相册", "sync_albums_manual_subtitle": "将所有上传的视频和照片同步到选定的备份相册", + "sync_local": "同步本地", + "sync_remote": "同步远程", "sync_upload_album_setting_subtitle": "创建照片和视频并上传到 Immich 上的选定相册中", "tag": "标签", "tag_assets": "标记项目", @@ -1806,6 +1871,7 @@ "tag_updated": "已更新标签:{tag}", "tagged_assets": "{count, plural, one {# 个项目} other {# 个项目}}被加上标签", "tags": "标签", + "tap_to_run_job": "点击运行作业", "template": "模版", "theme": "主题", "theme_selection": "主题选项", @@ -1838,6 +1904,7 @@ "total": "总计", "total_usage": "总用量", "trash": "回收站", + "trash_action_prompt": "已将 {count} 项移至回收站", "trash_all": "全部删除", "trash_count": "删除{count, number}项", "trash_delete_asset": "将项目放入回收站/删除", @@ -1855,9 +1922,11 @@ "unable_to_change_pin_code": "无法修改PIN码", "unable_to_setup_pin_code": "无法设置PIN码", "unarchive": "取消归档", + "unarchive_action_prompt": "已从归档中移除 {count} 项", "unarchived_count": "{count, plural, other {取消归档 # 项}}", "undo": "撤销", "unfavorite": "取消收藏", + "unfavorite_action_prompt": "已从收藏中移除 {count} 项", "unhide_person": "显示人物", "unknown": "未知", "unknown_country": "未知的国家", @@ -1875,15 +1944,20 @@ "unselect_all_duplicates": "取消选择所有重复项", "unselect_all_in": "取消选择 {group} 中的所有内容", "unstack": "取消堆叠", + "unstack_action_prompt": "{count} 个未堆叠", "unstacked_assets_count": "{count, plural, one {#个项目} other {#个项目}}已取消堆叠", + "untagged": "无标签", "up_next": "下一个", "updated_at": "已更新", "updated_password": "更新密码", "upload": "上传", + "upload_action_prompt": "有{count}个待上传", "upload_concurrency": "上传并发", + "upload_details": "上传详情", "upload_dialog_info": "是否要将所选项目备份到服务器?", "upload_dialog_title": "上传项目", "upload_errors": "上传完成,出现{count, plural, one {#个错误} other {#个错误}},刷新页面以查看新上传的项目。", + "upload_finished": "上传完成", "upload_progress": "剩余{remaining, number} - 已处理 {processed, number}/{total, number}", "upload_skipped_duplicates": "已跳过{count, plural, one {#个重复项} other {#个重复项}}", "upload_status_duplicates": "重复项", @@ -1892,6 +1966,7 @@ "upload_success": "上传成功,刷新页面查看新上传的项目。", "upload_to_immich": "上传至 Immich({count})", "uploading": "正在上传", + "uploading_media": "文件上传中", "url": "URL", "usage": "用量", "use_biometric": "使用生物识别", @@ -1912,6 +1987,7 @@ "user_usage_stats_description": "查看帐户使用统计信息", "username": "用户名", "users": "用户", + "users_added_to_album_count": "已将 {count, plural, one {# 个用户} other {# 个用户}} 添加到相册", "utilities": "实用工具", "validate": "验证", "validate_endpoint_error": "请输入有效的 URL", @@ -1930,6 +2006,7 @@ "view_album": "查看相册", "view_all": "查看全部", "view_all_users": "查看全部用户", + "view_details": "查看详情", "view_in_timeline": "在时间轴中查看", "view_link": "查看链接", "view_links": "查看链接", diff --git a/mobile/.fvmrc b/mobile/.fvmrc index 7d2a608dfa..3ca65ffc7c 100644 --- a/mobile/.fvmrc +++ b/mobile/.fvmrc @@ -1,3 +1,3 @@ { - "flutter": "3.32.6" + "flutter": "3.32.8" } \ No newline at end of file diff --git a/mobile/.vscode/settings.json b/mobile/.vscode/settings.json index c1b3fc9ce3..9a9fb67ce3 100644 --- a/mobile/.vscode/settings.json +++ b/mobile/.vscode/settings.json @@ -1,5 +1,9 @@ { - "dart.flutterSdkPath": ".fvm/versions/3.32.6", + "dart.flutterSdkPath": ".fvm/versions/3.32.8", + "dart.lineLength": 120, + "[dart]": { + "editor.rulers": [120], + }, "search.exclude": { "**/.fvm": true }, diff --git a/mobile/analysis_options.yaml b/mobile/analysis_options.yaml index 7a03b54a95..1b0b7170d2 100644 --- a/mobile/analysis_options.yaml +++ b/mobile/analysis_options.yaml @@ -9,6 +9,9 @@ # packages, and plugins designed to encourage good coding practices. include: package:flutter_lints/flutter.yaml +formatter: + page_width: 120 + linter: # The lint rules applied to this project can be customized in the # section below to disable rules from the `package:flutter_lints/flutter.yaml` diff --git a/mobile/android/app/build.gradle b/mobile/android/app/build.gradle index 870e424461..1f0e2e7675 100644 --- a/mobile/android/app/build.gradle +++ b/mobile/android/app/build.gradle @@ -3,6 +3,8 @@ plugins { id "kotlin-android" id "dev.flutter.flutter-gradle-plugin" id 'com.google.devtools.ksp' + id 'org.jetbrains.kotlin.plugin.compose' version '2.0.20' // this version matches your Kotlin version + } def localProperties = new Properties() @@ -45,6 +47,10 @@ android { main.java.srcDirs += 'src/main/kotlin' } + buildFeatures { + compose true + } + defaultConfig { applicationId "app.alextran.immich" minSdkVersion 26 @@ -66,20 +72,6 @@ android { } } - flavorDimensions "default" - productFlavors { - production { - dimension "default" - applicationId "app.alextran.immich" - } - - beta { - dimension "default" - applicationId "app.alextran.immich.beta" - versionNameSuffix "-BETA" - } - } - buildTypes { debug { applicationIdSuffix '.debug' @@ -105,6 +97,8 @@ dependencies { def guava_version = '33.3.1-android' def glide_version = '4.16.0' def serialization_version = '1.8.1' + def compose_version = '1.1.1' + def gson_version = '2.10.1' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version" @@ -116,6 +110,17 @@ dependencies { ksp "com.github.bumptech.glide:ksp:$glide_version" coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.2' + + //Glance Widget + implementation "androidx.glance:glance-appwidget:$compose_version" + implementation "com.google.code.gson:gson:$gson_version" + + // Glance Configure + implementation "androidx.activity:activity-compose:1.8.2" + implementation "androidx.compose.ui:ui:$compose_version" + implementation "androidx.compose.ui:ui-tooling:$compose_version" + implementation "androidx.compose.material3:material3:1.2.1" + implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.6.2" } // This is uncommented in F-Droid build script diff --git a/mobile/android/app/proguard-rules.pro b/mobile/android/app/proguard-rules.pro index ea6dd795b5..898caee06c 100644 --- a/mobile/android/app/proguard-rules.pro +++ b/mobile/android/app/proguard-rules.pro @@ -25,8 +25,15 @@ @com.google.gson.annotations.SerializedName ; } +# TypeToken preventions +-keep class com.google.gson.reflect.TypeToken { *; } +-keep class * extends com.google.gson.reflect.TypeToken + # Retain generic signatures of TypeToken and its subclasses with R8 version 3.0 and higher. -keep,allowobfuscation,allowshrinking class com.google.gson.reflect.TypeToken -keep,allowobfuscation,allowshrinking class * extends com.google.gson.reflect.TypeToken -##---------------End: proguard configuration for Gson ---------- \ No newline at end of file +##---------------End: proguard configuration for Gson ---------- + +# Keep all widget model classes and their fields for Gson +-keep class app.alextran.immich.widget.model.** { *; } \ No newline at end of file diff --git a/mobile/android/app/src/beta/AndroidManifest.xml b/mobile/android/app/src/beta/AndroidManifest.xml deleted file mode 100644 index d4f7b5bc80..0000000000 --- a/mobile/android/app/src/beta/AndroidManifest.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/mobile/android/app/src/main/AndroidManifest.xml b/mobile/android/app/src/main/AndroidManifest.xml index cf3b7ee719..09276f6d4a 100644 --- a/mobile/android/app/src/main/AndroidManifest.xml +++ b/mobile/android/app/src/main/AndroidManifest.xml @@ -141,6 +141,41 @@ android:name="androidx.startup.InitializationProvider" android:authorities="${applicationId}.androidx-startup" tools:node="remove" /> + + + + + + + + + + + + + + + + + + + + + + + @@ -154,4 +189,4 @@ - \ No newline at end of file + diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/BackupWorker.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/BackupWorker.kt index 0fb75b002c..9c90528dc9 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/BackupWorker.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/BackupWorker.kt @@ -83,6 +83,7 @@ class BackupWorker(ctx: Context, params: WorkerParameters) : ListenableWorker(ct flutterLoader.ensureInitializationCompleteAsync(ctx, null, Handler(Looper.getMainLooper())) { runDart() + } return resolvableFuture diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt index b5ef90310e..201d0a43e1 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt @@ -88,7 +88,8 @@ data class PlatformAsset ( val width: Long? = null, val height: Long? = null, val durationInSeconds: Long, - val orientation: Long + val orientation: Long, + val isFavorite: Boolean ) { companion object { @@ -102,7 +103,8 @@ data class PlatformAsset ( val height = pigeonVar_list[6] as Long? val durationInSeconds = pigeonVar_list[7] as Long val orientation = pigeonVar_list[8] as Long - return PlatformAsset(id, name, type, createdAt, updatedAt, width, height, durationInSeconds, orientation) + val isFavorite = pigeonVar_list[9] as Boolean + return PlatformAsset(id, name, type, createdAt, updatedAt, width, height, durationInSeconds, orientation, isFavorite) } } fun toList(): List { @@ -116,6 +118,7 @@ data class PlatformAsset ( height, durationInSeconds, orientation, + isFavorite, ) } override fun equals(other: Any?): Boolean { diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt index 02cd54b8c3..b2ceb8a9f2 100644 --- a/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/sync/MessagesImplBase.kt @@ -29,20 +29,24 @@ open class NativeSyncApiImplBase(context: Context) { MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO.toString() ) const val BUCKET_SELECTION = "(${MediaStore.Files.FileColumns.BUCKET_ID} = ?)" - val ASSET_PROJECTION = arrayOf( - MediaStore.MediaColumns._ID, - MediaStore.MediaColumns.DATA, - MediaStore.MediaColumns.DISPLAY_NAME, - MediaStore.MediaColumns.DATE_TAKEN, - MediaStore.MediaColumns.DATE_ADDED, - MediaStore.MediaColumns.DATE_MODIFIED, - MediaStore.Files.FileColumns.MEDIA_TYPE, - MediaStore.MediaColumns.BUCKET_ID, - MediaStore.MediaColumns.WIDTH, - MediaStore.MediaColumns.HEIGHT, - MediaStore.MediaColumns.DURATION, - MediaStore.MediaColumns.ORIENTATION, - ) + val ASSET_PROJECTION = buildList { + add(MediaStore.MediaColumns._ID) + add(MediaStore.MediaColumns.DATA) + add(MediaStore.MediaColumns.DISPLAY_NAME) + add(MediaStore.MediaColumns.DATE_TAKEN) + add(MediaStore.MediaColumns.DATE_ADDED) + add(MediaStore.MediaColumns.DATE_MODIFIED) + add(MediaStore.Files.FileColumns.MEDIA_TYPE) + add(MediaStore.MediaColumns.BUCKET_ID) + add(MediaStore.MediaColumns.WIDTH) + add(MediaStore.MediaColumns.HEIGHT) + add(MediaStore.MediaColumns.DURATION) + add(MediaStore.MediaColumns.ORIENTATION) + // IS_FAVORITE is only available on Android 11 and above + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) { + add(MediaStore.MediaColumns.IS_FAVORITE) + } + }.toTypedArray() const val HASH_BUFFER_SIZE = 2 * 1024 * 1024 } @@ -77,6 +81,7 @@ open class NativeSyncApiImplBase(context: Context) { val durationColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.DURATION) val orientationColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.ORIENTATION) + val favoriteColumn = c.getColumnIndex(MediaStore.MediaColumns.IS_FAVORITE) while (c.moveToNext()) { val id = c.getLong(idColumn).toString() @@ -105,6 +110,7 @@ open class NativeSyncApiImplBase(context: Context) { else c.getLong(durationColumn) / 1000 val bucketId = c.getString(bucketIdColumn) val orientation = c.getInt(orientationColumn) + val isFavorite = if (favoriteColumn == -1) false else c.getInt(favoriteColumn) != 0 val asset = PlatformAsset( id, @@ -116,6 +122,7 @@ open class NativeSyncApiImplBase(context: Context) { height, duration, orientation.toLong(), + isFavorite, ) yield(AssetResult.ValidAsset(asset, bucketId)) } diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/BitmapUtils.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/BitmapUtils.kt new file mode 100644 index 0000000000..9188df1700 --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/BitmapUtils.kt @@ -0,0 +1,33 @@ +package app.alextran.immich.widget + +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import java.io.File + +fun loadScaledBitmap(file: File, reqWidth: Int, reqHeight: Int): Bitmap? { + val options = BitmapFactory.Options().apply { + inJustDecodeBounds = true + } + BitmapFactory.decodeFile(file.absolutePath, options) + + options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight) + options.inJustDecodeBounds = false + + return BitmapFactory.decodeFile(file.absolutePath, options) +} + +fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int { + val (height: Int, width: Int) = options.run { outHeight to outWidth } + var inSampleSize = 1 + + if (height > reqHeight || width > reqWidth) { + val halfHeight: Int = height / 2 + val halfWidth: Int = width / 2 + + while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) { + inSampleSize *= 2 + } + } + + return inSampleSize +} diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImageDownloadWorker.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImageDownloadWorker.kt new file mode 100644 index 0000000000..25a7ed99f1 --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImageDownloadWorker.kt @@ -0,0 +1,244 @@ +package app.alextran.immich.widget + +import android.content.Context +import android.graphics.Bitmap +import android.util.Log +import androidx.datastore.preferences.core.Preferences +import androidx.glance.* +import androidx.glance.appwidget.GlanceAppWidgetManager +import androidx.glance.appwidget.state.updateAppWidgetState +import androidx.work.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import java.io.File +import java.io.FileOutputStream +import java.util.UUID +import java.util.concurrent.TimeUnit +import androidx.glance.appwidget.state.getAppWidgetState +import androidx.glance.state.PreferencesGlanceStateDefinition +import app.alextran.immich.widget.model.* +import java.time.LocalDate + +class ImageDownloadWorker( + private val context: Context, + workerParameters: WorkerParameters +) : CoroutineWorker(context, workerParameters) { + + companion object { + + private val uniqueWorkName = ImageDownloadWorker::class.java.simpleName + + private fun buildConstraints(): Constraints { + return Constraints.Builder() + .setRequiredNetworkType(NetworkType.CONNECTED) + .build() + } + + private fun buildInputData(appWidgetId: Int, widgetType: WidgetType): Data { + return Data.Builder() + .putString(kWorkerWidgetType, widgetType.toString()) + .putInt(kWorkerWidgetID, appWidgetId) + .build() + } + + fun enqueuePeriodic(context: Context, appWidgetId: Int, widgetType: WidgetType) { + val manager = WorkManager.getInstance(context) + + val workRequest = PeriodicWorkRequestBuilder( + 20, TimeUnit.MINUTES + ) + .setConstraints(buildConstraints()) + .setInputData(buildInputData(appWidgetId, widgetType)) + .addTag(appWidgetId.toString()) + .build() + + manager.enqueueUniquePeriodicWork( + "$uniqueWorkName-$appWidgetId", + ExistingPeriodicWorkPolicy.UPDATE, + workRequest + ) + } + + fun singleShot(context: Context, appWidgetId: Int, widgetType: WidgetType) { + val manager = WorkManager.getInstance(context) + + val workRequest = OneTimeWorkRequestBuilder() + .setConstraints(buildConstraints()) + .setInputData(buildInputData(appWidgetId, widgetType)) + .addTag(appWidgetId.toString()) + .build() + + manager.enqueueUniqueWork( + "$uniqueWorkName-$appWidgetId-singleShot", + ExistingWorkPolicy.REPLACE, + workRequest + ) + } + + suspend fun cancel(context: Context, appWidgetId: Int) { + WorkManager.getInstance(context).cancelAllWorkByTag("$uniqueWorkName-$appWidgetId") + + // delete cached image + val glanceId = GlanceAppWidgetManager(context).getGlanceIdBy(appWidgetId) + val widgetConfig = getAppWidgetState(context, PreferencesGlanceStateDefinition, glanceId) + val currentImgUUID = widgetConfig[kImageUUID] + + if (!currentImgUUID.isNullOrEmpty()) { + val file = File(context.cacheDir, imageFilename(currentImgUUID)) + file.delete() + } + } + } + + override suspend fun doWork(): Result { + return try { + val widgetType = WidgetType.valueOf(inputData.getString(kWorkerWidgetType) ?: "") + val widgetId = inputData.getInt(kWorkerWidgetID, -1) + val glanceId = GlanceAppWidgetManager(context).getGlanceIdBy(widgetId) + val widgetConfig = getAppWidgetState(context, PreferencesGlanceStateDefinition, glanceId) + val currentImgUUID = widgetConfig[kImageUUID] + + val serverConfig = ImmichAPI.getServerConfig(context) + + // clear any image caches and go to "login" state if no credentials + if (serverConfig == null) { + if (!currentImgUUID.isNullOrEmpty()) { + deleteImage(currentImgUUID) + updateWidget( + glanceId, + "", + "", + "immich://", + WidgetState.LOG_IN + ) + } + + return Result.success() + } + + // fetch new image + val entry = when (widgetType) { + WidgetType.RANDOM -> fetchRandom(serverConfig, widgetConfig) + WidgetType.MEMORIES -> fetchMemory(serverConfig) + } + + // clear current image if it exists + if (!currentImgUUID.isNullOrEmpty()) { + deleteImage(currentImgUUID) + } + + // save a new image + val imgUUID = UUID.randomUUID().toString() + saveImage(entry.image, imgUUID) + + // trigger the update routine with new image uuid + updateWidget(glanceId, imgUUID, entry.subtitle, entry.deeplink) + + Result.success() + } catch (e: Exception) { + Log.e(uniqueWorkName, "Error while loading image", e) + if (runAttemptCount < 10) { + Result.retry() + } else { + Result.failure() + } + } + } + + private suspend fun updateWidget( + glanceId: GlanceId, + imageUUID: String, + subtitle: String?, + deeplink: String?, + widgetState: WidgetState = WidgetState.SUCCESS + ) { + updateAppWidgetState(context, glanceId) { prefs -> + prefs[kNow] = System.currentTimeMillis() + prefs[kImageUUID] = imageUUID + prefs[kWidgetState] = widgetState.toString() + prefs[kSubtitleText] = subtitle ?: "" + prefs[kDeeplinkURL] = deeplink ?: "" + } + + PhotoWidget().update(context,glanceId) + } + + private suspend fun fetchRandom( + serverConfig: ServerConfig, + widgetConfig: Preferences + ): WidgetEntry { + val api = ImmichAPI(serverConfig) + + val filters = SearchFilters() + val albumId = widgetConfig[kSelectedAlbum] + val showSubtitle = widgetConfig[kShowAlbumName] + val albumName = widgetConfig[kSelectedAlbumName] + var subtitle: String? = if (showSubtitle == true) albumName else "" + + + if (albumId == "FAVORITES") { + filters.isFavorite = true + } else if (albumId != null) { + filters.albumIds = listOf(albumId) + } + + var randomSearch = api.fetchSearchResults(filters) + + // handle an empty album, fallback to random + if (randomSearch.isEmpty() && albumId != null) { + randomSearch = api.fetchSearchResults(SearchFilters()) + subtitle = "" + } + + val random = randomSearch.first() + val image = api.fetchImage(random) + + return WidgetEntry( + image, + subtitle, + assetDeeplink(random) + ) + } + + private suspend fun fetchMemory( + serverConfig: ServerConfig + ): WidgetEntry { + val api = ImmichAPI(serverConfig) + + val today = LocalDate.now() + val memories = api.fetchMemory(today) + val asset: Asset + var subtitle: String? = null + + if (memories.isNotEmpty()) { + // pick a random asset from a random memory + val memory = memories.random() + asset = memory.assets.random() + + val yearDiff = today.year - memory.data.year + subtitle = "$yearDiff ${if (yearDiff == 1) "year" else "years"} ago" + } else { + val filters = SearchFilters(size=1) + asset = api.fetchSearchResults(filters).first() + } + + val image = api.fetchImage(asset) + return WidgetEntry( + image, + subtitle, + assetDeeplink(asset) + ) + } + + private suspend fun deleteImage(uuid: String) = withContext(Dispatchers.IO) { + val file = File(context.cacheDir, imageFilename(uuid)) + file.delete() + } + + private suspend fun saveImage(bitmap: Bitmap, uuid: String) = withContext(Dispatchers.IO) { + val file = File(context.cacheDir, imageFilename(uuid)) + FileOutputStream(file).use { out -> + bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out) + } + } +} diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImmichAPI.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImmichAPI.kt new file mode 100644 index 0000000000..42f5fb4b1b --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/ImmichAPI.kt @@ -0,0 +1,103 @@ +package app.alextran.immich.widget + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import app.alextran.immich.widget.model.* +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import es.antonborri.home_widget.HomeWidgetPlugin +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import java.io.OutputStreamWriter +import java.net.HttpURLConnection +import java.net.URL +import java.net.URLEncoder +import java.time.LocalDate +import java.time.format.DateTimeFormatter + +class ImmichAPI(cfg: ServerConfig) { + + companion object { + fun getServerConfig(context: Context): ServerConfig? { + val prefs = HomeWidgetPlugin.getData(context) + + val serverURL = prefs.getString("widget_server_url", "") ?: "" + val sessionKey = prefs.getString("widget_auth_token", "") ?: "" + + if (serverURL.isBlank() || sessionKey.isBlank()) { + return null + } + + return ServerConfig( + serverURL, + sessionKey + ) + } + } + + + private val gson = Gson() + private val serverConfig = cfg + + private fun buildRequestURL(endpoint: String, params: List> = emptyList()): URL { + val urlString = StringBuilder("${serverConfig.serverEndpoint}$endpoint?sessionKey=${serverConfig.sessionKey}") + + for ((key, value) in params) { + urlString.append("&${URLEncoder.encode(key, "UTF-8")}=${URLEncoder.encode(value, "UTF-8")}") + } + + return URL(urlString.toString()) + } + + suspend fun fetchSearchResults(filters: SearchFilters): List = withContext(Dispatchers.IO) { + val url = buildRequestURL("/search/random") + val connection = (url.openConnection() as HttpURLConnection).apply { + requestMethod = "POST" + setRequestProperty("Content-Type", "application/json") + doOutput = true + } + + connection.outputStream.use { + OutputStreamWriter(it).use { writer -> + writer.write(gson.toJson(filters)) + writer.flush() + } + } + + val response = connection.inputStream.bufferedReader().readText() + val type = object : TypeToken>() {}.type + gson.fromJson(response, type) + } + + suspend fun fetchMemory(date: LocalDate): List = withContext(Dispatchers.IO) { + val iso8601 = date.format(DateTimeFormatter.ISO_LOCAL_DATE) + val url = buildRequestURL("/memories", listOf("for" to iso8601)) + val connection = (url.openConnection() as HttpURLConnection).apply { + requestMethod = "GET" + } + + val response = connection.inputStream.bufferedReader().readText() + val type = object : TypeToken>() {}.type + gson.fromJson(response, type) + } + + suspend fun fetchImage(asset: Asset): Bitmap = withContext(Dispatchers.IO) { + val url = buildRequestURL("/assets/${asset.id}/thumbnail", listOf("size" to "preview")) + val connection = url.openConnection() + val data = connection.getInputStream().readBytes() + BitmapFactory.decodeByteArray(data, 0, data.size) + ?: throw Exception("Invalid image data") + } + + suspend fun fetchAlbums(): List = withContext(Dispatchers.IO) { + val url = buildRequestURL("/albums") + val connection = (url.openConnection() as HttpURLConnection).apply { + requestMethod = "GET" + } + + val response = connection.inputStream.bufferedReader().readText() + val type = object : TypeToken>() {}.type + gson.fromJson(response, type) + } +} diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/MemoryReceiver.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/MemoryReceiver.kt new file mode 100644 index 0000000000..63b32eb6f0 --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/MemoryReceiver.kt @@ -0,0 +1,58 @@ +package app.alextran.immich.widget + +import android.appwidget.AppWidgetManager +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import androidx.glance.appwidget.GlanceAppWidgetReceiver +import app.alextran.immich.widget.model.* +import es.antonborri.home_widget.HomeWidgetPlugin +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +class MemoryReceiver : GlanceAppWidgetReceiver() { + override val glanceAppWidget = PhotoWidget() + + override fun onUpdate( + context: Context, + appWidgetManager: AppWidgetManager, + appWidgetIds: IntArray + ) { + super.onUpdate(context, appWidgetManager, appWidgetIds) + + appWidgetIds.forEach { widgetID -> + ImageDownloadWorker.enqueuePeriodic(context, widgetID, WidgetType.MEMORIES) + } + } + + override fun onReceive(context: Context, intent: Intent) { + val fromMainApp = intent.getBooleanExtra(HomeWidgetPlugin.TRIGGERED_FROM_HOME_WIDGET, false) + val provider = ComponentName(context, MemoryReceiver::class.java) + val glanceIds = AppWidgetManager.getInstance(context).getAppWidgetIds(provider) + + // Launch coroutine to setup a single shot if the app requested the update + if (fromMainApp) { + glanceIds.forEach { widgetID -> + ImageDownloadWorker.singleShot(context, widgetID, WidgetType.MEMORIES) + } + } + + // make sure the periodic jobs are running + glanceIds.forEach { widgetID -> + ImageDownloadWorker.enqueuePeriodic(context, widgetID, WidgetType.MEMORIES) + } + + super.onReceive(context, intent) + } + + override fun onDeleted(context: Context, appWidgetIds: IntArray) { + super.onDeleted(context, appWidgetIds) + CoroutineScope(Dispatchers.Default).launch { + appWidgetIds.forEach { id -> + ImageDownloadWorker.cancel(context, id) + } + } + } +} + diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/PhotoWidget.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/PhotoWidget.kt new file mode 100644 index 0000000000..b1a0a9de31 --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/PhotoWidget.kt @@ -0,0 +1,124 @@ +package app.alextran.immich.widget + +import android.content.Context +import android.content.Intent +import android.graphics.Bitmap +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.* +import androidx.core.net.toUri +import androidx.datastore.preferences.core.MutablePreferences +import androidx.glance.appwidget.* +import androidx.glance.* +import androidx.glance.action.clickable +import androidx.glance.layout.* +import androidx.glance.state.GlanceStateDefinition +import androidx.glance.state.PreferencesGlanceStateDefinition +import androidx.glance.text.Text +import androidx.glance.text.TextAlign +import androidx.glance.text.TextStyle +import androidx.glance.unit.ColorProvider +import app.alextran.immich.R +import app.alextran.immich.widget.model.* +import java.io.File + +class PhotoWidget : GlanceAppWidget() { + override var stateDefinition: GlanceStateDefinition<*> = PreferencesGlanceStateDefinition + + override suspend fun provideGlance(context: Context, id: GlanceId) { + provideContent { + val prefs = currentState() + + val imageUUID = prefs[kImageUUID] + val subtitle = prefs[kSubtitleText] + val deeplinkURL = prefs[kDeeplinkURL]?.toUri() + val widgetState = prefs[kWidgetState] + var bitmap: Bitmap? = null + + if (imageUUID != null) { + // fetch a random photo from server + val file = File(context.cacheDir, imageFilename(imageUUID)) + + if (file.exists()) { + bitmap = loadScaledBitmap(file, 500, 500) + } + } + + // WIDGET CONTENT + Box( + modifier = GlanceModifier + .fillMaxSize() + .background(GlanceTheme.colors.background) + .clickable { + val intent = Intent(Intent.ACTION_VIEW, deeplinkURL ?: "immich://".toUri()) + intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK + context.startActivity(intent) + } + ) { + if (bitmap != null) { + Image( + provider = ImageProvider(bitmap), + contentDescription = "Widget Image", + contentScale = ContentScale.Crop, + modifier = GlanceModifier.fillMaxSize() + ) + + if (!subtitle.isNullOrBlank()) { + Column( + verticalAlignment = Alignment.Bottom, + horizontalAlignment = Alignment.Start, + modifier = GlanceModifier + .fillMaxSize() + .padding(12.dp) + ) { + Text( + text = subtitle, + style = TextStyle( + color = ColorProvider(Color.White), + fontSize = 16.sp + ), + modifier = GlanceModifier + .background(ColorProvider(Color(0x99000000))) // 60% black + .padding(8.dp) + .cornerRadius(8.dp) + ) + } + } + } else { + Column( + modifier = GlanceModifier.fillMaxSize(), + verticalAlignment = Alignment.CenterVertically, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Image( + provider = ImageProvider(R.drawable.splash), + contentDescription = null, + ) + + if (widgetState == WidgetState.LOG_IN.toString()) { + Box( + modifier = GlanceModifier.fillMaxWidth().padding(16.dp), + contentAlignment = Alignment.Center + ) { + Text("Log in to your Immich server", style = TextStyle(textAlign = TextAlign.Center, color = GlanceTheme.colors.primary)) + } + } else { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalAlignment = Alignment.CenterHorizontally, + modifier = GlanceModifier.fillMaxWidth().padding(16.dp) + ) { + CircularProgressIndicator( + modifier = GlanceModifier.size(12.dp) + ) + + Spacer(modifier = GlanceModifier.width(8.dp)) + + Text("Loading widget...", style = TextStyle(textAlign = TextAlign.Center, color = GlanceTheme.colors.primary)) + } + } + } + } + } + } + } +} diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/RandomReceiver.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/RandomReceiver.kt new file mode 100644 index 0000000000..a7662181bc --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/RandomReceiver.kt @@ -0,0 +1,57 @@ +package app.alextran.immich.widget + +import android.appwidget.AppWidgetManager +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import es.antonborri.home_widget.HomeWidgetPlugin +import androidx.glance.appwidget.GlanceAppWidgetReceiver +import app.alextran.immich.widget.model.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +class RandomReceiver : GlanceAppWidgetReceiver() { + override val glanceAppWidget = PhotoWidget() + + override fun onUpdate( + context: Context, + appWidgetManager: AppWidgetManager, + appWidgetIds: IntArray + ) { + super.onUpdate(context, appWidgetManager, appWidgetIds) + + appWidgetIds.forEach { widgetID -> + ImageDownloadWorker.enqueuePeriodic(context, widgetID, WidgetType.RANDOM) + } + } + + override fun onReceive(context: Context, intent: Intent) { + val fromMainApp = intent.getBooleanExtra(HomeWidgetPlugin.TRIGGERED_FROM_HOME_WIDGET, false) + val provider = ComponentName(context, RandomReceiver::class.java) + val glanceIds = AppWidgetManager.getInstance(context).getAppWidgetIds(provider) + + // Launch coroutine to setup a single shot if the app requested the update + if (fromMainApp) { + glanceIds.forEach { widgetID -> + ImageDownloadWorker.singleShot(context, widgetID, WidgetType.RANDOM) + } + } + + // make sure the periodic jobs are running + glanceIds.forEach { widgetID -> + ImageDownloadWorker.enqueuePeriodic(context, widgetID, WidgetType.RANDOM) + } + + super.onReceive(context, intent) + } + + override fun onDeleted(context: Context, appWidgetIds: IntArray) { + super.onDeleted(context, appWidgetIds) + CoroutineScope(Dispatchers.Default).launch { + appWidgetIds.forEach { id -> + ImageDownloadWorker.cancel(context, id) + } + } + } +} diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/configure/Dropdown.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/configure/Dropdown.kt new file mode 100644 index 0000000000..74686ee0b8 --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/configure/Dropdown.kt @@ -0,0 +1,64 @@ +package app.alextran.immich.widget.configure + +import androidx.compose.foundation.layout.* +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.* + + +data class DropdownItem ( + val label: String, + val id: String, +) + +// Creating a composable to display a drop down menu +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun Dropdown(items: List, + selectedItem: DropdownItem?, + onItemSelected: (DropdownItem) -> Unit, + enabled: Boolean = true +) { + + var expanded by remember { mutableStateOf(false) } + var selectedOption by remember { mutableStateOf(selectedItem?.label ?: items[0].label) } + + ExposedDropdownMenuBox( + expanded = expanded, + onExpandedChange = { expanded = !expanded && enabled }, + ) { + + TextField( + value = selectedOption, + onValueChange = {}, + readOnly = true, + enabled = enabled, + trailingIcon = { + ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) + }, + colors = ExposedDropdownMenuDefaults.textFieldColors(), + modifier = Modifier + .fillMaxWidth() + .menuAnchor() + ) + + ExposedDropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false } + ) { + items.forEach { option -> + DropdownMenuItem( + text = { Text(option.label, color = MaterialTheme.colorScheme.onSurface) }, + onClick = { + selectedOption = option.label + onItemSelected(option) + + expanded = false + }, + contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding + ) + } + } + } + } + diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/configure/LightDarkTheme.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/configure/LightDarkTheme.kt new file mode 100644 index 0000000000..efdcc41540 --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/configure/LightDarkTheme.kt @@ -0,0 +1,28 @@ +package app.alextran.immich.widget.configure + +import android.os.Build +import androidx.compose.foundation.* +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext + +@Composable +fun LightDarkTheme( + content: @Composable () -> Unit +) { + val context = LocalContext.current + val isDarkTheme = isSystemInDarkTheme() + + val colorScheme = when { + Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && isDarkTheme -> + dynamicDarkColorScheme(context) + Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && !isDarkTheme -> + dynamicLightColorScheme(context) + isDarkTheme -> darkColorScheme() + else -> lightColorScheme() + } + MaterialTheme( + colorScheme = colorScheme, + content = content + ) +} diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/configure/RandomConfigure.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/configure/RandomConfigure.kt new file mode 100644 index 0000000000..83e404a8f1 --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/configure/RandomConfigure.kt @@ -0,0 +1,210 @@ +package app.alextran.immich.widget.configure + +import android.appwidget.AppWidgetManager +import android.content.Context +import android.os.Bundle +import android.util.Log +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Check +import androidx.compose.material.icons.filled.Close +import androidx.compose.material.icons.filled.Warning +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.unit.dp +import androidx.glance.GlanceId +import androidx.glance.appwidget.GlanceAppWidgetManager +import androidx.glance.appwidget.state.getAppWidgetState +import androidx.glance.appwidget.state.updateAppWidgetState +import androidx.glance.state.PreferencesGlanceStateDefinition +import app.alextran.immich.widget.ImageDownloadWorker +import app.alextran.immich.widget.ImmichAPI +import app.alextran.immich.widget.model.* +import kotlinx.coroutines.launch +import java.io.FileNotFoundException + +class RandomConfigure : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + // Get widget ID from intent + val appWidgetId = intent?.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, + AppWidgetManager.INVALID_APPWIDGET_ID) + ?: AppWidgetManager.INVALID_APPWIDGET_ID + + if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) { + finish() + return + } + + val glanceId = GlanceAppWidgetManager(applicationContext) + .getGlanceIdBy(appWidgetId) + + setContent { + LightDarkTheme { + RandomConfiguration(applicationContext, appWidgetId, glanceId, onDone = { + finish() + Log.w("WIDGET_ACTIVITY", "SAVING") + }) + } + } + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun RandomConfiguration(context: Context, appWidgetId: Int, glanceId: GlanceId, onDone: () -> Unit) { + + var selectedAlbum by remember { mutableStateOf(null) } + var showAlbumName by remember { mutableStateOf(false) } + var availableAlbums by remember { mutableStateOf>(listOf()) } + var state by remember { mutableStateOf(WidgetConfigState.LOADING) } + + val scope = rememberCoroutineScope() + + LaunchedEffect(Unit) { + // get albums from server + val serverCfg = ImmichAPI.getServerConfig(context) + + if (serverCfg == null) { + state = WidgetConfigState.LOG_IN + return@LaunchedEffect + } + + val api = ImmichAPI(serverCfg) + + val currentState = getAppWidgetState(context, PreferencesGlanceStateDefinition, glanceId) + val currentAlbumId = currentState[kSelectedAlbum] ?: "NONE" + val currentAlbumName = currentState[kSelectedAlbumName] ?: "None" + var albumItems: List + + try { + albumItems = api.fetchAlbums().map { + DropdownItem(it.albumName, it.id) + } + + state = WidgetConfigState.SUCCESS + } catch (e: FileNotFoundException) { + Log.e("WidgetWorker", "Error fetching albums: ${e.message}") + + state = WidgetConfigState.NO_CONNECTION + albumItems = listOf(DropdownItem(currentAlbumName, currentAlbumId)) + } + + availableAlbums = listOf(DropdownItem("None", "NONE"), DropdownItem("Favorites", "FAVORITES")) + albumItems + + // load selected configuration + val albumEntity = availableAlbums.firstOrNull { it.id == currentAlbumId } + selectedAlbum = albumEntity ?: availableAlbums.first() + + // load showAlbumName + showAlbumName = currentState[kShowAlbumName] == true + } + + suspend fun saveConfiguration() { + updateAppWidgetState(context, glanceId) { prefs -> + prefs[kSelectedAlbum] = selectedAlbum?.id ?: "" + prefs[kSelectedAlbumName] = selectedAlbum?.label ?: "" + prefs[kShowAlbumName] = showAlbumName + } + + ImageDownloadWorker.singleShot(context, appWidgetId, WidgetType.RANDOM) + } + + Scaffold( + topBar = { + TopAppBar ( + title = { Text("Widget Configuration") }, + actions = { + IconButton(onClick = { + scope.launch { + saveConfiguration() + onDone() + } + }) { + Icon(Icons.Default.Check, contentDescription = "Close", tint = MaterialTheme.colorScheme.primary) + } + } + ) + } + ) { innerPadding -> + Surface( + modifier = Modifier + .fillMaxSize() + .padding(innerPadding), // Respect the top bar + color = MaterialTheme.colorScheme.background + ) { + Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.TopCenter) { + when (state) { + WidgetConfigState.LOADING -> CircularProgressIndicator(modifier = Modifier.size(48.dp)) + WidgetConfigState.LOG_IN -> Text("You must log in inside the Immich App to configure this widget.") + else -> { + Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) { + Text("View a random image from your library or a specific album.", style = MaterialTheme.typography.bodyMedium) + + // no connection warning + if (state == WidgetConfigState.NO_CONNECTION) { + Row( + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(12.dp)) + .background(MaterialTheme.colorScheme.errorContainer) + .padding(12.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = Icons.Default.Warning, + contentDescription = "Warning", + modifier = Modifier.size(24.dp) + ) + Spacer(modifier = Modifier.width(12.dp)) + Text( + text = "No connection to the server is available. Please try again later.", + style = MaterialTheme.typography.bodyMedium + ) + } + } + + Column( + modifier = Modifier + .clip(RoundedCornerShape(12.dp)) + .background(MaterialTheme.colorScheme.surfaceContainer) + .padding(16.dp), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + Text("Album") + Dropdown( + items = availableAlbums, + selectedItem = selectedAlbum, + onItemSelected = { selectedAlbum = it }, + enabled = (state != WidgetConfigState.NO_CONNECTION) + ) + + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier.fillMaxWidth() + ) { + Text(text = "Show Album Name") + Switch( + checked = showAlbumName, + onCheckedChange = { showAlbumName = it }, + enabled = (state != WidgetConfigState.NO_CONNECTION) + ) + } + } + } + } + } + } + } + } +} + diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/model/Model.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/model/Model.kt new file mode 100644 index 0000000000..9595a3b696 --- /dev/null +++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/model/Model.kt @@ -0,0 +1,80 @@ +package app.alextran.immich.widget.model + +import android.graphics.Bitmap +import androidx.datastore.preferences.core.* + +// MARK: Immich Entities + +enum class AssetType { + IMAGE, VIDEO, AUDIO, OTHER +} + +data class Asset( + val id: String, + val type: AssetType, +) + +data class SearchFilters( + var type: AssetType = AssetType.IMAGE, + val size: Int = 1, + var albumIds: List = listOf(), + var isFavorite: Boolean? = null +) + +data class MemoryResult( + val id: String, + var assets: List, + val type: String, + val data: MemoryData +) { + data class MemoryData(val year: Int) +} + +data class Album( + val id: String, + val albumName: String +) + +// MARK: Widget Specific + +enum class WidgetType { + RANDOM, MEMORIES; +} + +enum class WidgetState { + LOADING, SUCCESS, LOG_IN; +} + +enum class WidgetConfigState { + LOADING, SUCCESS, LOG_IN, NO_CONNECTION +} + +data class WidgetEntry ( + val image: Bitmap, + val subtitle: String?, + val deeplink: String? +) + +data class ServerConfig(val serverEndpoint: String, val sessionKey: String) + +// MARK: Widget State Keys +val kImageUUID = stringPreferencesKey("uuid") +val kSubtitleText = stringPreferencesKey("subtitle") +val kNow = longPreferencesKey("now") +val kWidgetState = stringPreferencesKey("state") +val kSelectedAlbum = stringPreferencesKey("albumID") +val kSelectedAlbumName = stringPreferencesKey("albumName") +val kShowAlbumName = booleanPreferencesKey("showAlbumName") +val kDeeplinkURL = stringPreferencesKey("deeplink") + +const val kWorkerWidgetType = "widgetType" +const val kWorkerWidgetID = "widgetId" +const val kTriggeredFromApp = "triggeredFromApp" + +fun imageFilename(id: String): String { + return "widget_image_$id.jpg" +} + +fun assetDeeplink(asset: Asset): String { + return "immich://asset?id=${asset.id}" +} diff --git a/mobile/android/app/src/main/res/drawable-nodpi/memory_preview.png b/mobile/android/app/src/main/res/drawable-nodpi/memory_preview.png new file mode 100644 index 0000000000..97aceb3ef6 Binary files /dev/null and b/mobile/android/app/src/main/res/drawable-nodpi/memory_preview.png differ diff --git a/mobile/android/app/src/main/res/drawable-nodpi/random_preview.png b/mobile/android/app/src/main/res/drawable-nodpi/random_preview.png new file mode 100644 index 0000000000..f94d1bbcd5 Binary files /dev/null and b/mobile/android/app/src/main/res/drawable-nodpi/random_preview.png differ diff --git a/mobile/android/app/src/main/res/values/strings.xml b/mobile/android/app/src/main/res/values/strings.xml new file mode 100644 index 0000000000..5ac495ebb5 --- /dev/null +++ b/mobile/android/app/src/main/res/values/strings.xml @@ -0,0 +1,8 @@ + + + Memories + Random + + See memories from Immich. + View a random image from your library or a specific album. + diff --git a/mobile/android/app/src/main/res/xml/memory_widget.xml b/mobile/android/app/src/main/res/xml/memory_widget.xml new file mode 100644 index 0000000000..611c5aae02 --- /dev/null +++ b/mobile/android/app/src/main/res/xml/memory_widget.xml @@ -0,0 +1,9 @@ + diff --git a/mobile/android/app/src/main/res/xml/random_widget.xml b/mobile/android/app/src/main/res/xml/random_widget.xml new file mode 100644 index 0000000000..25fb24754f --- /dev/null +++ b/mobile/android/app/src/main/res/xml/random_widget.xml @@ -0,0 +1,13 @@ + diff --git a/mobile/android/fastlane/Fastfile b/mobile/android/fastlane/Fastfile index 31203fe55f..8fd2ba3059 100644 --- a/mobile/android/fastlane/Fastfile +++ b/mobile/android/fastlane/Fastfile @@ -35,8 +35,8 @@ platform :android do task: 'bundle', build_type: 'Release', properties: { - "android.injected.version.code" => 204, - "android.injected.version.name" => "1.135.3", + "android.injected.version.code" => 3002, + "android.injected.version.name" => "1.137.3", } ) 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') diff --git a/mobile/dcm_global.yaml b/mobile/dcm_global.yaml index d2465e64b6..c33846e674 100644 --- a/mobile/dcm_global.yaml +++ b/mobile/dcm_global.yaml @@ -1 +1 @@ -version: '>=1.29.0 <1.30.0' +version: '>=1.29.0 <=1.30.0' diff --git a/mobile/drift_schemas/main/drift_schema_v1.json b/mobile/drift_schemas/main/drift_schema_v1.json index c19bcfb945..978a9ba8ad 100644 --- a/mobile/drift_schemas/main/drift_schema_v1.json +++ b/mobile/drift_schemas/main/drift_schema_v1.json @@ -1 +1 @@ -{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_admin","getter_name":"isAdmin","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_admin\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_admin\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"profile_image_path","getter_name":"profileImagePath","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"quota_size_in_bytes","getter_name":"quotaSizeInBytes","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"quota_usage_in_bytes","getter_name":"quotaUsageInBytes","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[0,1],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id)","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id)"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[2],"type":"index","data":{"on":2,"name":"idx_local_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":5,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_asset_owner_checksum","sql":null,"unique":true,"columns":["checksum","owner_id"]}},{"id":6,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":7,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":8,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":9,"references":[],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":10,"references":[2,9],"type":"table","data":{"name":"local_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":11,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":12,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":13,"references":[1,12],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":14,"references":[12,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":15,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":16,"references":[1,15],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}}]} \ No newline at end of file +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_admin","getter_name":"isAdmin","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_admin\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_admin\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"profile_image_path","getter_name":"profileImagePath","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"quota_size_in_bytes","getter_name":"quotaSizeInBytes","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"quota_usage_in_bytes","getter_name":"quotaUsageInBytes","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[0,1],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id)","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id)"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[2],"type":"index","data":{"on":2,"name":"idx_local_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":5,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_asset_owner_checksum","sql":null,"unique":true,"columns":["checksum","owner_id"]}},{"id":6,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":7,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":8,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":9,"references":[],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":10,"references":[2,9],"type":"table","data":{"name":"local_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":11,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":12,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":13,"references":[1,12],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":14,"references":[12,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":15,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":16,"references":[1,15],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}},{"id":17,"references":[0],"type":"table","data":{"name":"person_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"face_asset_id","getter_name":"faceAssetId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumbnail_path","getter_name":"thumbnailPath","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_hidden","getter_name":"isHidden","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_hidden\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_hidden\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"color","getter_name":"color","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"birth_date","getter_name":"birthDate","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}}]} \ No newline at end of file diff --git a/mobile/drift_schemas/main/drift_schema_v2.json b/mobile/drift_schemas/main/drift_schema_v2.json index c19bcfb945..978a9ba8ad 100644 --- a/mobile/drift_schemas/main/drift_schema_v2.json +++ b/mobile/drift_schemas/main/drift_schema_v2.json @@ -1 +1 @@ -{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_admin","getter_name":"isAdmin","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_admin\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_admin\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"profile_image_path","getter_name":"profileImagePath","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"quota_size_in_bytes","getter_name":"quotaSizeInBytes","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"quota_usage_in_bytes","getter_name":"quotaUsageInBytes","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[0,1],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id)","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id)"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[2],"type":"index","data":{"on":2,"name":"idx_local_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":5,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_asset_owner_checksum","sql":null,"unique":true,"columns":["checksum","owner_id"]}},{"id":6,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":7,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":8,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":9,"references":[],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":10,"references":[2,9],"type":"table","data":{"name":"local_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":11,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":12,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":13,"references":[1,12],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":14,"references":[12,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":15,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":16,"references":[1,15],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}}]} \ No newline at end of file +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_admin","getter_name":"isAdmin","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_admin\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_admin\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"profile_image_path","getter_name":"profileImagePath","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"quota_size_in_bytes","getter_name":"quotaSizeInBytes","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"quota_usage_in_bytes","getter_name":"quotaUsageInBytes","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[0,1],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id)","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id)"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[2],"type":"index","data":{"on":2,"name":"idx_local_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":5,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_asset_owner_checksum","sql":null,"unique":true,"columns":["checksum","owner_id"]}},{"id":6,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":7,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":8,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":9,"references":[],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":10,"references":[2,9],"type":"table","data":{"name":"local_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":11,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":12,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":13,"references":[1,12],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":14,"references":[12,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":15,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":16,"references":[1,15],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}},{"id":17,"references":[0],"type":"table","data":{"name":"person_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"face_asset_id","getter_name":"faceAssetId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumbnail_path","getter_name":"thumbnailPath","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_hidden","getter_name":"isHidden","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_hidden\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_hidden\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"color","getter_name":"color","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"birth_date","getter_name":"birthDate","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}}]} \ No newline at end of file diff --git a/mobile/drift_schemas/main/drift_schema_v3.json b/mobile/drift_schemas/main/drift_schema_v3.json new file mode 100644 index 0000000000..1acfbaf493 --- /dev/null +++ b/mobile/drift_schemas/main/drift_schema_v3.json @@ -0,0 +1 @@ +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_admin","getter_name":"isAdmin","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_admin\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_admin\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"profile_image_path","getter_name":"profileImagePath","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"quota_size_in_bytes","getter_name":"quotaSizeInBytes","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"quota_usage_in_bytes","getter_name":"quotaUsageInBytes","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[0],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[2],"type":"index","data":{"on":2,"name":"idx_local_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":5,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_asset_owner_checksum","sql":null,"unique":true,"columns":["checksum","owner_id"]}},{"id":6,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":7,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":8,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":9,"references":[],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":10,"references":[2,9],"type":"table","data":{"name":"local_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":11,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":12,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":13,"references":[1,12],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":14,"references":[12,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":15,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":16,"references":[1,15],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}},{"id":17,"references":[0],"type":"table","data":{"name":"person_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"face_asset_id","getter_name":"faceAssetId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumbnail_path","getter_name":"thumbnailPath","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_hidden","getter_name":"isHidden","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_hidden\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_hidden\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"color","getter_name":"color","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"birth_date","getter_name":"birthDate","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}}]} \ No newline at end of file diff --git a/mobile/drift_schemas/main/drift_schema_v4.json b/mobile/drift_schemas/main/drift_schema_v4.json new file mode 100644 index 0000000000..2488319a5e --- /dev/null +++ b/mobile/drift_schemas/main/drift_schema_v4.json @@ -0,0 +1 @@ +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_admin","getter_name":"isAdmin","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_admin\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_admin\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"profile_image_path","getter_name":"profileImagePath","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"quota_size_in_bytes","getter_name":"quotaSizeInBytes","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"quota_usage_in_bytes","getter_name":"quotaUsageInBytes","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[0],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":5,"references":[3,4],"type":"table","data":{"name":"local_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":6,"references":[3],"type":"index","data":{"on":3,"name":"idx_local_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":7,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_asset_owner_checksum","sql":null,"unique":true,"columns":["checksum","owner_id"]}},{"id":8,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":9,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":10,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":11,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":12,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":13,"references":[1,12],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":14,"references":[12,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":15,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":16,"references":[1,15],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}},{"id":17,"references":[0],"type":"table","data":{"name":"person_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"face_asset_id","getter_name":"faceAssetId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_hidden","getter_name":"isHidden","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_hidden\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_hidden\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"color","getter_name":"color","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"birth_date","getter_name":"birthDate","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":18,"references":[1,17],"type":"table","data":{"name":"asset_face_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"person_id","getter_name":"personId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES person_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES person_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"image_width","getter_name":"imageWidth","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"image_height","getter_name":"imageHeight","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x1","getter_name":"boundingBoxX1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y1","getter_name":"boundingBoxY1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x2","getter_name":"boundingBoxX2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y2","getter_name":"boundingBoxY2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"source_type","getter_name":"sourceType","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}}]} \ No newline at end of file diff --git a/mobile/drift_schemas/main/drift_schema_v5.json b/mobile/drift_schemas/main/drift_schema_v5.json new file mode 100644 index 0000000000..ce29eaabdc --- /dev/null +++ b/mobile/drift_schemas/main/drift_schema_v5.json @@ -0,0 +1 @@ +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_admin","getter_name":"isAdmin","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_admin\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_admin\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"has_profile_image","getter_name":"hasProfileImage","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"has_profile_image\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"has_profile_image\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"profile_changed_at","getter_name":"profileChangedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[0],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":5,"references":[3,4],"type":"table","data":{"name":"local_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":6,"references":[3],"type":"index","data":{"on":3,"name":"idx_local_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":7,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_asset_owner_checksum","sql":null,"unique":true,"columns":["checksum","owner_id"]}},{"id":8,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":9,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":10,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":11,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":12,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":13,"references":[1,12],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":14,"references":[12,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":15,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":16,"references":[1,15],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}},{"id":17,"references":[0],"type":"table","data":{"name":"person_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"face_asset_id","getter_name":"faceAssetId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_hidden","getter_name":"isHidden","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_hidden\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_hidden\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"color","getter_name":"color","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"birth_date","getter_name":"birthDate","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":18,"references":[1,17],"type":"table","data":{"name":"asset_face_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"person_id","getter_name":"personId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES person_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES person_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"image_width","getter_name":"imageWidth","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"image_height","getter_name":"imageHeight","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x1","getter_name":"boundingBoxX1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y1","getter_name":"boundingBoxY1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x2","getter_name":"boundingBoxX2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y2","getter_name":"boundingBoxY2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"source_type","getter_name":"sourceType","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}}]} \ No newline at end of file diff --git a/mobile/drift_schemas/main/drift_schema_v6.json b/mobile/drift_schemas/main/drift_schema_v6.json new file mode 100644 index 0000000000..adb2484006 --- /dev/null +++ b/mobile/drift_schemas/main/drift_schema_v6.json @@ -0,0 +1 @@ +{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":true},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"user_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_admin","getter_name":"isAdmin","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_admin\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_admin\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"email","getter_name":"email","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"has_profile_image","getter_name":"hasProfileImage","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"has_profile_image\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"has_profile_image\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"profile_changed_at","getter_name":"profileChangedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":1,"references":[0],"type":"table","data":{"name":"remote_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"local_date_time","getter_name":"localDateTime","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"thumb_hash","getter_name":"thumbHash","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"live_photo_video_id","getter_name":"livePhotoVideoId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"visibility","getter_name":"visibility","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetVisibility.values)","dart_type_name":"AssetVisibility"}},{"name":"stack_id","getter_name":"stackId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"library_id","getter_name":"libraryId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":2,"references":[0],"type":"table","data":{"name":"stack_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"primary_asset_id","getter_name":"primaryAssetId","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":3,"references":[],"type":"table","data":{"name":"local_asset_entity","was_declared_in_moor":false,"columns":[{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AssetType.values)","dart_type_name":"AssetType"}},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"duration_in_seconds","getter_name":"durationInSeconds","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"checksum","getter_name":"checksum","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":4,"references":[],"type":"table","data":{"name":"local_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"backup_selection","getter_name":"backupSelection","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(BackupSelection.values)","dart_type_name":"BackupSelection"}},{"name":"is_ios_shared_album","getter_name":"isIosSharedAlbum","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_ios_shared_album\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_ios_shared_album\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"marker","getter_name":"marker_","moor_type":"bool","nullable":true,"customConstraints":null,"defaultConstraints":"CHECK (\"marker\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"marker\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":5,"references":[3,4],"type":"table","data":{"name":"local_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES local_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES local_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":6,"references":[3],"type":"index","data":{"on":3,"name":"idx_local_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":7,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_owner_checksum","sql":null,"unique":false,"columns":["owner_id","checksum"]}},{"id":8,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_assets_owner_checksum","sql":"CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum\nON remote_asset_entity (owner_id, checksum)\nWHERE (library_id IS NULL);\n","unique":true,"columns":[]}},{"id":9,"references":[1],"type":"index","data":{"on":1,"name":"UQ_remote_assets_owner_library_checksum","sql":"CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum\nON remote_asset_entity (owner_id, library_id, checksum)\nWHERE (library_id IS NOT NULL);\n","unique":true,"columns":[]}},{"id":10,"references":[1],"type":"index","data":{"on":1,"name":"idx_remote_asset_checksum","sql":null,"unique":false,"columns":["checksum"]}},{"id":11,"references":[0],"type":"table","data":{"name":"user_metadata_entity","was_declared_in_moor":false,"columns":[{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"key","getter_name":"key","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(UserMetadataKey.values)","dart_type_name":"UserMetadataKey"}},{"name":"value","getter_name":"value","moor_type":"blob","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"userMetadataConverter","dart_type_name":"Map"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["user_id","key"]}},{"id":12,"references":[0],"type":"table","data":{"name":"partner_entity","was_declared_in_moor":false,"columns":[{"name":"shared_by_id","getter_name":"sharedById","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"shared_with_id","getter_name":"sharedWithId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"in_timeline","getter_name":"inTimeline","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"in_timeline\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"in_timeline\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["shared_by_id","shared_with_id"]}},{"id":13,"references":[1],"type":"table","data":{"name":"remote_exif_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"city","getter_name":"city","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"state","getter_name":"state","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"country","getter_name":"country","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"date_time_original","getter_name":"dateTimeOriginal","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"height","getter_name":"height","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"width","getter_name":"width","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"exposure_time","getter_name":"exposureTime","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"f_number","getter_name":"fNumber","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"file_size","getter_name":"fileSize","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"focal_length","getter_name":"focalLength","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"latitude","getter_name":"latitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"longitude","getter_name":"longitude","moor_type":"double","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"iso","getter_name":"iso","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"make","getter_name":"make","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"model","getter_name":"model","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"lens","getter_name":"lens","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"orientation","getter_name":"orientation","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"time_zone","getter_name":"timeZone","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"rating","getter_name":"rating","moor_type":"int","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"projection_type","getter_name":"projectionType","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id"]}},{"id":14,"references":[0,1],"type":"table","data":{"name":"remote_album_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"description","getter_name":"description","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('\\'\\'')","default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"thumbnail_asset_id","getter_name":"thumbnailAssetId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"is_activity_enabled","getter_name":"isActivityEnabled","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_activity_enabled\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_activity_enabled\" IN (0, 1))"},"default_dart":"const CustomExpression('1')","default_client_dart":null,"dsl_features":[]},{"name":"order","getter_name":"order","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumAssetOrder.values)","dart_type_name":"AlbumAssetOrder"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":15,"references":[1,14],"type":"table","data":{"name":"remote_album_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","album_id"]}},{"id":16,"references":[14,0],"type":"table","data":{"name":"remote_album_user_entity","was_declared_in_moor":false,"columns":[{"name":"album_id","getter_name":"albumId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_album_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_album_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"user_id","getter_name":"userId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"role","getter_name":"role","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(AlbumUserRole.values)","dart_type_name":"AlbumUserRole"}}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["album_id","user_id"]}},{"id":17,"references":[0],"type":"table","data":{"name":"memory_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"deleted_at","getter_name":"deletedAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"type","getter_name":"type","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[],"type_converter":{"dart_expr":"const EnumIndexConverter(MemoryTypeEnum.values)","dart_type_name":"MemoryTypeEnum"}},{"name":"data","getter_name":"data","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_saved","getter_name":"isSaved","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_saved\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_saved\" IN (0, 1))"},"default_dart":"const CustomExpression('0')","default_client_dart":null,"dsl_features":[]},{"name":"memory_at","getter_name":"memoryAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"seen_at","getter_name":"seenAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"show_at","getter_name":"showAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"hide_at","getter_name":"hideAt","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":18,"references":[1,17],"type":"table","data":{"name":"memory_asset_entity","was_declared_in_moor":false,"columns":[{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"memory_id","getter_name":"memoryId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES memory_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES memory_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["asset_id","memory_id"]}},{"id":19,"references":[0],"type":"table","data":{"name":"person_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"created_at","getter_name":"createdAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"updated_at","getter_name":"updatedAt","moor_type":"dateTime","nullable":false,"customConstraints":null,"default_dart":"const CustomExpression('CURRENT_TIMESTAMP')","default_client_dart":null,"dsl_features":[]},{"name":"owner_id","getter_name":"ownerId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES user_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES user_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"name","getter_name":"name","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"face_asset_id","getter_name":"faceAssetId","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_favorite","getter_name":"isFavorite","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_favorite\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_favorite\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"is_hidden","getter_name":"isHidden","moor_type":"bool","nullable":false,"customConstraints":null,"defaultConstraints":"CHECK (\"is_hidden\" IN (0, 1))","dialectAwareDefaultConstraints":{"sqlite":"CHECK (\"is_hidden\" IN (0, 1))"},"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"color","getter_name":"color","moor_type":"string","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"birth_date","getter_name":"birthDate","moor_type":"dateTime","nullable":true,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}},{"id":20,"references":[1,19],"type":"table","data":{"name":"asset_face_entity","was_declared_in_moor":false,"columns":[{"name":"id","getter_name":"id","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"asset_id","getter_name":"assetId","moor_type":"string","nullable":false,"customConstraints":null,"defaultConstraints":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES remote_asset_entity (id) ON DELETE CASCADE"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"person_id","getter_name":"personId","moor_type":"string","nullable":true,"customConstraints":null,"defaultConstraints":"REFERENCES person_entity (id) ON DELETE SET NULL","dialectAwareDefaultConstraints":{"sqlite":"REFERENCES person_entity (id) ON DELETE SET NULL"},"default_dart":null,"default_client_dart":null,"dsl_features":["unknown"]},{"name":"image_width","getter_name":"imageWidth","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"image_height","getter_name":"imageHeight","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x1","getter_name":"boundingBoxX1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y1","getter_name":"boundingBoxY1","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_x2","getter_name":"boundingBoxX2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"bounding_box_y2","getter_name":"boundingBoxY2","moor_type":"int","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"source_type","getter_name":"sourceType","moor_type":"string","nullable":false,"customConstraints":null,"default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":true,"constraints":[],"strict":true,"explicit_pk":["id"]}}]} \ No newline at end of file diff --git a/mobile/ios/Runner.xcodeproj/project.pbxproj b/mobile/ios/Runner.xcodeproj/project.pbxproj index fb0908e8b6..218a294c33 100644 --- a/mobile/ios/Runner.xcodeproj/project.pbxproj +++ b/mobile/ios/Runner.xcodeproj/project.pbxproj @@ -649,7 +649,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/RunnerProfile.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 210; + CURRENT_PROJECT_VERSION = 213; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_BITCODE = NO; @@ -793,7 +793,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 210; + CURRENT_PROJECT_VERSION = 213; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_BITCODE = NO; @@ -823,7 +823,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 210; + CURRENT_PROJECT_VERSION = 213; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_BITCODE = NO; @@ -857,7 +857,7 @@ CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 210; + CURRENT_PROJECT_VERSION = 213; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; @@ -900,7 +900,7 @@ CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 210; + CURRENT_PROJECT_VERSION = 213; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; @@ -940,7 +940,7 @@ CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 210; + CURRENT_PROJECT_VERSION = 213; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; @@ -979,7 +979,7 @@ CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 210; + CURRENT_PROJECT_VERSION = 213; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; @@ -1023,7 +1023,7 @@ CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 210; + CURRENT_PROJECT_VERSION = 213; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; @@ -1064,7 +1064,7 @@ CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 210; + CURRENT_PROJECT_VERSION = 213; CUSTOM_GROUP_ID = group.app.immich.share; DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; diff --git a/mobile/ios/Runner/Info.plist b/mobile/ios/Runner/Info.plist index 4237813dfc..d3e42b9939 100644 --- a/mobile/ios/Runner/Info.plist +++ b/mobile/ios/Runner/Info.plist @@ -78,7 +78,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.135.1 + 1.137.2 CFBundleSignature ???? CFBundleURLTypes @@ -105,7 +105,7 @@ CFBundleVersion - 210 + 213 FLTEnableImpeller ITSAppUsesNonExemptEncryption diff --git a/mobile/ios/Runner/Sync/Messages.g.swift b/mobile/ios/Runner/Sync/Messages.g.swift index e629604d6a..b7f4293836 100644 --- a/mobile/ios/Runner/Sync/Messages.g.swift +++ b/mobile/ios/Runner/Sync/Messages.g.swift @@ -139,6 +139,7 @@ struct PlatformAsset: Hashable { var height: Int64? = nil var durationInSeconds: Int64 var orientation: Int64 + var isFavorite: Bool // swift-format-ignore: AlwaysUseLowerCamelCase @@ -152,6 +153,7 @@ struct PlatformAsset: Hashable { let height: Int64? = nilOrValue(pigeonVar_list[6]) let durationInSeconds = pigeonVar_list[7] as! Int64 let orientation = pigeonVar_list[8] as! Int64 + let isFavorite = pigeonVar_list[9] as! Bool return PlatformAsset( id: id, @@ -162,7 +164,8 @@ struct PlatformAsset: Hashable { width: width, height: height, durationInSeconds: durationInSeconds, - orientation: orientation + orientation: orientation, + isFavorite: isFavorite ) } func toList() -> [Any?] { @@ -176,6 +179,7 @@ struct PlatformAsset: Hashable { height, durationInSeconds, orientation, + isFavorite, ] } static func == (lhs: PlatformAsset, rhs: PlatformAsset) -> Bool { diff --git a/mobile/ios/Runner/Sync/MessagesImpl.swift b/mobile/ios/Runner/Sync/MessagesImpl.swift index 459e29fa5a..2810dee7c1 100644 --- a/mobile/ios/Runner/Sync/MessagesImpl.swift +++ b/mobile/ios/Runner/Sync/MessagesImpl.swift @@ -28,7 +28,8 @@ extension PHAsset { width: Int64(pixelWidth), height: Int64(pixelHeight), durationInSeconds: Int64(duration), - orientation: 0 + orientation: 0, + isFavorite: isFavorite ) } } @@ -171,7 +172,8 @@ class NativeSyncApiImpl: NativeSyncApi { name: "", type: 0, durationInSeconds: 0, - orientation: 0 + orientation: 0, + isFavorite: false ) if (updatedAssets.contains(AssetWrapper(with: predicate))) { continue diff --git a/mobile/ios/build/XCBuildData/a34f3d77f077776687d3b444cba8f1c4.xcbuilddata/manifest.json b/mobile/ios/build/XCBuildData/a34f3d77f077776687d3b444cba8f1c4.xcbuilddata/manifest.json deleted file mode 100644 index 7391713b6f..0000000000 --- a/mobile/ios/build/XCBuildData/a34f3d77f077776687d3b444cba8f1c4.xcbuilddata/manifest.json +++ /dev/null @@ -1 +0,0 @@ -{"client":{"name":"basic","version":0,"file-system":"device-agnostic","perform-ownership-analysis":"no"},"targets":{"":[""]},"commands":{"":{"tool":"phony","inputs":[""],"outputs":[""]},"P0:::Gate WorkspaceHeaderMapVFSFilesWritten":{"tool":"phony","inputs":[],"outputs":[""]}}} \ No newline at end of file diff --git a/mobile/ios/fastlane/Fastfile b/mobile/ios/fastlane/Fastfile index 4c60a2e831..19f00c5f43 100644 --- a/mobile/ios/fastlane/Fastfile +++ b/mobile/ios/fastlane/Fastfile @@ -22,7 +22,7 @@ platform :ios do path: "./Runner.xcodeproj", ) increment_version_number( - version_number: "1.135.3" + version_number: "1.137.3" ) increment_build_number( build_number: latest_testflight_build_number + 1, diff --git a/mobile/lib/constants/colors.dart b/mobile/lib/constants/colors.dart index ade878d6f6..069ed519cf 100644 --- a/mobile/lib/constants/colors.dart +++ b/mobile/lib/constants/colors.dart @@ -1,17 +1,6 @@ import 'package:flutter/material.dart'; -enum ImmichColorPreset { - indigo, - deepPurple, - pink, - red, - orange, - yellow, - lime, - green, - cyan, - slateGray -} +enum ImmichColorPreset { indigo, deepPurple, pink, red, orange, yellow, lime, green, cyan, slateGray } const ImmichColorPreset defaultColorPreset = ImmichColorPreset.indigo; const String defaultColorPresetName = "indigo"; diff --git a/mobile/lib/constants/constants.dart b/mobile/lib/constants/constants.dart index 6d98152efc..b3d9d138c4 100644 --- a/mobile/lib/constants/constants.dart +++ b/mobile/lib/constants/constants.dart @@ -10,12 +10,20 @@ const int kSyncEventBatchSize = 5000; const int kFetchLocalAssetsBatchSize = 40000; // Hash batch limits -const int kBatchHashFileLimit = 128; +const int kBatchHashFileLimit = 256; const int kBatchHashSizeLimit = 1024 * 1024 * 1024; // 1GB // Secure storage keys const String kSecuredPinCode = "secured_pin_code"; +// background_downloader task groups +const String kManualUploadGroup = 'manual_upload_group'; +const String kBackupGroup = 'backup_group'; +const String kBackupLivePhotoGroup = 'backup_live_photo_group'; +const String kDownloadGroupImage = 'group_image'; +const String kDownloadGroupVideo = 'group_video'; +const String kDownloadGroupLivePhoto = 'group_livephoto'; + // Timeline constants const int kTimelineNoneSegmentSize = 120; const int kTimelineAssetLoadBatchSize = 256; @@ -28,7 +36,11 @@ const String appShareGroupId = "group.app.immich.share"; // add widget identifiers here for new widgets // these are used to force a widget refresh -const List kWidgetNames = [ - 'com.immich.widget.random', - 'com.immich.widget.memory', +// (iOSName, androidFQDN) +const List<(String, String)> kWidgetNames = [ + ('com.immich.widget.random', 'app.alextran.immich.widget.RandomReceiver'), + ('com.immich.widget.memory', 'app.alextran.immich.widget.MemoryReceiver'), ]; + +const double kUploadStatusFailed = -1.0; +const double kUploadStatusCanceled = -2.0; diff --git a/mobile/lib/constants/enums.dart b/mobile/lib/constants/enums.dart index febc71032e..6ec0ce37ea 100644 --- a/mobile/lib/constants/enums.dart +++ b/mobile/lib/constants/enums.dart @@ -1,13 +1,6 @@ -enum SortOrder { - asc, - desc, -} +enum SortOrder { asc, desc } -enum TextSearchType { - context, - filename, - description, -} +enum TextSearchType { context, filename, description } enum AssetVisibilityEnum { timeline, hidden, archive, locked } diff --git a/mobile/lib/constants/filters.dart b/mobile/lib/constants/filters.dart index 61597f08d1..e77bcfbf1f 100644 --- a/mobile/lib/constants/filters.dart +++ b/mobile/lib/constants/filters.dart @@ -2,511 +2,49 @@ import 'package:flutter/material.dart'; const List filters = [ //Original - ColorFilter.matrix([ - 1, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0]), //Vintage - ColorFilter.matrix([ - 0.8, - 0.1, - 0.1, - 0, - 20, - 0.1, - 0.8, - 0.1, - 0, - 20, - 0.1, - 0.1, - 0.8, - 0, - 20, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.8, 0.1, 0.1, 0, 20, 0.1, 0.8, 0.1, 0, 20, 0.1, 0.1, 0.8, 0, 20, 0, 0, 0, 1, 0]), //Mood - ColorFilter.matrix([ - 1.2, - 0.1, - 0.1, - 0, - 10, - 0.1, - 1, - 0.1, - 0, - 10, - 0.1, - 0.1, - 1, - 0, - 10, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.2, 0.1, 0.1, 0, 10, 0.1, 1, 0.1, 0, 10, 0.1, 0.1, 1, 0, 10, 0, 0, 0, 1, 0]), //Crisp - ColorFilter.matrix([ - 1.2, - 0, - 0, - 0, - 0, - 0, - 1.2, - 0, - 0, - 0, - 0, - 0, - 1.2, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.2, 0, 0, 0, 0, 0, 1.2, 0, 0, 0, 0, 0, 1.2, 0, 0, 0, 0, 0, 1, 0]), //Cool - ColorFilter.matrix([ - 0.9, - 0, - 0.2, - 0, - 0, - 0, - 1, - 0.1, - 0, - 0, - 0.1, - 0, - 1.2, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.9, 0, 0.2, 0, 0, 0, 1, 0.1, 0, 0, 0.1, 0, 1.2, 0, 0, 0, 0, 0, 1, 0]), //Blush - ColorFilter.matrix([ - 1.1, - 0.1, - 0.1, - 0, - 10, - 0.1, - 1, - 0.1, - 0, - 10, - 0.1, - 0.1, - 1, - 0, - 5, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.1, 0.1, 0.1, 0, 10, 0.1, 1, 0.1, 0, 10, 0.1, 0.1, 1, 0, 5, 0, 0, 0, 1, 0]), //Sunkissed - ColorFilter.matrix([ - 1.3, - 0, - 0.1, - 0, - 15, - 0, - 1.1, - 0.1, - 0, - 10, - 0, - 0, - 0.9, - 0, - 5, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.3, 0, 0.1, 0, 15, 0, 1.1, 0.1, 0, 10, 0, 0, 0.9, 0, 5, 0, 0, 0, 1, 0]), //Fresh - ColorFilter.matrix([ - 1.2, - 0, - 0, - 0, - 20, - 0, - 1.2, - 0, - 0, - 20, - 0, - 0, - 1.1, - 0, - 20, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.2, 0, 0, 0, 20, 0, 1.2, 0, 0, 20, 0, 0, 1.1, 0, 20, 0, 0, 0, 1, 0]), //Classic - ColorFilter.matrix([ - 1.1, - 0, - -0.1, - 0, - 10, - -0.1, - 1.1, - 0.1, - 0, - 5, - 0, - -0.1, - 1.1, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.1, 0, -0.1, 0, 10, -0.1, 1.1, 0.1, 0, 5, 0, -0.1, 1.1, 0, 0, 0, 0, 0, 1, 0]), //Lomo-ish - ColorFilter.matrix([ - 1.5, - 0, - 0.1, - 0, - 0, - 0, - 1.45, - 0, - 0, - 0, - 0.1, - 0, - 1.3, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.5, 0, 0.1, 0, 0, 0, 1.45, 0, 0, 0, 0.1, 0, 1.3, 0, 0, 0, 0, 0, 1, 0]), //Nashville - ColorFilter.matrix([ - 1.2, - 0.15, - -0.15, - 0, - 15, - 0.1, - 1.1, - 0.1, - 0, - 10, - -0.05, - 0.2, - 1.25, - 0, - 5, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.2, 0.15, -0.15, 0, 15, 0.1, 1.1, 0.1, 0, 10, -0.05, 0.2, 1.25, 0, 5, 0, 0, 0, 1, 0]), //Valencia - ColorFilter.matrix([ - 1.15, - 0.1, - 0.1, - 0, - 20, - 0.1, - 1.1, - 0, - 0, - 10, - 0.1, - 0.1, - 1.2, - 0, - 5, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.15, 0.1, 0.1, 0, 20, 0.1, 1.1, 0, 0, 10, 0.1, 0.1, 1.2, 0, 5, 0, 0, 0, 1, 0]), //Clarendon - ColorFilter.matrix([ - 1.2, - 0, - 0, - 0, - 10, - 0, - 1.25, - 0, - 0, - 10, - 0, - 0, - 1.3, - 0, - 10, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.2, 0, 0, 0, 10, 0, 1.25, 0, 0, 10, 0, 0, 1.3, 0, 10, 0, 0, 0, 1, 0]), //Moon - ColorFilter.matrix([ - 0.33, - 0.33, - 0.33, - 0, - 0, - 0.33, - 0.33, - 0.33, - 0, - 0, - 0.33, - 0.33, - 0.33, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.33, 0.33, 0.33, 0, 0, 0.33, 0.33, 0.33, 0, 0, 0.33, 0.33, 0.33, 0, 0, 0, 0, 0, 1, 0]), //Willow - ColorFilter.matrix([ - 0.5, - 0.5, - 0.5, - 0, - 20, - 0.5, - 0.5, - 0.5, - 0, - 20, - 0.5, - 0.5, - 0.5, - 0, - 20, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.5, 0.5, 0.5, 0, 20, 0.5, 0.5, 0.5, 0, 20, 0.5, 0.5, 0.5, 0, 20, 0, 0, 0, 1, 0]), //Kodak - ColorFilter.matrix([ - 1.3, - 0.1, - -0.1, - 0, - 10, - 0, - 1.25, - 0.1, - 0, - 10, - 0, - -0.1, - 1.1, - 0, - 5, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.3, 0.1, -0.1, 0, 10, 0, 1.25, 0.1, 0, 10, 0, -0.1, 1.1, 0, 5, 0, 0, 0, 1, 0]), //Frost - ColorFilter.matrix([ - 0.8, - 0.2, - 0.1, - 0, - 0, - 0.2, - 1.1, - 0.1, - 0, - 0, - 0.1, - 0.1, - 1.2, - 0, - 10, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.8, 0.2, 0.1, 0, 0, 0.2, 1.1, 0.1, 0, 0, 0.1, 0.1, 1.2, 0, 10, 0, 0, 0, 1, 0]), //Night Vision - ColorFilter.matrix([ - 0.1, - 0.95, - 0.2, - 0, - 0, - 0.1, - 1.5, - 0.1, - 0, - 0, - 0.2, - 0.7, - 0, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.1, 0.95, 0.2, 0, 0, 0.1, 1.5, 0.1, 0, 0, 0.2, 0.7, 0, 0, 0, 0, 0, 0, 1, 0]), //Sunset - ColorFilter.matrix([ - 1.5, - 0.2, - 0, - 0, - 0, - 0.1, - 0.9, - 0.1, - 0, - 0, - -0.1, - -0.2, - 1.3, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.5, 0.2, 0, 0, 0, 0.1, 0.9, 0.1, 0, 0, -0.1, -0.2, 1.3, 0, 0, 0, 0, 0, 1, 0]), //Noir - ColorFilter.matrix([ - 1.3, - -0.3, - 0.1, - 0, - 0, - -0.1, - 1.2, - -0.1, - 0, - 0, - 0.1, - -0.2, - 1.3, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.3, -0.3, 0.1, 0, 0, -0.1, 1.2, -0.1, 0, 0, 0.1, -0.2, 1.3, 0, 0, 0, 0, 0, 1, 0]), //Dreamy - ColorFilter.matrix([ - 1.1, - 0.1, - 0.1, - 0, - 0, - 0.1, - 1.1, - 0.1, - 0, - 0, - 0.1, - 0.1, - 1.1, - 0, - 15, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.1, 0.1, 0.1, 0, 0, 0.1, 1.1, 0.1, 0, 0, 0.1, 0.1, 1.1, 0, 15, 0, 0, 0, 1, 0]), //Sepia - ColorFilter.matrix([ - 0.393, - 0.769, - 0.189, - 0, - 0, - 0.349, - 0.686, - 0.168, - 0, - 0, - 0.272, - 0.534, - 0.131, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.393, 0.769, 0.189, 0, 0, 0.349, 0.686, 0.168, 0, 0, 0.272, 0.534, 0.131, 0, 0, 0, 0, 0, 1, 0]), //Radium ColorFilter.matrix([ 1.438, @@ -554,212 +92,23 @@ const List filters = [ 0, ]), //Purple Haze - ColorFilter.matrix([ - 1.3, - 0, - 1.2, - 0, - 0, - 0, - 1.1, - 0, - 0, - 0, - 0.2, - 0, - 1.3, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.3, 0, 1.2, 0, 0, 0, 1.1, 0, 0, 0, 0.2, 0, 1.3, 0, 0, 0, 0, 0, 1, 0]), //Lemonade - ColorFilter.matrix([ - 1.2, - 0.1, - 0, - 0, - 0, - 0, - 1.1, - 0.2, - 0, - 0, - 0.1, - 0, - 0.7, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.2, 0.1, 0, 0, 0, 0, 1.1, 0.2, 0, 0, 0.1, 0, 0.7, 0, 0, 0, 0, 0, 1, 0]), //Caramel - ColorFilter.matrix([ - 1.6, - 0.2, - 0, - 0, - 0, - 0.1, - 1.3, - 0.1, - 0, - 0, - 0, - 0.1, - 0.9, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.6, 0.2, 0, 0, 0, 0.1, 1.3, 0.1, 0, 0, 0, 0.1, 0.9, 0, 0, 0, 0, 0, 1, 0]), //Peachy - ColorFilter.matrix([ - 1.3, - 0.5, - 0, - 0, - 0, - 0.2, - 1.1, - 0.3, - 0, - 0, - 0.1, - 0.1, - 1.2, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.3, 0.5, 0, 0, 0, 0.2, 1.1, 0.3, 0, 0, 0.1, 0.1, 1.2, 0, 0, 0, 0, 0, 1, 0]), //Neon - ColorFilter.matrix([ - 1, - 0, - 1, - 0, - 0, - 0, - 2, - 0, - 0, - 0, - 0, - 0, - 3, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 1, 0]), //Cold Morning - ColorFilter.matrix([ - 0.9, - 0.1, - 0.2, - 0, - 0, - 0, - 1, - 0.1, - 0, - 0, - 0.1, - 0, - 1.2, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.9, 0.1, 0.2, 0, 0, 0, 1, 0.1, 0, 0, 0.1, 0, 1.2, 0, 0, 0, 0, 0, 1, 0]), //Lush - ColorFilter.matrix([ - 0.9, - 0.2, - 0, - 0, - 0, - 0, - 1.2, - 0, - 0, - 0, - 0, - 0, - 1.1, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.9, 0.2, 0, 0, 0, 0, 1.2, 0, 0, 0, 0, 0, 1.1, 0, 0, 0, 0, 0, 1, 0]), //Urban Neon - ColorFilter.matrix([ - 1.1, - 0, - 0.3, - 0, - 0, - 0, - 0.9, - 0.3, - 0, - 0, - 0.3, - 0.1, - 1.2, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([1.1, 0, 0.3, 0, 0, 0, 0.9, 0.3, 0, 0, 0.3, 0.1, 1.2, 0, 0, 0, 0, 0, 1, 0]), //Monochrome - ColorFilter.matrix([ - 0.6, - 0.2, - 0.2, - 0, - 0, - 0.2, - 0.6, - 0.2, - 0, - 0, - 0.2, - 0.2, - 0.7, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]), + ColorFilter.matrix([0.6, 0.2, 0.2, 0, 0, 0.2, 0.6, 0.2, 0, 0, 0.2, 0.2, 0.7, 0, 0, 0, 0, 0, 1, 0]), ]; const List filterNames = [ diff --git a/mobile/lib/constants/locales.dart b/mobile/lib/constants/locales.dart index b2d8df525b..f3c24384b0 100644 --- a/mobile/lib/constants/locales.dart +++ b/mobile/lib/constants/locales.dart @@ -7,10 +7,8 @@ const Map locales = { 'Arabic (ar)': Locale('ar'), 'Bulgarian (bg)': Locale('bg'), 'Catalan (ca)': Locale('ca'), - 'Chinese Simplified (zh_CN)': - Locale.fromSubtags(languageCode: 'zh', scriptCode: 'SIMPLIFIED'), - 'Chinese Traditional (zh_TW)': - Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant'), + 'Chinese Simplified (zh_CN)': Locale.fromSubtags(languageCode: 'zh', scriptCode: 'SIMPLIFIED'), + 'Chinese Traditional (zh_TW)': Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant'), 'Croatian (hr)': Locale('hr'), 'Czech (cs)': Locale('cs'), 'Danish (da)': Locale('da'), @@ -37,10 +35,8 @@ const Map locales = { 'Portuguese (pt)': Locale('pt'), 'Romanian (ro)': Locale('ro'), 'Russian (ru)': Locale('ru'), - 'Serbian Cyrillic (sr_Cyrl)': - Locale.fromSubtags(languageCode: 'sr', scriptCode: 'Cyrl'), - 'Serbian Latin (sr_Latn)': - Locale.fromSubtags(languageCode: 'sr', scriptCode: 'Latn'), + 'Serbian Cyrillic (sr_Cyrl)': Locale.fromSubtags(languageCode: 'sr', scriptCode: 'Cyrl'), + 'Serbian Latin (sr_Latn)': Locale.fromSubtags(languageCode: 'sr', scriptCode: 'Latn'), 'Slovak (sk)': Locale('sk'), 'Slovenian (sl)': Locale('sl'), 'Spanish (es)': Locale('es'), @@ -55,7 +51,4 @@ const Map locales = { const String translationsPath = 'assets/i18n'; -const List localesNotSupportedByOverpass = [ - Locale('el', 'GR'), - Locale('sr', 'Cyrl'), -]; +const List localesNotSupportedByOverpass = [Locale('el', 'GR'), Locale('sr', 'Cyrl')]; diff --git a/mobile/lib/domain/models/asset/base_asset.model.dart b/mobile/lib/domain/models/asset/base_asset.model.dart index 356b64cffc..4d40be2d32 100644 --- a/mobile/lib/domain/models/asset/base_asset.model.dart +++ b/mobile/lib/domain/models/asset/base_asset.model.dart @@ -9,11 +9,7 @@ enum AssetType { audio, } -enum AssetState { - local, - remote, - merged, -} +enum AssetState { local, remote, merged } sealed class BaseAsset { final String name; @@ -53,10 +49,8 @@ sealed class BaseAsset { return const Duration(); } - bool get hasRemote => - storage == AssetState.remote || storage == AssetState.merged; - bool get hasLocal => - storage == AssetState.local || storage == AssetState.merged; + bool get hasRemote => storage == AssetState.remote || storage == AssetState.merged; + bool get hasLocal => storage == AssetState.local || storage == AssetState.merged; bool get isLocalOnly => storage == AssetState.local; bool get isRemoteOnly => storage == AssetState.remote; diff --git a/mobile/lib/domain/models/asset/local_asset.model.dart b/mobile/lib/domain/models/asset/local_asset.model.dart index 3466a0f25b..9cd20acb0a 100644 --- a/mobile/lib/domain/models/asset/local_asset.model.dart +++ b/mobile/lib/domain/models/asset/local_asset.model.dart @@ -22,8 +22,7 @@ class LocalAsset extends BaseAsset { }); @override - AssetState get storage => - remoteId == null ? AssetState.local : AssetState.merged; + AssetState get storage => remoteId == null ? AssetState.local : AssetState.merged; @override String get heroTag => '${id}_${remoteId ?? checksum}'; @@ -54,8 +53,7 @@ class LocalAsset extends BaseAsset { } @override - int get hashCode => - super.hashCode ^ id.hashCode ^ remoteId.hashCode ^ orientation.hashCode; + int get hashCode => super.hashCode ^ id.hashCode ^ remoteId.hashCode ^ orientation.hashCode; LocalAsset copyWith({ String? id, diff --git a/mobile/lib/domain/models/asset/remote_asset.model.dart b/mobile/lib/domain/models/asset/remote_asset.model.dart index 760a16170b..8648255167 100644 --- a/mobile/lib/domain/models/asset/remote_asset.model.dart +++ b/mobile/lib/domain/models/asset/remote_asset.model.dart @@ -1,11 +1,6 @@ part of 'base_asset.model.dart'; -enum AssetVisibility { - timeline, - hidden, - archive, - locked, -} +enum AssetVisibility { timeline, hidden, archive, locked } // Model for an asset stored in the server class RemoteAsset extends BaseAsset { @@ -15,7 +10,6 @@ class RemoteAsset extends BaseAsset { final AssetVisibility visibility; final String ownerId; final String? stackId; - final int stackCount; const RemoteAsset({ required this.id, @@ -34,12 +28,10 @@ class RemoteAsset extends BaseAsset { this.visibility = AssetVisibility.timeline, super.livePhotoVideoId, this.stackId, - this.stackCount = 0, }); @override - AssetState get storage => - localId == null ? AssetState.remote : AssetState.merged; + AssetState get storage => localId == null ? AssetState.remote : AssetState.merged; @override String get heroTag => '${localId ?? checksum}_$id'; @@ -61,7 +53,6 @@ class RemoteAsset extends BaseAsset { thumbHash: ${thumbHash ?? ""}, visibility: $visibility, stackId: ${stackId ?? ""}, - stackCount: $stackCount, checksum: $checksum, livePhotoVideoId: ${livePhotoVideoId ?? ""}, }'''; @@ -77,8 +68,7 @@ class RemoteAsset extends BaseAsset { ownerId == other.ownerId && thumbHash == other.thumbHash && visibility == other.visibility && - stackId == other.stackId && - stackCount == other.stackCount; + stackId == other.stackId; } @override @@ -89,8 +79,7 @@ class RemoteAsset extends BaseAsset { localId.hashCode ^ thumbHash.hashCode ^ visibility.hashCode ^ - stackId.hashCode ^ - stackCount.hashCode; + stackId.hashCode; RemoteAsset copyWith({ String? id, @@ -109,7 +98,6 @@ class RemoteAsset extends BaseAsset { AssetVisibility? visibility, String? livePhotoVideoId, String? stackId, - int? stackCount, }) { return RemoteAsset( id: id ?? this.id, @@ -128,7 +116,6 @@ class RemoteAsset extends BaseAsset { visibility: visibility ?? this.visibility, livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, stackId: stackId ?? this.stackId, - stackCount: stackCount ?? this.stackCount, ); } } diff --git a/mobile/lib/domain/models/asset_face.model.dart b/mobile/lib/domain/models/asset_face.model.dart new file mode 100644 index 0000000000..f432b923e3 --- /dev/null +++ b/mobile/lib/domain/models/asset_face.model.dart @@ -0,0 +1,98 @@ +// Model for an asset face stored in the server +class AssetFace { + final String id; + final String assetId; + final String? personId; + final int imageWidth; + final int imageHeight; + final int boundingBoxX1; + final int boundingBoxY1; + final int boundingBoxX2; + final int boundingBoxY2; + final String sourceType; + + const AssetFace({ + required this.id, + required this.assetId, + this.personId, + required this.imageWidth, + required this.imageHeight, + required this.boundingBoxX1, + required this.boundingBoxY1, + required this.boundingBoxX2, + required this.boundingBoxY2, + required this.sourceType, + }); + + AssetFace copyWith({ + String? id, + String? assetId, + String? personId, + int? imageWidth, + int? imageHeight, + int? boundingBoxX1, + int? boundingBoxY1, + int? boundingBoxX2, + int? boundingBoxY2, + String? sourceType, + }) { + return AssetFace( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId ?? this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + } + + @override + String toString() { + return '''AssetFace { + id: $id, + assetId: $assetId, + personId: ${personId ?? ""}, + imageWidth: $imageWidth, + imageHeight: $imageHeight, + boundingBoxX1: $boundingBoxX1, + boundingBoxY1: $boundingBoxY1, + boundingBoxX2: $boundingBoxX2, + boundingBoxY2: $boundingBoxY2, + sourceType: $sourceType, +}'''; + } + + @override + bool operator ==(covariant AssetFace other) { + if (identical(this, other)) return true; + + return other.id == id && + other.assetId == assetId && + other.personId == personId && + other.imageWidth == imageWidth && + other.imageHeight == imageHeight && + other.boundingBoxX1 == boundingBoxX1 && + other.boundingBoxY1 == boundingBoxY1 && + other.boundingBoxX2 == boundingBoxX2 && + other.boundingBoxY2 == boundingBoxY2 && + other.sourceType == sourceType; + } + + @override + int get hashCode { + return id.hashCode ^ + assetId.hashCode ^ + personId.hashCode ^ + imageWidth.hashCode ^ + imageHeight.hashCode ^ + boundingBoxX1.hashCode ^ + boundingBoxY1.hashCode ^ + boundingBoxX2.hashCode ^ + boundingBoxY2.hashCode ^ + sourceType.hashCode; + } +} diff --git a/mobile/lib/domain/models/device_asset.model.dart b/mobile/lib/domain/models/device_asset.model.dart index 2ec56b0d80..a404f5a9e2 100644 --- a/mobile/lib/domain/models/device_asset.model.dart +++ b/mobile/lib/domain/models/device_asset.model.dart @@ -5,19 +5,13 @@ class DeviceAsset { final Uint8List hash; final DateTime modifiedTime; - const DeviceAsset({ - required this.assetId, - required this.hash, - required this.modifiedTime, - }); + const DeviceAsset({required this.assetId, required this.hash, required this.modifiedTime}); @override bool operator ==(covariant DeviceAsset other) { if (identical(this, other)) return true; - return other.assetId == assetId && - other.hash == hash && - other.modifiedTime == modifiedTime; + return other.assetId == assetId && other.hash == hash && other.modifiedTime == modifiedTime; } @override @@ -30,11 +24,7 @@ class DeviceAsset { return 'DeviceAsset(assetId: $assetId, hash: $hash, modifiedTime: $modifiedTime)'; } - DeviceAsset copyWith({ - String? assetId, - Uint8List? hash, - DateTime? modifiedTime, - }) { + DeviceAsset copyWith({String? assetId, Uint8List? hash, DateTime? modifiedTime}) { return DeviceAsset( assetId: assetId ?? this.assetId, hash: hash ?? this.hash, diff --git a/mobile/lib/domain/models/exif.model.dart b/mobile/lib/domain/models/exif.model.dart index b73aa4cae1..6e94c44650 100644 --- a/mobile/lib/domain/models/exif.model.dart +++ b/mobile/lib/domain/models/exif.model.dart @@ -25,8 +25,7 @@ class ExifInfo { final int? iso; final double? exposureSeconds; - bool get hasCoordinates => - latitude != null && longitude != null && latitude != 0 && longitude != 0; + bool get hasCoordinates => latitude != null && longitude != null && latitude != 0 && longitude != 0; String get exposureTime { if (exposureSeconds == null) { diff --git a/mobile/lib/domain/models/log.model.dart b/mobile/lib/domain/models/log.model.dart index dffd1cccda..9902ca04ca 100644 --- a/mobile/lib/domain/models/log.model.dart +++ b/mobile/lib/domain/models/log.model.dart @@ -1,16 +1,5 @@ /// Log levels according to dart logging [Level] -enum LogLevel { - all, - finest, - finer, - fine, - config, - info, - warning, - severe, - shout, - off, -} +enum LogLevel { all, finest, finer, fine, config, info, warning, severe, shout, off } class LogMessage { final String message; @@ -43,12 +32,7 @@ class LogMessage { @override int get hashCode { - return message.hashCode ^ - level.hashCode ^ - createdAt.hashCode ^ - logger.hashCode ^ - error.hashCode ^ - stack.hashCode; + return message.hashCode ^ level.hashCode ^ createdAt.hashCode ^ logger.hashCode ^ error.hashCode ^ stack.hashCode; } @override diff --git a/mobile/lib/domain/models/memory.model.dart b/mobile/lib/domain/models/memory.model.dart index ba2a43428f..40117c5ac6 100644 --- a/mobile/lib/domain/models/memory.model.dart +++ b/mobile/lib/domain/models/memory.model.dart @@ -13,34 +13,23 @@ enum MemoryTypeEnum { class MemoryData { final int year; - const MemoryData({ - required this.year, - }); + const MemoryData({required this.year}); - MemoryData copyWith({ - int? year, - }) { - return MemoryData( - year: year ?? this.year, - ); + MemoryData copyWith({int? year}) { + return MemoryData(year: year ?? this.year); } Map toMap() { - return { - 'year': year, - }; + return {'year': year}; } factory MemoryData.fromMap(Map map) { - return MemoryData( - year: map['year'] as int, - ); + return MemoryData(year: map['year'] as int); } String toJson() => json.encode(toMap()); - factory MemoryData.fromJson(String source) => - MemoryData.fromMap(json.decode(source) as Map); + factory MemoryData.fromJson(String source) => MemoryData.fromMap(json.decode(source) as Map); @override String toString() => 'MemoryData(year: $year)'; @@ -124,7 +113,21 @@ class DriftMemory { @override String toString() { - return 'Memory(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, ownerId: $ownerId, type: $type, data: $data, isSaved: $isSaved, memoryAt: $memoryAt, seenAt: $seenAt, showAt: $showAt, hideAt: $hideAt, assets: $assets)'; + return '''Memory { + id: $id, + createdAt: $createdAt, + updatedAt: $updatedAt, + deletedAt: ${deletedAt ?? ""}, + ownerId: $ownerId, + type: $type, + data: $data, + isSaved: $isSaved, + memoryAt: $memoryAt, + seenAt: ${seenAt ?? ""}, + showAt: ${showAt ?? ""}, + hideAt: ${hideAt ?? ""}, + assets: $assets +}'''; } @override diff --git a/mobile/lib/domain/models/person.model.dart b/mobile/lib/domain/models/person.model.dart index 10453f768d..7559720c45 100644 --- a/mobile/lib/domain/models/person.model.dart +++ b/mobile/lib/domain/models/person.model.dart @@ -1,7 +1,8 @@ import 'dart:convert'; -class Person { - const Person({ +// TODO: Remove PersonDto once Isar is removed +class PersonDto { + const PersonDto({ required this.id, this.birthDate, required this.isHidden, @@ -22,7 +23,7 @@ class Person { return 'Person(id: $id, birthDate: $birthDate, isHidden: $isHidden, name: $name, thumbnailPath: $thumbnailPath, updatedAt: $updatedAt)'; } - Person copyWith({ + PersonDto copyWith({ String? id, DateTime? birthDate, bool? isHidden, @@ -30,7 +31,7 @@ class Person { String? thumbnailPath, DateTime? updatedAt, }) { - return Person( + return PersonDto( id: id ?? this.id, birthDate: birthDate ?? this.birthDate, isHidden: isHidden ?? this.isHidden, @@ -51,28 +52,23 @@ class Person { }; } - factory Person.fromMap(Map map) { - return Person( + factory PersonDto.fromMap(Map map) { + return PersonDto( id: map['id'] as String, - birthDate: map['birthDate'] != null - ? DateTime.fromMillisecondsSinceEpoch(map['birthDate'] as int) - : null, + birthDate: map['birthDate'] != null ? DateTime.fromMillisecondsSinceEpoch(map['birthDate'] as int) : null, isHidden: map['isHidden'] as bool, name: map['name'] as String, thumbnailPath: map['thumbnailPath'] as String, - updatedAt: map['updatedAt'] != null - ? DateTime.fromMillisecondsSinceEpoch(map['updatedAt'] as int) - : null, + updatedAt: map['updatedAt'] != null ? DateTime.fromMillisecondsSinceEpoch(map['updatedAt'] as int) : null, ); } String toJson() => json.encode(toMap()); - factory Person.fromJson(String source) => - Person.fromMap(json.decode(source) as Map); + factory PersonDto.fromJson(String source) => PersonDto.fromMap(json.decode(source) as Map); @override - bool operator ==(covariant Person other) { + bool operator ==(covariant PersonDto other) { if (identical(this, other)) return true; return other.id == id && @@ -93,3 +89,102 @@ class Person { updatedAt.hashCode; } } + +// Model for a person stored in the server +class DriftPerson { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String name; + final String? faceAssetId; + final bool isFavorite; + final bool isHidden; + final String? color; + final DateTime? birthDate; + + const DriftPerson({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.isFavorite, + required this.isHidden, + required this.color, + this.birthDate, + }); + + DriftPerson copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + String? faceAssetId, + bool? isFavorite, + bool? isHidden, + String? color, + DateTime? birthDate, + }) { + return DriftPerson( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId ?? this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color ?? this.color, + birthDate: birthDate ?? this.birthDate, + ); + } + + @override + String toString() { + return '''Person { + id: $id, + createdAt: $createdAt, + updatedAt: $updatedAt, + ownerId: $ownerId, + name: $name, + faceAssetId: ${faceAssetId ?? ""}, + isFavorite: $isFavorite, + isHidden: $isHidden, + color: ${color ?? ""}, + birthDate: ${birthDate ?? ""} +}'''; + } + + @override + bool operator ==(covariant DriftPerson other) { + if (identical(this, other)) return true; + + return other.id == id && + other.createdAt == createdAt && + other.updatedAt == updatedAt && + other.ownerId == ownerId && + other.name == name && + other.faceAssetId == faceAssetId && + other.isFavorite == isFavorite && + other.isHidden == isHidden && + other.color == color && + other.birthDate == birthDate; + } + + @override + int get hashCode { + return id.hashCode ^ + createdAt.hashCode ^ + updatedAt.hashCode ^ + ownerId.hashCode ^ + name.hashCode ^ + faceAssetId.hashCode ^ + isFavorite.hashCode ^ + isHidden.hashCode ^ + color.hashCode ^ + birthDate.hashCode; + } +} diff --git a/mobile/lib/domain/models/search_result.model.dart b/mobile/lib/domain/models/search_result.model.dart index e8c9429432..bae8b8e821 100644 --- a/mobile/lib/domain/models/search_result.model.dart +++ b/mobile/lib/domain/models/search_result.model.dart @@ -5,21 +5,12 @@ class SearchResult { final List assets; final int? nextPage; - const SearchResult({ - required this.assets, - this.nextPage, - }); + const SearchResult({required this.assets, this.nextPage}); int get totalAssets => assets.length; - SearchResult copyWith({ - List? assets, - int? nextPage, - }) { - return SearchResult( - assets: assets ?? this.assets, - nextPage: nextPage ?? this.nextPage, - ); + SearchResult copyWith({List? assets, int? nextPage}) { + return SearchResult(assets: assets ?? this.assets, nextPage: nextPage ?? this.nextPage); } @override diff --git a/mobile/lib/domain/models/setting.model.dart b/mobile/lib/domain/models/setting.model.dart index 99246c31a1..f427d93285 100644 --- a/mobile/lib/domain/models/setting.model.dart +++ b/mobile/lib/domain/models/setting.model.dart @@ -8,7 +8,7 @@ enum Setting { loadOriginalVideo(StoreKey.loadOriginalVideo, false), preferRemoteImage(StoreKey.preferRemoteImage, false), advancedTroubleshooting(StoreKey.advancedTroubleshooting, false), - ; + enableBackup(StoreKey.enableBackup, false); const Setting(this.storeKey, this.defaultValue); diff --git a/mobile/lib/domain/models/stack.model.dart b/mobile/lib/domain/models/stack.model.dart index d7faf07a22..d5ccf5558d 100644 --- a/mobile/lib/domain/models/stack.model.dart +++ b/mobile/lib/domain/models/stack.model.dart @@ -1,5 +1,3 @@ -import 'dart:convert'; - // Model for a stack stored in the server class Stack { final String id; @@ -16,13 +14,7 @@ class Stack { required this.primaryAssetId, }); - Stack copyWith({ - String? id, - DateTime? createdAt, - DateTime? updatedAt, - String? ownerId, - String? primaryAssetId, - }) { + Stack copyWith({String? id, DateTime? createdAt, DateTime? updatedAt, String? ownerId, String? primaryAssetId}) { return Stack( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -32,34 +24,15 @@ class Stack { ); } - Map toMap() { - return { - 'id': id, - 'createdAt': createdAt.millisecondsSinceEpoch, - 'updatedAt': updatedAt.millisecondsSinceEpoch, - 'ownerId': ownerId, - 'primaryAssetId': primaryAssetId, - }; - } - - factory Stack.fromMap(Map map) { - return Stack( - id: map['id'] as String, - createdAt: DateTime.fromMillisecondsSinceEpoch(map['createdAt'] as int), - updatedAt: DateTime.fromMillisecondsSinceEpoch(map['updatedAt'] as int), - ownerId: map['ownerId'] as String, - primaryAssetId: map['primaryAssetId'] as String, - ); - } - - String toJson() => json.encode(toMap()); - - factory Stack.fromJson(String source) => - Stack.fromMap(json.decode(source) as Map); - @override String toString() { - return 'Stack(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, ownerId: $ownerId, primaryAssetId: $primaryAssetId)'; + return '''Stack { + id: $id, + createdAt: $createdAt, + updatedAt: $updatedAt, + ownerId: $ownerId, + primaryAssetId: $primaryAssetId +}'''; } @override @@ -75,11 +48,7 @@ class Stack { @override int get hashCode { - return id.hashCode ^ - createdAt.hashCode ^ - updatedAt.hashCode ^ - ownerId.hashCode ^ - primaryAssetId.hashCode; + return id.hashCode ^ createdAt.hashCode ^ updatedAt.hashCode ^ ownerId.hashCode ^ primaryAssetId.hashCode; } } @@ -88,19 +57,13 @@ class StackResponse { final String primaryAssetId; final List assetIds; - const StackResponse({ - required this.id, - required this.primaryAssetId, - required this.assetIds, - }); + const StackResponse({required this.id, required this.primaryAssetId, required this.assetIds}); @override bool operator ==(covariant StackResponse other) { if (identical(this, other)) return true; - return other.id == id && - other.primaryAssetId == primaryAssetId && - other.assetIds == assetIds; + return other.id == id && other.primaryAssetId == primaryAssetId && other.assetIds == assetIds; } @override diff --git a/mobile/lib/domain/models/store.model.dart b/mobile/lib/domain/models/store.model.dart index 61c5807ba8..e4e316b814 100644 --- a/mobile/lib/domain/models/store.model.dart +++ b/mobile/lib/domain/models/store.model.dart @@ -70,7 +70,10 @@ enum StoreKey { // Experimental stuff photoManagerCustomFilter._(1000), betaPromptShown._(1001), - betaTimeline._(1002); + betaTimeline._(1002), + enableBackup._(1003), + useWifiForUploadVideos._(1004), + useWifiForUploadPhotos._(1005); const StoreKey._(this.id); final int id; diff --git a/mobile/lib/domain/models/timeline.model.dart b/mobile/lib/domain/models/timeline.model.dart index f3b688b8b8..d4cc5ab5c6 100644 --- a/mobile/lib/domain/models/timeline.model.dart +++ b/mobile/lib/domain/models/timeline.model.dart @@ -1,17 +1,8 @@ import 'package:immich_mobile/domain/utils/event_stream.dart'; -enum GroupAssetsBy { - day, - month, - none; -} +enum GroupAssetsBy { day, month, auto, none } -enum HeaderType { - none, - month, - day, - monthAndDay; -} +enum HeaderType { none, month, day, monthAndDay } class Bucket { final int assetCount; @@ -44,3 +35,13 @@ class TimeBucket extends Bucket { class TimelineReloadEvent extends Event { const TimelineReloadEvent(); } + +class ScrollToTopEvent extends Event { + const ScrollToTopEvent(); +} + +class ScrollToDateEvent extends Event { + final DateTime date; + + const ScrollToDateEvent(this.date); +} diff --git a/mobile/lib/domain/models/user.model.dart b/mobile/lib/domain/models/user.model.dart index abf2e5620b..b0a66f7d70 100644 --- a/mobile/lib/domain/models/user.model.dart +++ b/mobile/lib/domain/models/user.model.dart @@ -1,3 +1,6 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'dart:convert'; + import 'package:immich_mobile/domain/models/user_metadata.model.dart'; // TODO: Rename to User once Isar is removed @@ -8,7 +11,6 @@ class UserDto { final bool isAdmin; final DateTime updatedAt; - final String? profileImagePath; final AvatarColor avatarColor; final bool memoryEnabled; @@ -22,18 +24,22 @@ class UserDto { bool get hasQuota => quotaSizeInBytes > 0; + final bool hasProfileImage; + final DateTime profileChangedAt; + const UserDto({ required this.id, required this.email, required this.name, required this.isAdmin, required this.updatedAt, - this.profileImagePath, + required this.profileChangedAt, this.avatarColor = AvatarColor.primary, this.memoryEnabled = true, this.inTimeline = false, this.isPartnerSharedBy = false, this.isPartnerSharedWith = false, + this.hasProfileImage = false, this.quotaUsageInBytes = 0, this.quotaSizeInBytes = 0, }); @@ -46,14 +52,13 @@ email: $email, name: $name, isAdmin: $isAdmin, updatedAt: $updatedAt, -profileImagePath: ${profileImagePath ?? ''}, avatarColor: $avatarColor, memoryEnabled: $memoryEnabled, inTimeline: $inTimeline, isPartnerSharedBy: $isPartnerSharedBy, isPartnerSharedWith: $isPartnerSharedWith, -quotaUsageInBytes: $quotaUsageInBytes, -quotaSizeInBytes: $quotaSizeInBytes, +hasProfileImage: $hasProfileImage +profileChangedAt: $profileChangedAt }'''; } @@ -63,30 +68,27 @@ quotaSizeInBytes: $quotaSizeInBytes, String? name, bool? isAdmin, DateTime? updatedAt, - String? profileImagePath, AvatarColor? avatarColor, bool? memoryEnabled, bool? inTimeline, bool? isPartnerSharedBy, bool? isPartnerSharedWith, - int? quotaUsageInBytes, - int? quotaSizeInBytes, - }) => - UserDto( - id: id ?? this.id, - email: email ?? this.email, - name: name ?? this.name, - isAdmin: isAdmin ?? this.isAdmin, - updatedAt: updatedAt ?? this.updatedAt, - profileImagePath: profileImagePath ?? this.profileImagePath, - avatarColor: avatarColor ?? this.avatarColor, - memoryEnabled: memoryEnabled ?? this.memoryEnabled, - inTimeline: inTimeline ?? this.inTimeline, - isPartnerSharedBy: isPartnerSharedBy ?? this.isPartnerSharedBy, - isPartnerSharedWith: isPartnerSharedWith ?? this.isPartnerSharedWith, - quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, - quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes, - ); + bool? hasProfileImage, + DateTime? profileChangedAt, + }) => UserDto( + id: id ?? this.id, + email: email ?? this.email, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + updatedAt: updatedAt ?? this.updatedAt, + avatarColor: avatarColor ?? this.avatarColor, + memoryEnabled: memoryEnabled ?? this.memoryEnabled, + inTimeline: inTimeline ?? this.inTimeline, + isPartnerSharedBy: isPartnerSharedBy ?? this.isPartnerSharedBy, + isPartnerSharedWith: isPartnerSharedWith ?? this.isPartnerSharedWith, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + ); @override bool operator ==(covariant UserDto other) { @@ -99,12 +101,11 @@ quotaSizeInBytes: $quotaSizeInBytes, other.name == name && other.isPartnerSharedBy == isPartnerSharedBy && other.isPartnerSharedWith == isPartnerSharedWith && - other.profileImagePath == profileImagePath && other.isAdmin == isAdmin && other.memoryEnabled == memoryEnabled && other.inTimeline == inTimeline && - other.quotaUsageInBytes == quotaUsageInBytes && - other.quotaSizeInBytes == quotaSizeInBytes; + other.hasProfileImage == hasProfileImage && + other.profileChangedAt.isAtSameMomentAs(profileChangedAt); } @override @@ -114,12 +115,83 @@ quotaSizeInBytes: $quotaSizeInBytes, email.hashCode ^ updatedAt.hashCode ^ isAdmin.hashCode ^ - profileImagePath.hashCode ^ avatarColor.hashCode ^ memoryEnabled.hashCode ^ inTimeline.hashCode ^ isPartnerSharedBy.hashCode ^ isPartnerSharedWith.hashCode ^ - quotaUsageInBytes.hashCode ^ - quotaSizeInBytes.hashCode; + hasProfileImage.hashCode ^ + profileChangedAt.hashCode; +} + +class PartnerUserDto { + final String id; + final String email; + final String name; + final bool inTimeline; + + final String? profileImagePath; + + const PartnerUserDto({ + required this.id, + required this.email, + required this.name, + required this.inTimeline, + this.profileImagePath, + }); + + PartnerUserDto copyWith({String? id, String? email, String? name, bool? inTimeline, String? profileImagePath}) { + return PartnerUserDto( + id: id ?? this.id, + email: email ?? this.email, + name: name ?? this.name, + inTimeline: inTimeline ?? this.inTimeline, + profileImagePath: profileImagePath ?? this.profileImagePath, + ); + } + + Map toMap() { + return { + 'id': id, + 'email': email, + 'name': name, + 'inTimeline': inTimeline, + 'profileImagePath': profileImagePath, + }; + } + + factory PartnerUserDto.fromMap(Map map) { + return PartnerUserDto( + id: map['id'] as String, + email: map['email'] as String, + name: map['name'] as String, + inTimeline: map['inTimeline'] as bool, + profileImagePath: map['profileImagePath'] != null ? map['profileImagePath'] as String : null, + ); + } + + String toJson() => json.encode(toMap()); + + factory PartnerUserDto.fromJson(String source) => PartnerUserDto.fromMap(json.decode(source) as Map); + + @override + String toString() { + return 'PartnerUserDto(id: $id, email: $email, name: $name, inTimeline: $inTimeline, profileImagePath: $profileImagePath)'; + } + + @override + bool operator ==(covariant PartnerUserDto other) { + if (identical(this, other)) return true; + + return other.id == id && + other.email == email && + other.name == name && + other.inTimeline == inTimeline && + other.profileImagePath == profileImagePath; + } + + @override + int get hashCode { + return id.hashCode ^ email.hashCode ^ name.hashCode ^ inTimeline.hashCode ^ profileImagePath.hashCode; + } } diff --git a/mobile/lib/domain/models/user_metadata.model.dart b/mobile/lib/domain/models/user_metadata.model.dart index c5c63cad5e..1c371a9d3e 100644 --- a/mobile/lib/domain/models/user_metadata.model.dart +++ b/mobile/lib/domain/models/user_metadata.model.dart @@ -24,18 +24,17 @@ enum AvatarColor { const AvatarColor(this.value); Color toColor({bool isDarkTheme = false}) => switch (this) { - AvatarColor.primary => - isDarkTheme ? const Color(0xFFABCBFA) : const Color(0xFF4250AF), - AvatarColor.pink => const Color.fromARGB(255, 244, 114, 182), - AvatarColor.red => const Color.fromARGB(255, 239, 68, 68), - AvatarColor.yellow => const Color.fromARGB(255, 234, 179, 8), - AvatarColor.blue => const Color.fromARGB(255, 59, 130, 246), - AvatarColor.green => const Color.fromARGB(255, 22, 163, 74), - AvatarColor.purple => const Color.fromARGB(255, 147, 51, 234), - AvatarColor.orange => const Color.fromARGB(255, 234, 88, 12), - AvatarColor.gray => const Color.fromARGB(255, 75, 85, 99), - AvatarColor.amber => const Color.fromARGB(255, 217, 119, 6), - }; + AvatarColor.primary => isDarkTheme ? const Color(0xFFABCBFA) : const Color(0xFF4250AF), + AvatarColor.pink => const Color.fromARGB(255, 244, 114, 182), + AvatarColor.red => const Color.fromARGB(255, 239, 68, 68), + AvatarColor.yellow => const Color.fromARGB(255, 234, 179, 8), + AvatarColor.blue => const Color.fromARGB(255, 59, 130, 246), + AvatarColor.green => const Color.fromARGB(255, 22, 163, 74), + AvatarColor.purple => const Color.fromARGB(255, 147, 51, 234), + AvatarColor.orange => const Color.fromARGB(255, 234, 88, 12), + AvatarColor.gray => const Color.fromARGB(255, 75, 85, 99), + AvatarColor.amber => const Color.fromARGB(255, 217, 119, 6), + }; } class Onboarding { @@ -194,17 +193,9 @@ class License { final String activationKey; final String licenseKey; - const License({ - required this.activatedAt, - required this.activationKey, - required this.licenseKey, - }); + const License({required this.activatedAt, required this.activationKey, required this.licenseKey}); - License copyWith({ - DateTime? activatedAt, - String? activationKey, - String? licenseKey, - }) { + License copyWith({DateTime? activatedAt, String? activationKey, String? licenseKey}) { return License( activatedAt: activatedAt ?? this.activatedAt, activationKey: activationKey ?? this.activationKey, @@ -241,14 +232,11 @@ licenseKey: $licenseKey, bool operator ==(covariant License other) { if (identical(this, other)) return true; - return activatedAt == other.activatedAt && - activationKey == other.activationKey && - licenseKey == other.licenseKey; + return activatedAt == other.activatedAt && activationKey == other.activationKey && licenseKey == other.licenseKey; } @override - int get hashCode => - activatedAt.hashCode ^ activationKey.hashCode ^ licenseKey.hashCode; + int get hashCode => activatedAt.hashCode ^ activationKey.hashCode ^ licenseKey.hashCode; } // Model for a user metadata stored in the server @@ -259,16 +247,11 @@ class UserMetadata { final Preferences? preferences; final License? license; - const UserMetadata({ - required this.userId, - required this.key, - this.onboarding, - this.preferences, - this.license, - }) : assert( - onboarding != null || preferences != null || license != null, - 'One of onboarding, preferences and license must be provided', - ); + const UserMetadata({required this.userId, required this.key, this.onboarding, this.preferences, this.license}) + : assert( + onboarding != null || preferences != null || license != null, + 'One of onboarding, preferences and license must be provided', + ); UserMetadata copyWith({ String? userId, @@ -310,10 +293,6 @@ license: ${license ?? ""}, @override int get hashCode { - return userId.hashCode ^ - key.hashCode ^ - onboarding.hashCode ^ - preferences.hashCode ^ - license.hashCode; + return userId.hashCode ^ key.hashCode ^ onboarding.hashCode ^ preferences.hashCode ^ license.hashCode; } } diff --git a/mobile/lib/domain/services/asset.service.dart b/mobile/lib/domain/services/asset.service.dart index 63b1aad8c1..c8cc61314e 100644 --- a/mobile/lib/domain/services/asset.service.dart +++ b/mobile/lib/domain/services/asset.service.dart @@ -13,15 +13,17 @@ class AssetService { const AssetService({ required RemoteAssetRepository remoteAssetRepository, required DriftLocalAssetRepository localAssetRepository, - }) : _remoteAssetRepository = remoteAssetRepository, - _localAssetRepository = localAssetRepository, - _platform = const LocalPlatform(); + }) : _remoteAssetRepository = remoteAssetRepository, + _localAssetRepository = localAssetRepository, + _platform = const LocalPlatform(); Stream watchAsset(BaseAsset asset) { final id = asset is LocalAsset ? asset.id : (asset as RemoteAsset).id; - return asset is LocalAsset - ? _localAssetRepository.watchAsset(id) - : _remoteAssetRepository.watchAsset(id); + return asset is LocalAsset ? _localAssetRepository.watchAsset(id) : _remoteAssetRepository.watchAsset(id); + } + + Future getRemoteAsset(String id) { + return _remoteAssetRepository.get(id); } Future> getStack(RemoteAsset asset) async { @@ -40,8 +42,7 @@ class AssetService { return null; } - final id = - asset is LocalAsset ? asset.remoteId! : (asset as RemoteAsset).id; + final id = asset is LocalAsset ? asset.remoteId! : (asset as RemoteAsset).id; return _remoteAssetRepository.getExif(id); } @@ -56,8 +57,7 @@ class AssetService { width = exif?.width ?? asset.width?.toDouble(); height = exif?.height ?? asset.height?.toDouble(); } else if (asset is LocalAsset) { - isFlipped = _platform.isAndroid && - (asset.orientation == 90 || asset.orientation == 270); + isFlipped = _platform.isAndroid && (asset.orientation == 90 || asset.orientation == 270); width = asset.width?.toDouble(); height = asset.height?.toDouble(); } else { @@ -76,4 +76,12 @@ class AssetService { Future> getPlaces() { return _remoteAssetRepository.getPlaces(); } + + Future<(int local, int remote)> getAssetCounts() async { + return (await _localAssetRepository.getCount(), await _remoteAssetRepository.getCount()); + } + + Future getLocalHashedCount() { + return _localAssetRepository.getHashedCount(); + } } diff --git a/mobile/lib/domain/services/hash.service.dart b/mobile/lib/domain/services/hash.service.dart index d004041276..2a07320906 100644 --- a/mobile/lib/domain/services/hash.service.dart +++ b/mobile/lib/domain/services/hash.service.dart @@ -25,24 +25,20 @@ class HashService { required NativeSyncApi nativeSyncApi, this.batchSizeLimit = kBatchHashSizeLimit, this.batchFileLimit = kBatchHashFileLimit, - }) : _localAlbumRepository = localAlbumRepository, - _localAssetRepository = localAssetRepository, - _storageRepository = storageRepository, - _nativeSyncApi = nativeSyncApi; + }) : _localAlbumRepository = localAlbumRepository, + _localAssetRepository = localAssetRepository, + _storageRepository = storageRepository, + _nativeSyncApi = nativeSyncApi; Future hashAssets() async { final Stopwatch stopwatch = Stopwatch()..start(); // Sorted by backupSelection followed by isCloud final localAlbums = await _localAlbumRepository.getAll( - sortBy: { - SortLocalAlbumsBy.backupSelection, - SortLocalAlbumsBy.isIosSharedAlbum, - }, + sortBy: {SortLocalAlbumsBy.backupSelection, SortLocalAlbumsBy.isIosSharedAlbum}, ); for (final album in localAlbums) { - final assetsToHash = - await _localAlbumRepository.getAssetsToHash(album.id); + final assetsToHash = await _localAlbumRepository.getAssetsToHash(album.id); if (assetsToHash.isNotEmpty) { await _hashAssets(assetsToHash); } @@ -88,8 +84,7 @@ class HashService { _log.fine("Hashing ${toHash.length} files"); final hashed = []; - final hashes = - await _nativeSyncApi.hashPaths(toHash.map((e) => e.path).toList()); + final hashes = await _nativeSyncApi.hashPaths(toHash.map((e) => e.path).toList()); assert( hashes.length == toHash.length, "Hashes length does not match toHash length: ${hashes.length} != ${toHash.length}", @@ -109,6 +104,7 @@ class HashService { DLog.log("Hashed ${hashed.length}/${toHash.length} assets"); await _localAssetRepository.updateHashes(hashed); + await _storageRepository.clearCache(); } } diff --git a/mobile/lib/domain/services/local_album.service.dart b/mobile/lib/domain/services/local_album.service.dart index 9af12ce595..6c1479fdc9 100644 --- a/mobile/lib/domain/services/local_album.service.dart +++ b/mobile/lib/domain/services/local_album.service.dart @@ -7,11 +7,19 @@ class LocalAlbumService { const LocalAlbumService(this._repository); - Future> getAll() { - return _repository.getAll(); + Future> getAll({Set sortBy = const {}}) { + return _repository.getAll(sortBy: sortBy); } Future getThumbnail(String albumId) { return _repository.getThumbnail(albumId); } + + Future update(LocalAlbum album) { + return _repository.upsert(album); + } + + Future getCount() { + return _repository.getCount(); + } } diff --git a/mobile/lib/domain/services/local_sync.service.dart b/mobile/lib/domain/services/local_sync.service.dart index 8d3b747b37..13ebecfd46 100644 --- a/mobile/lib/domain/services/local_sync.service.dart +++ b/mobile/lib/domain/services/local_sync.service.dart @@ -21,9 +21,9 @@ class LocalSyncService { required DriftLocalAlbumRepository localAlbumRepository, required NativeSyncApi nativeSyncApi, Platform? platform, - }) : _localAlbumRepository = localAlbumRepository, - _nativeSyncApi = nativeSyncApi, - _platform = platform ?? const LocalPlatform(); + }) : _localAlbumRepository = localAlbumRepository, + _nativeSyncApi = nativeSyncApi, + _platform = platform ?? const LocalPlatform(); Future sync({bool full = false}) async { final Stopwatch stopwatch = Stopwatch()..start(); @@ -66,14 +66,11 @@ class LocalSyncService { // On iOS, we need to full sync albums that are marked as cloud as the delta sync // does not include changes for cloud albums. If ignoreIcloudAssets is enabled, // remove the albums from the local database from the previous sync - final cloudAlbums = - deviceAlbums.where((a) => a.isCloud).toLocalAlbums(); + final cloudAlbums = deviceAlbums.where((a) => a.isCloud).toLocalAlbums(); for (final album in cloudAlbums) { final dbAlbum = dbAlbums.firstWhereOrNull((a) => a.id == album.id); if (dbAlbum == null) { - _log.warning( - "Cloud album ${album.name} not found in local database. Skipping sync.", - ); + _log.warning("Cloud album ${album.name} not found in local database. Skipping sync."); continue; } await updateAlbum(dbAlbum, album); @@ -95,8 +92,7 @@ class LocalSyncService { final Stopwatch stopwatch = Stopwatch()..start(); final deviceAlbums = await _nativeSyncApi.getAlbums(); - final dbAlbums = - await _localAlbumRepository.getAll(sortBy: {SortLocalAlbumsBy.id}); + final dbAlbums = await _localAlbumRepository.getAll(sortBy: {SortLocalAlbumsBy.id}); await diffSortedLists( dbAlbums, @@ -120,14 +116,9 @@ class LocalSyncService { try { _log.fine("Adding device album ${album.name}"); - final assets = album.assetCount > 0 - ? await _nativeSyncApi.getAssetsForAlbum(album.id) - : []; + final assets = album.assetCount > 0 ? await _nativeSyncApi.getAssetsForAlbum(album.id) : []; - await _localAlbumRepository.upsert( - album, - toUpsert: assets.toLocalAssets(), - ); + await _localAlbumRepository.upsert(album, toUpsert: assets.toLocalAssets()); _log.fine("Successfully added device album ${album.name}"); } catch (e, s) { _log.warning("Error while adding device album", e, s); @@ -150,9 +141,7 @@ class LocalSyncService { _log.fine("Syncing device album ${dbAlbum.name}"); if (_albumsEqual(deviceAlbum, dbAlbum)) { - _log.fine( - "Device album ${dbAlbum.name} has not changed. Skipping sync.", - ); + _log.fine("Device album ${dbAlbum.name} has not changed. Skipping sync."); return false; } @@ -176,10 +165,7 @@ class LocalSyncService { @visibleForTesting // The [deviceAlbum] is expected to be refreshed before calling this method // with modified time and asset count - Future checkAddition( - LocalAlbum dbAlbum, - LocalAlbum deviceAlbum, - ) async { + Future checkAddition(LocalAlbum dbAlbum, LocalAlbum deviceAlbum) async { try { _log.fine("Fast syncing device album ${dbAlbum.name}"); // Assets has been modified @@ -188,16 +174,12 @@ class LocalSyncService { return false; } - final updatedTime = - (dbAlbum.updatedAt.millisecondsSinceEpoch ~/ 1000) + 1; - final newAssetsCount = - await _nativeSyncApi.getAssetsCountSince(deviceAlbum.id, updatedTime); + final updatedTime = (dbAlbum.updatedAt.millisecondsSinceEpoch ~/ 1000) + 1; + final newAssetsCount = await _nativeSyncApi.getAssetsCountSince(deviceAlbum.id, updatedTime); // Early return if no new assets were found if (newAssetsCount == 0) { - _log.fine( - "No new assets found despite album having changes. Proceeding to full sync for ${dbAlbum.name}", - ); + _log.fine("No new assets found despite album having changes. Proceeding to full sync for ${dbAlbum.name}"); return false; } @@ -207,10 +189,7 @@ class LocalSyncService { return false; } - final newAssets = await _nativeSyncApi.getAssetsForAlbum( - deviceAlbum.id, - updatedTimeCond: updatedTime, - ); + final newAssets = await _nativeSyncApi.getAssetsForAlbum(deviceAlbum.id, updatedTimeCond: updatedTime); await _localAlbumRepository.upsert( deviceAlbum.copyWith(backupSelection: dbAlbum.backupSelection), @@ -230,18 +209,12 @@ class LocalSyncService { Future fullDiff(LocalAlbum dbAlbum, LocalAlbum deviceAlbum) async { try { final assetsInDevice = deviceAlbum.assetCount > 0 - ? await _nativeSyncApi - .getAssetsForAlbum(deviceAlbum.id) - .then((a) => a.toLocalAssets()) - : []; - final assetsInDb = dbAlbum.assetCount > 0 - ? await _localAlbumRepository.getAssets(dbAlbum.id) + ? await _nativeSyncApi.getAssetsForAlbum(deviceAlbum.id).then((a) => a.toLocalAssets()) : []; + final assetsInDb = dbAlbum.assetCount > 0 ? await _localAlbumRepository.getAssets(dbAlbum.id) : []; if (deviceAlbum.assetCount == 0) { - _log.fine( - "Device album ${deviceAlbum.name} is empty. Removing assets from DB.", - ); + _log.fine("Device album ${deviceAlbum.name} is empty. Removing assets from DB."); await _localAlbumRepository.upsert( deviceAlbum.copyWith(backupSelection: dbAlbum.backupSelection), toDelete: assetsInDb.map((a) => a.id), @@ -249,18 +222,11 @@ class LocalSyncService { return true; } - final updatedDeviceAlbum = deviceAlbum.copyWith( - backupSelection: dbAlbum.backupSelection, - ); + final updatedDeviceAlbum = deviceAlbum.copyWith(backupSelection: dbAlbum.backupSelection); if (dbAlbum.assetCount == 0) { - _log.fine( - "Device album ${deviceAlbum.name} is empty. Adding assets to DB.", - ); - await _localAlbumRepository.upsert( - updatedDeviceAlbum, - toUpsert: assetsInDevice, - ); + _log.fine("Device album ${deviceAlbum.name} is empty. Adding assets to DB."); + await _localAlbumRepository.upsert(updatedDeviceAlbum, toUpsert: assetsInDevice); return true; } @@ -292,18 +258,12 @@ class LocalSyncService { ); if (assetsToUpsert.isEmpty && assetsToDelete.isEmpty) { - _log.fine( - "No asset changes detected in album ${deviceAlbum.name}. Updating metadata.", - ); + _log.fine("No asset changes detected in album ${deviceAlbum.name}. Updating metadata."); _localAlbumRepository.upsert(updatedDeviceAlbum); return true; } - await _localAlbumRepository.upsert( - updatedDeviceAlbum, - toUpsert: assetsToUpsert, - toDelete: assetsToDelete, - ); + await _localAlbumRepository.upsert(updatedDeviceAlbum, toUpsert: assetsToUpsert, toDelete: assetsToDelete); return true; } catch (e, s) { @@ -321,9 +281,7 @@ class LocalSyncService { } bool _albumsEqual(LocalAlbum a, LocalAlbum b) { - return a.name == b.name && - a.assetCount == b.assetCount && - a.updatedAt.isAtSameMomentAs(b.updatedAt); + return a.name == b.name && a.assetCount == b.assetCount && a.updatedAt.isAtSameMomentAs(b.updatedAt); } } @@ -333,9 +291,7 @@ extension on Iterable { (e) => LocalAlbum( id: e.id, name: e.name, - updatedAt: e.updatedAt == null - ? DateTime.now() - : DateTime.fromMillisecondsSinceEpoch(e.updatedAt! * 1000), + updatedAt: e.updatedAt == null ? DateTime.now() : DateTime.fromMillisecondsSinceEpoch(e.updatedAt! * 1000), assetCount: e.assetCount, ), ).toList(); @@ -350,16 +306,13 @@ extension on Iterable { name: e.name, checksum: null, type: AssetType.values.elementAtOrNull(e.type) ?? AssetType.other, - createdAt: e.createdAt == null - ? DateTime.now() - : DateTime.fromMillisecondsSinceEpoch(e.createdAt! * 1000), - updatedAt: e.updatedAt == null - ? DateTime.now() - : DateTime.fromMillisecondsSinceEpoch(e.updatedAt! * 1000), + createdAt: e.createdAt == null ? DateTime.now() : DateTime.fromMillisecondsSinceEpoch(e.createdAt! * 1000), + updatedAt: e.updatedAt == null ? DateTime.now() : DateTime.fromMillisecondsSinceEpoch(e.updatedAt! * 1000), width: e.width, height: e.height, durationInSeconds: e.durationInSeconds, orientation: e.orientation, + isFavorite: e.isFavorite, ), ).toList(); } diff --git a/mobile/lib/domain/services/log.service.dart b/mobile/lib/domain/services/log.service.dart index c52665f093..ff72ec5502 100644 --- a/mobile/lib/domain/services/log.service.dart +++ b/mobile/lib/domain/services/log.service.dart @@ -56,17 +56,12 @@ class LogService { }) async { final instance = LogService._(logRepository, storeRepository, shouldBuffer); await logRepository.truncate(limit: kLogTruncateLimit); - final level = await instance._storeRepository.tryGet(StoreKey.logLevel) ?? - LogLevel.info.index; + final level = await instance._storeRepository.tryGet(StoreKey.logLevel) ?? LogLevel.info.index; Logger.root.level = Level.LEVELS.elementAtOrNull(level) ?? Level.INFO; return instance; } - LogService._( - this._logRepository, - this._storeRepository, - this._shouldBuffer, - ) { + LogService._(this._logRepository, this._storeRepository, this._shouldBuffer) { _logSubscription = Logger.root.onRecord.listen(_handleLogRecord); } @@ -90,10 +85,7 @@ class LogService { if (_shouldBuffer) { _msgBuffer.add(record); - _flushTimer ??= Timer( - const Duration(seconds: 5), - () => unawaited(flushBuffer()), - ); + _flushTimer ??= Timer(const Duration(seconds: 5), () => unawaited(flushBuffer())); } else { unawaited(_logRepository.insert(record)); } @@ -146,9 +138,7 @@ class LoggerUnInitializedException implements Exception { /// Log levels according to dart logging [Level] extension LevelDomainToInfraExtension on Level { - LogLevel toLogLevel() => - LogLevel.values.elementAtOrNull(Level.LEVELS.indexOf(this)) ?? - LogLevel.info; + LogLevel toLogLevel() => LogLevel.values.elementAtOrNull(Level.LEVELS.indexOf(this)) ?? LogLevel.info; } extension on LogLevel { diff --git a/mobile/lib/domain/services/memory.service.dart b/mobile/lib/domain/services/memory.service.dart index c94b8a9f0a..ead613370f 100644 --- a/mobile/lib/domain/services/memory.service.dart +++ b/mobile/lib/domain/services/memory.service.dart @@ -12,4 +12,12 @@ class DriftMemoryService { Future> getMemoryLane(String ownerId) { return _repository.getAll(ownerId); } + + Future get(String memoryId) { + return _repository.get(memoryId); + } + + Future getCount() { + return _repository.getCount(); + } } diff --git a/mobile/lib/domain/services/partner.service.dart b/mobile/lib/domain/services/partner.service.dart new file mode 100644 index 0000000000..7733b5be6b --- /dev/null +++ b/mobile/lib/domain/services/partner.service.dart @@ -0,0 +1,51 @@ +import 'package:flutter/foundation.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; +import 'package:immich_mobile/infrastructure/repositories/partner.repository.dart'; +import 'package:immich_mobile/repositories/partner_api.repository.dart'; + +class DriftPartnerService { + final DriftPartnerRepository _driftPartnerRepository; + final PartnerApiRepository _partnerApiRepository; + + const DriftPartnerService(this._driftPartnerRepository, this._partnerApiRepository); + + Future> getSharedWith(String userId) { + return _driftPartnerRepository.getSharedWith(userId); + } + + Future> getSharedBy(String userId) { + return _driftPartnerRepository.getSharedBy(userId); + } + + Future> getAvailablePartners(String currentUserId) async { + final otherUsers = await _driftPartnerRepository.getAvailablePartners(currentUserId); + final currentPartners = await _driftPartnerRepository.getSharedBy(currentUserId); + final available = otherUsers.where((user) { + return !currentPartners.any((partner) => partner.id == user.id); + }).toList(); + + return available; + } + + Future toggleShowInTimeline(String partnerId, String userId) async { + final partner = await _driftPartnerRepository.getPartner(partnerId, userId); + if (partner == null) { + debugPrint("Partner not found: $partnerId for user: $userId"); + return; + } + + await _partnerApiRepository.update(partnerId, inTimeline: !partner.inTimeline); + + await _driftPartnerRepository.toggleShowInTimeline(partner, userId); + } + + Future addPartner(String partnerId, String userId) async { + await _partnerApiRepository.create(partnerId); + await _driftPartnerRepository.create(partnerId, userId); + } + + Future removePartner(String partnerId, String userId) async { + await _partnerApiRepository.delete(partnerId); + await _driftPartnerRepository.delete(partnerId, userId); + } +} diff --git a/mobile/lib/domain/services/people.service.dart b/mobile/lib/domain/services/people.service.dart new file mode 100644 index 0000000000..d45f710d7b --- /dev/null +++ b/mobile/lib/domain/services/people.service.dart @@ -0,0 +1,30 @@ +import 'dart:async'; + +import 'package:immich_mobile/domain/models/person.model.dart'; +import 'package:immich_mobile/infrastructure/repositories/people.repository.dart'; +import 'package:immich_mobile/repositories/person_api.repository.dart'; + +class DriftPeopleService { + final DriftPeopleRepository _repository; + final PersonApiRepository _personApiRepository; + + const DriftPeopleService(this._repository, this._personApiRepository); + + Future> getAssetPeople(String assetId) { + return _repository.getAssetPeople(assetId); + } + + Future> getAllPeople() { + return _repository.getAllPeople(); + } + + Future updateName(String personId, String name) async { + await _personApiRepository.update(personId, name: name); + return _repository.updateName(personId, name); + } + + Future updateBrithday(String personId, DateTime birthday) async { + await _personApiRepository.update(personId, birthday: birthday); + return _repository.updateBirthday(personId, birthday); + } +} diff --git a/mobile/lib/domain/services/remote_album.service.dart b/mobile/lib/domain/services/remote_album.service.dart index ebb24d5fe5..f6c596f24a 100644 --- a/mobile/lib/domain/services/remote_album.service.dart +++ b/mobile/lib/domain/services/remote_album.service.dart @@ -22,11 +22,11 @@ class RemoteAlbumService { return _repository.getAll(); } - List sortAlbums( - List albums, - RemoteAlbumSortMode sortMode, { - bool isReverse = false, - }) { + Future get(String albumId) { + return _repository.get(albumId); + } + + List sortAlbums(List albums, RemoteAlbumSortMode sortMode, {bool isReverse = false}) { return sortMode.sortFn(albums, isReverse); } @@ -44,8 +44,7 @@ class RemoteAlbumService { filtered = filtered .where( (album) => - album.name.toLowerCase().contains(lowerQuery) || - album.description.toLowerCase().contains(lowerQuery), + album.name.toLowerCase().contains(lowerQuery) || album.description.toLowerCase().contains(lowerQuery), ) .toList(); } @@ -53,12 +52,10 @@ class RemoteAlbumService { if (userId != null) { switch (filterMode) { case QuickFilterMode.myAlbums: - filtered = - filtered.where((album) => album.ownerId == userId).toList(); + filtered = filtered.where((album) => album.ownerId == userId).toList(); break; case QuickFilterMode.sharedWithMe: - filtered = - filtered.where((album) => album.ownerId != userId).toList(); + filtered = filtered.where((album) => album.ownerId != userId).toList(); break; case QuickFilterMode.all: break; @@ -68,16 +65,8 @@ class RemoteAlbumService { return filtered; } - Future createAlbum({ - required String title, - required List assetIds, - String? description, - }) async { - final album = await _albumApiRepository.createDriftAlbum( - title, - description: description, - assetIds: assetIds, - ); + Future createAlbum({required String title, required List assetIds, String? description}) async { + final album = await _albumApiRepository.createDriftAlbum(title, description: description, assetIds: assetIds); await _repository.create(album, assetIds); @@ -119,14 +108,8 @@ class RemoteAlbumService { return _repository.getAssets(albumId); } - Future addAssets({ - required String albumId, - required List assetIds, - }) async { - final album = await _albumApiRepository.addAssets( - albumId, - assetIds, - ); + Future addAssets({required String albumId, required List assetIds}) async { + final album = await _albumApiRepository.addAssets(albumId, assetIds); await _repository.addAssets(albumId, album.added); @@ -139,12 +122,13 @@ class RemoteAlbumService { await _repository.deleteAlbum(albumId); } - Future addUsers({ - required String albumId, - required List userIds, - }) async { + Future addUsers({required String albumId, required List userIds}) async { await _albumApiRepository.addUsers(albumId, userIds); return _repository.addUsers(albumId, userIds); } + + Future getCount() { + return _repository.getCount(); + } } diff --git a/mobile/lib/domain/services/search.service.dart b/mobile/lib/domain/services/search.service.dart index 052a2ca9da..6ccc5a97bf 100644 --- a/mobile/lib/domain/services/search.service.dart +++ b/mobile/lib/domain/services/search.service.dart @@ -83,10 +83,10 @@ extension on AssetResponseDto { extension on AssetTypeEnum { AssetType toAssetType() => switch (this) { - AssetTypeEnum.IMAGE => AssetType.image, - AssetTypeEnum.VIDEO => AssetType.video, - AssetTypeEnum.AUDIO => AssetType.audio, - AssetTypeEnum.OTHER => AssetType.other, - _ => throw Exception('Unknown AssetType value: $this'), - }; + AssetTypeEnum.IMAGE => AssetType.image, + AssetTypeEnum.VIDEO => AssetType.video, + AssetTypeEnum.AUDIO => AssetType.audio, + AssetTypeEnum.OTHER => AssetType.other, + _ => throw Exception('Unknown AssetType value: $this'), + }; } diff --git a/mobile/lib/domain/services/setting.service.dart b/mobile/lib/domain/services/setting.service.dart index 8f91e9c66b..99e07a2872 100644 --- a/mobile/lib/domain/services/setting.service.dart +++ b/mobile/lib/domain/services/setting.service.dart @@ -9,16 +9,11 @@ final AppSetting = SettingsService(storeService: StoreService.I); class SettingsService { final StoreService _storeService; - const SettingsService({required StoreService storeService}) - : _storeService = storeService; + const SettingsService({required StoreService storeService}) : _storeService = storeService; - T get(Setting setting) => - _storeService.get(setting.storeKey, setting.defaultValue); + T get(Setting setting) => _storeService.get(setting.storeKey, setting.defaultValue); - Future set(Setting setting, T value) => - _storeService.put(setting.storeKey, value); + Future set(Setting setting, T value) => _storeService.put(setting.storeKey, value); - Stream watch(Setting setting) => _storeService - .watch(setting.storeKey) - .map((v) => v ?? setting.defaultValue); + Stream watch(Setting setting) => _storeService.watch(setting.storeKey).map((v) => v ?? setting.defaultValue); } diff --git a/mobile/lib/domain/services/store.service.dart b/mobile/lib/domain/services/store.service.dart index b1e991b348..dc845b70f1 100644 --- a/mobile/lib/domain/services/store.service.dart +++ b/mobile/lib/domain/services/store.service.dart @@ -12,8 +12,7 @@ class StoreService { final Map _cache = {}; late final StreamSubscription _storeUpdateSubscription; - StoreService._({required IsarStoreRepository storeRepository}) - : _storeRepository = storeRepository; + StoreService._({required IsarStoreRepository storeRepository}) : _storeRepository = storeRepository; // TODO: Temporary typedef to make minimal changes. Remove this and make the presentation layer access store through a provider static StoreService? _instance; @@ -25,16 +24,12 @@ class StoreService { } // TODO: Replace the implementation with the one from create after removing the typedef - static Future init({ - required IsarStoreRepository storeRepository, - }) async { + static Future init({required IsarStoreRepository storeRepository}) async { _instance ??= await create(storeRepository: storeRepository); return _instance!; } - static Future create({ - required IsarStoreRepository storeRepository, - }) async { + static Future create({required IsarStoreRepository storeRepository}) async { final instance = StoreService._(storeRepository: storeRepository); await instance._populateCache(); instance._storeUpdateSubscription = instance._listenForChange(); @@ -48,10 +43,9 @@ class StoreService { } } - StreamSubscription _listenForChange() => - _storeRepository.watchAll().listen((event) { - _cache[event.key.id] = event.value; - }); + StreamSubscription _listenForChange() => _storeRepository.watchAll().listen((event) { + _cache[event.key.id] = event.value; + }); /// Disposes the store and cancels the subscription. To reuse the store call init() again void dispose() async { diff --git a/mobile/lib/domain/services/sync_stream.service.dart b/mobile/lib/domain/services/sync_stream.service.dart index 6183865041..c21a9cade5 100644 --- a/mobile/lib/domain/services/sync_stream.service.dart +++ b/mobile/lib/domain/services/sync_stream.service.dart @@ -18,14 +18,14 @@ class SyncStreamService { required SyncApiRepository syncApiRepository, required SyncStreamRepository syncStreamRepository, bool Function()? cancelChecker, - }) : _syncApiRepository = syncApiRepository, - _syncStreamRepository = syncStreamRepository, - _cancelChecker = cancelChecker; + }) : _syncApiRepository = syncApiRepository, + _syncStreamRepository = syncStreamRepository, + _cancelChecker = cancelChecker; bool get isCancelled => _cancelChecker?.call() ?? false; Future sync() { - _logger.info("Remote sync request for userr"); + _logger.info("Remote sync request for user"); DLog.log("Remote sync request for user"); // Start the sync stream and handle events return _syncApiRepository.streamChanges(_handleEvents); @@ -34,9 +34,7 @@ class SyncStreamService { Future handleWsAssetUploadReadyV1Batch(List batchData) async { if (batchData.isEmpty) return; - _logger.info( - 'Processing batch of ${batchData.length} AssetUploadReadyV1 events', - ); + _logger.info('Processing batch of ${batchData.length} AssetUploadReadyV1 events'); final List assets = []; final List exifs = []; @@ -65,22 +63,12 @@ class SyncStreamService { } if (assets.isNotEmpty && exifs.isNotEmpty) { - await _syncStreamRepository.updateAssetsV1( - assets, - debugLabel: 'websocket-batch', - ); - await _syncStreamRepository.updateAssetsExifV1( - exifs, - debugLabel: 'websocket-batch', - ); + await _syncStreamRepository.updateAssetsV1(assets, debugLabel: 'websocket-batch'); + await _syncStreamRepository.updateAssetsExifV1(exifs, debugLabel: 'websocket-batch'); _logger.info('Successfully processed ${assets.length} assets in batch'); } } catch (error, stackTrace) { - _logger.severe( - "Error processing AssetUploadReadyV1 websocket batch events", - error, - stackTrace, - ); + _logger.severe("Error processing AssetUploadReadyV1 websocket batch events", error, stackTrace); } } @@ -114,10 +102,7 @@ class SyncStreamService { batch.clear(); } - Future _handleSyncData( - SyncEntityType type, - Iterable data, - ) async { + Future _handleSyncData(SyncEntityType type, Iterable data) async { _logger.fine("Processing sync data for $type of length ${data.length}"); switch (type) { case SyncEntityType.userV1: @@ -135,30 +120,15 @@ class SyncStreamService { case SyncEntityType.assetExifV1: return _syncStreamRepository.updateAssetsExifV1(data.cast()); case SyncEntityType.partnerAssetV1: - return _syncStreamRepository.updateAssetsV1( - data.cast(), - debugLabel: 'partner', - ); + return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'partner'); case SyncEntityType.partnerAssetBackfillV1: - return _syncStreamRepository.updateAssetsV1( - data.cast(), - debugLabel: 'partner backfill', - ); + return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'partner backfill'); case SyncEntityType.partnerAssetDeleteV1: - return _syncStreamRepository.deleteAssetsV1( - data.cast(), - debugLabel: "partner", - ); + return _syncStreamRepository.deleteAssetsV1(data.cast(), debugLabel: "partner"); case SyncEntityType.partnerAssetExifV1: - return _syncStreamRepository.updateAssetsExifV1( - data.cast(), - debugLabel: 'partner', - ); + return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'partner'); case SyncEntityType.partnerAssetExifBackfillV1: - return _syncStreamRepository.updateAssetsExifV1( - data.cast(), - debugLabel: 'partner backfill', - ); + return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'partner backfill'); case SyncEntityType.albumV1: return _syncStreamRepository.updateAlbumsV1(data.cast()); case SyncEntityType.albumDeleteV1: @@ -166,39 +136,21 @@ class SyncStreamService { case SyncEntityType.albumUserV1: return _syncStreamRepository.updateAlbumUsersV1(data.cast()); case SyncEntityType.albumUserBackfillV1: - return _syncStreamRepository.updateAlbumUsersV1( - data.cast(), - debugLabel: 'backfill', - ); + return _syncStreamRepository.updateAlbumUsersV1(data.cast(), debugLabel: 'backfill'); case SyncEntityType.albumUserDeleteV1: return _syncStreamRepository.deleteAlbumUsersV1(data.cast()); case SyncEntityType.albumAssetV1: - return _syncStreamRepository.updateAssetsV1( - data.cast(), - debugLabel: 'album', - ); + return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'album'); case SyncEntityType.albumAssetBackfillV1: - return _syncStreamRepository.updateAssetsV1( - data.cast(), - debugLabel: 'album backfill', - ); + return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'album backfill'); case SyncEntityType.albumAssetExifV1: - return _syncStreamRepository.updateAssetsExifV1( - data.cast(), - debugLabel: 'album', - ); + return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'album'); case SyncEntityType.albumAssetExifBackfillV1: - return _syncStreamRepository.updateAssetsExifV1( - data.cast(), - debugLabel: 'album backfill', - ); + return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'album backfill'); case SyncEntityType.albumToAssetV1: return _syncStreamRepository.updateAlbumToAssetsV1(data.cast()); case SyncEntityType.albumToAssetBackfillV1: - return _syncStreamRepository.updateAlbumToAssetsV1( - data.cast(), - debugLabel: 'backfill', - ); + return _syncStreamRepository.updateAlbumToAssetsV1(data.cast(), debugLabel: 'backfill'); case SyncEntityType.albumToAssetDeleteV1: return _syncStreamRepository.deleteAlbumToAssetsV1(data.cast()); // No-op. SyncAckV1 entities are checkpoints in the sync stream @@ -218,28 +170,23 @@ class SyncStreamService { case SyncEntityType.stackDeleteV1: return _syncStreamRepository.deleteStacksV1(data.cast()); case SyncEntityType.partnerStackV1: - return _syncStreamRepository.updateStacksV1( - data.cast(), - debugLabel: 'partner', - ); + return _syncStreamRepository.updateStacksV1(data.cast(), debugLabel: 'partner'); case SyncEntityType.partnerStackBackfillV1: - return _syncStreamRepository.updateStacksV1( - data.cast(), - debugLabel: 'partner backfill', - ); + return _syncStreamRepository.updateStacksV1(data.cast(), debugLabel: 'partner backfill'); case SyncEntityType.partnerStackDeleteV1: - return _syncStreamRepository.deleteStacksV1( - data.cast(), - debugLabel: 'partner', - ); + return _syncStreamRepository.deleteStacksV1(data.cast(), debugLabel: 'partner'); case SyncEntityType.userMetadataV1: - return _syncStreamRepository.updateUserMetadatasV1( - data.cast(), - ); + return _syncStreamRepository.updateUserMetadatasV1(data.cast()); case SyncEntityType.userMetadataDeleteV1: - return _syncStreamRepository.deleteUserMetadatasV1( - data.cast(), - ); + return _syncStreamRepository.deleteUserMetadatasV1(data.cast()); + case SyncEntityType.personV1: + return _syncStreamRepository.updatePeopleV1(data.cast()); + case SyncEntityType.personDeleteV1: + return _syncStreamRepository.deletePeopleV1(data.cast()); + case SyncEntityType.assetFaceV1: + return _syncStreamRepository.updateAssetFacesV1(data.cast()); + case SyncEntityType.assetFaceDeleteV1: + return _syncStreamRepository.deleteAssetFacesV1(data.cast()); default: _logger.warning("Unknown sync data type: $type"); } diff --git a/mobile/lib/domain/services/timeline.service.dart b/mobile/lib/domain/services/timeline.service.dart index 0d31f06e74..9fa4106d17 100644 --- a/mobile/lib/domain/services/timeline.service.dart +++ b/mobile/lib/domain/services/timeline.service.dart @@ -11,33 +11,27 @@ import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/infrastructure/repositories/timeline.repository.dart'; import 'package:immich_mobile/utils/async_mutex.dart'; -typedef TimelineAssetSource = Future> Function( - int index, - int count, -); +typedef TimelineAssetSource = Future> Function(int index, int count); typedef TimelineBucketSource = Stream> Function(); -typedef TimelineQuery = ({ - TimelineAssetSource assetSource, - TimelineBucketSource bucketSource, -}); +typedef TimelineQuery = ({TimelineAssetSource assetSource, TimelineBucketSource bucketSource}); class TimelineFactory { final DriftTimelineRepository _timelineRepository; final SettingsService _settingsService; - const TimelineFactory({ - required DriftTimelineRepository timelineRepository, - required SettingsService settingsService, - }) : _timelineRepository = timelineRepository, - _settingsService = settingsService; + const TimelineFactory({required DriftTimelineRepository timelineRepository, required SettingsService settingsService}) + : _timelineRepository = timelineRepository, + _settingsService = settingsService; - GroupAssetsBy get groupBy => - GroupAssetsBy.values[_settingsService.get(Setting.groupAssetsBy)]; + GroupAssetsBy get groupBy { + final group = GroupAssetsBy.values[_settingsService.get(Setting.groupAssetsBy)]; + // We do not support auto grouping in the new timeline yet, fallback to day grouping + return group == GroupAssetsBy.auto ? GroupAssetsBy.day : group; + } - TimelineService main(List timelineUsers) => - TimelineService(_timelineRepository.main(timelineUsers, groupBy)); + TimelineService main(List timelineUsers) => TimelineService(_timelineRepository.main(timelineUsers, groupBy)); TimelineService localAlbum({required String albumId}) => TimelineService(_timelineRepository.localAlbum(albumId, groupBy)); @@ -45,29 +39,24 @@ class TimelineFactory { TimelineService remoteAlbum({required String albumId}) => TimelineService(_timelineRepository.remoteAlbum(albumId, groupBy)); - TimelineService remoteAssets(String userId) => - TimelineService(_timelineRepository.remote(userId, groupBy)); + TimelineService remoteAssets(String userId) => TimelineService(_timelineRepository.remote(userId, groupBy)); - TimelineService favorite(String userId) => - TimelineService(_timelineRepository.favorite(userId, groupBy)); + TimelineService favorite(String userId) => TimelineService(_timelineRepository.favorite(userId, groupBy)); - TimelineService trash(String userId) => - TimelineService(_timelineRepository.trash(userId, groupBy)); + TimelineService trash(String userId) => TimelineService(_timelineRepository.trash(userId, groupBy)); - TimelineService archive(String userId) => - TimelineService(_timelineRepository.archived(userId, groupBy)); + TimelineService archive(String userId) => TimelineService(_timelineRepository.archived(userId, groupBy)); - TimelineService lockedFolder(String userId) => - TimelineService(_timelineRepository.locked(userId, groupBy)); + TimelineService lockedFolder(String userId) => TimelineService(_timelineRepository.locked(userId, groupBy)); - TimelineService video(String userId) => - TimelineService(_timelineRepository.video(userId, groupBy)); + TimelineService video(String userId) => TimelineService(_timelineRepository.video(userId, groupBy)); - TimelineService place(String place) => - TimelineService(_timelineRepository.place(place, groupBy)); + TimelineService place(String place) => TimelineService(_timelineRepository.place(place, groupBy)); - TimelineService fromAssets(List assets) => - TimelineService(_timelineRepository.fromAssets(assets)); + TimelineService person(String userId, String personId) => + TimelineService(_timelineRepository.person(userId, personId, groupBy)); + + TimelineService fromAssets(List assets) => TimelineService(_timelineRepository.fromAssets(assets)); } class TimelineService { @@ -81,21 +70,14 @@ class TimelineService { int _totalAssets = 0; int get totalAssets => _totalAssets; - TimelineService(TimelineQuery query) - : this._( - assetSource: query.assetSource, - bucketSource: query.bucketSource, - ); + TimelineService(TimelineQuery query) : this._(assetSource: query.assetSource, bucketSource: query.bucketSource); - TimelineService._({ - required TimelineAssetSource assetSource, - required TimelineBucketSource bucketSource, - }) : _assetSource = assetSource, - _bucketSource = bucketSource { + TimelineService._({required TimelineAssetSource assetSource, required TimelineBucketSource bucketSource}) + : _assetSource = assetSource, + _bucketSource = bucketSource { _bucketSubscription = _bucketSource().listen((buckets) { _mutex.run(() async { - final totalAssets = - buckets.fold(0, (acc, bucket) => acc + bucket.assetCount); + final totalAssets = buckets.fold(0, (acc, bucket) => acc + bucket.assetCount); if (totalAssets == 0) { _bufferOffset = 0; @@ -110,10 +92,7 @@ class TimelineService { count = kTimelineAssetLoadBatchSize; } else { offset = _bufferOffset; - count = math.min( - _buffer.length, - totalAssets - _bufferOffset, - ); + count = math.min(_buffer.length, totalAssets - _bufferOffset); } _buffer = await _assetSource(offset, count); _bufferOffset = offset; @@ -128,8 +107,7 @@ class TimelineService { Stream> Function() get watchBuckets => _bucketSource; - Future> loadAssets(int index, int count) => - _mutex.run(() => _loadAssets(index, count)); + Future> loadAssets(int index, int count) => _mutex.run(() => _loadAssets(index, count)); Future> _loadAssets(int index, int count) async { if (hasRange(index, count)) { @@ -142,10 +120,7 @@ class TimelineService { // make sure to load a meaningful amount of data (and not only the requested slice) // otherwise, each call to [loadAssets] would result in DB call trashing performance // fills small requests to [kTimelineAssetLoadBatchSize], adds some legroom into the opposite scroll direction for large requests - final len = math.max( - kTimelineAssetLoadBatchSize, - count + kTimelineAssetLoadOppositeSize, - ); + final len = math.max(kTimelineAssetLoadBatchSize, count + kTimelineAssetLoadOppositeSize); // when scrolling forward, start shortly before the requested offset // when scrolling backward, end shortly after the requested offset to guard against the user scrolling // in the other direction a tiny bit resulting in another required load from the DB @@ -178,11 +153,9 @@ class TimelineService { } // Pre-cache assets around the given index for asset viewer - Future preCacheAssets(int index) => - _mutex.run(() => _loadAssets(index, math.min(5, _totalAssets - index))); + Future preCacheAssets(int index) => _mutex.run(() => _loadAssets(index, math.min(5, _totalAssets - index))); - BaseAsset getRandomAsset() => - _buffer.elementAt(math.Random().nextInt(_buffer.length)); + BaseAsset getRandomAsset() => _buffer.elementAt(math.Random().nextInt(_buffer.length)); BaseAsset getAsset(int index) { if (!hasRange(index, 1)) { diff --git a/mobile/lib/domain/services/user.service.dart b/mobile/lib/domain/services/user.service.dart index 14fed9fb93..d347d8aa4f 100644 --- a/mobile/lib/domain/services/user.service.dart +++ b/mobile/lib/domain/services/user.service.dart @@ -18,9 +18,9 @@ class UserService { required IsarUserRepository isarUserRepository, required UserApiRepository userApiRepository, required StoreService storeService, - }) : _isarUserRepository = isarUserRepository, - _userApiRepository = userApiRepository, - _storeService = storeService; + }) : _isarUserRepository = isarUserRepository, + _userApiRepository = userApiRepository, + _storeService = storeService; UserDto getMyUser() { return _storeService.get(StoreKey.currentUser); @@ -44,11 +44,8 @@ class UserService { Future createProfileImage(String name, Uint8List image) async { try { - final path = await _userApiRepository.createProfileImage( - name: name, - data: image, - ); - final updatedUser = getMyUser().copyWith(profileImagePath: path); + final path = await _userApiRepository.createProfileImage(name: name, data: image); + final updatedUser = getMyUser(); await _storeService.put(StoreKey.currentUser, updatedUser); await _isarUserRepository.update(updatedUser); return path; diff --git a/mobile/lib/domain/utils/background_sync.dart b/mobile/lib/domain/utils/background_sync.dart index c71f1a8315..1944591c93 100644 --- a/mobile/lib/domain/utils/background_sync.dart +++ b/mobile/lib/domain/utils/background_sync.dart @@ -4,13 +4,38 @@ import 'package:immich_mobile/providers/infrastructure/sync.provider.dart'; import 'package:immich_mobile/utils/isolate.dart'; import 'package:worker_manager/worker_manager.dart'; +typedef SyncCallback = void Function(); +typedef SyncErrorCallback = void Function(String error); + class BackgroundSyncManager { + final SyncCallback? onRemoteSyncStart; + final SyncCallback? onRemoteSyncComplete; + final SyncErrorCallback? onRemoteSyncError; + + final SyncCallback? onLocalSyncStart; + final SyncCallback? onLocalSyncComplete; + final SyncErrorCallback? onLocalSyncError; + + final SyncCallback? onHashingStart; + final SyncCallback? onHashingComplete; + final SyncErrorCallback? onHashingError; + Cancelable? _syncTask; Cancelable? _syncWebsocketTask; Cancelable? _deviceAlbumSyncTask; Cancelable? _hashTask; - BackgroundSyncManager(); + BackgroundSyncManager({ + this.onRemoteSyncStart, + this.onRemoteSyncComplete, + this.onRemoteSyncError, + this.onLocalSyncStart, + this.onLocalSyncComplete, + this.onLocalSyncError, + this.onHashingStart, + this.onHashingComplete, + this.onHashingError, + }); Future cancel() { final futures = []; @@ -36,35 +61,44 @@ class BackgroundSyncManager { return _deviceAlbumSyncTask!.future; } + onLocalSyncStart?.call(); + // We use a ternary operator to avoid [_deviceAlbumSyncTask] from being // captured by the closure passed to [runInIsolateGentle]. _deviceAlbumSyncTask = full - ? runInIsolateGentle( - computation: (ref) => - ref.read(localSyncServiceProvider).sync(full: true), - ) - : runInIsolateGentle( - computation: (ref) => - ref.read(localSyncServiceProvider).sync(full: false), - ); + ? runInIsolateGentle(computation: (ref) => ref.read(localSyncServiceProvider).sync(full: true)) + : runInIsolateGentle(computation: (ref) => ref.read(localSyncServiceProvider).sync(full: false)); - return _deviceAlbumSyncTask!.whenComplete(() { - _deviceAlbumSyncTask = null; - }); + return _deviceAlbumSyncTask! + .whenComplete(() { + _deviceAlbumSyncTask = null; + onLocalSyncComplete?.call(); + }) + .catchError((error) { + onLocalSyncError?.call(error.toString()); + _deviceAlbumSyncTask = null; + }); } -// No need to cancel the task, as it can also be run when the user logs out + // No need to cancel the task, as it can also be run when the user logs out Future hashAssets() { if (_hashTask != null) { return _hashTask!.future; } - _hashTask = runInIsolateGentle( - computation: (ref) => ref.read(hashServiceProvider).hashAssets(), - ); - return _hashTask!.whenComplete(() { - _hashTask = null; - }); + onHashingStart?.call(); + + _hashTask = runInIsolateGentle(computation: (ref) => ref.read(hashServiceProvider).hashAssets()); + + return _hashTask! + .whenComplete(() { + onHashingComplete?.call(); + _hashTask = null; + }) + .catchError((error) { + onHashingError?.call(error.toString()); + _hashTask = null; + }); } Future syncRemote() { @@ -72,26 +106,31 @@ class BackgroundSyncManager { return _syncTask!.future; } - _syncTask = runInIsolateGentle( - computation: (ref) => ref.read(syncStreamServiceProvider).sync(), - ); - return _syncTask!.whenComplete(() { - _syncTask = null; - }); + onRemoteSyncStart?.call(); + + _syncTask = runInIsolateGentle(computation: (ref) => ref.read(syncStreamServiceProvider).sync()); + return _syncTask! + .whenComplete(() { + onRemoteSyncComplete?.call(); + _syncTask = null; + }) + .catchError((error) { + onRemoteSyncError?.call(error.toString()); + _syncTask = null; + }); } Future syncWebsocketBatch(List batchData) { if (_syncWebsocketTask != null) { return _syncWebsocketTask!.future; } - - _syncWebsocketTask = runInIsolateGentle( - computation: (ref) => ref - .read(syncStreamServiceProvider) - .handleWsAssetUploadReadyV1Batch(batchData), - ); + _syncWebsocketTask = _handleWsAssetUploadReadyV1Batch(batchData); return _syncWebsocketTask!.whenComplete(() { _syncWebsocketTask = null; }); } } + +Cancelable _handleWsAssetUploadReadyV1Batch(List batchData) => runInIsolateGentle( + computation: (ref) => ref.read(syncStreamServiceProvider).handleWsAssetUploadReadyV1Batch(batchData), +); diff --git a/mobile/lib/domain/utils/event_stream.dart b/mobile/lib/domain/utils/event_stream.dart index e728ece58b..5967fdca50 100644 --- a/mobile/lib/domain/utils/event_stream.dart +++ b/mobile/lib/domain/utils/event_stream.dart @@ -9,8 +9,7 @@ class EventStream { static final EventStream shared = EventStream._(); - final StreamController _controller = - StreamController.broadcast(); + final StreamController _controller = StreamController.broadcast(); void emit(Event event) { _controller.add(event); @@ -29,12 +28,7 @@ class EventStream { void Function()? onDone, bool? cancelOnError, }) { - return where().listen( - onData, - onError: onError, - onDone: onDone, - cancelOnError: cancelOnError, - ); + return where().listen(onData, onError: onError, onDone: onDone, cancelOnError: cancelOnError); } /// Closes the stream controller diff --git a/mobile/lib/entities/album.entity.dart b/mobile/lib/entities/album.entity.dart index f6d5322752..2ca0d50dcc 100644 --- a/mobile/lib/entities/album.entity.dart +++ b/mobile/lib/entities/album.entity.dart @@ -95,13 +95,11 @@ class Album { // accessible in an object freshly created (not loaded from DB) @ignore - Iterable get remoteUsers => sharedUsers.isEmpty - ? (sharedUsers as IsarLinksCommon).addedObjects - : sharedUsers; + Iterable get remoteUsers => + sharedUsers.isEmpty ? (sharedUsers as IsarLinksCommon).addedObjects : sharedUsers; @ignore - Iterable get remoteAssets => - assets.isEmpty ? (assets as IsarLinksCommon).addedObjects : assets; + Iterable get remoteAssets => assets.isEmpty ? (assets as IsarLinksCommon).addedObjects : assets; @override bool operator ==(other) { @@ -115,10 +113,7 @@ class Album { modifiedAt.isAtSameMomentAs(other.modifiedAt) && isAtSameMomentAs(startDate, other.startDate) && isAtSameMomentAs(endDate, other.endDate) && - isAtSameMomentAs( - lastModifiedAssetTimestamp, - other.lastModifiedAssetTimestamp, - ) && + isAtSameMomentAs(lastModifiedAssetTimestamp, other.lastModifiedAssetTimestamp) && shared == other.shared && activityEnabled == other.activityEnabled && owner.value == other.owner.value && @@ -164,33 +159,25 @@ class Album { a.remoteAssetCount = dto.assetCount; a.owner.value = await db.users.getById(dto.ownerId); if (dto.order != null) { - a.sortOrder = - dto.order == AssetOrder.asc ? SortOrder.asc : SortOrder.desc; + a.sortOrder = dto.order == AssetOrder.asc ? SortOrder.asc : SortOrder.desc; } if (dto.albumThumbnailAssetId != null) { - a.thumbnail.value = await db.assets - .where() - .remoteIdEqualTo(dto.albumThumbnailAssetId) - .findFirst(); + a.thumbnail.value = await db.assets.where().remoteIdEqualTo(dto.albumThumbnailAssetId).findFirst(); } if (dto.albumUsers.isNotEmpty) { - final users = await db.users.getAllById( - dto.albumUsers.map((e) => e.user.id).toList(growable: false), - ); + final users = await db.users.getAllById(dto.albumUsers.map((e) => e.user.id).toList(growable: false)); a.sharedUsers.addAll(users.cast()); } if (dto.assets.isNotEmpty) { - final assets = - await db.assets.getAllByRemoteId(dto.assets.map((e) => e.id)); + final assets = await db.assets.getAllByRemoteId(dto.assets.map((e) => e.id)); a.assets.addAll(assets); } return a; } @override - String toString() => - 'remoteId: $remoteId name: $name description: $description'; + String toString() => 'remoteId: $remoteId name: $name description: $description'; } extension AssetsHelper on IsarCollection { diff --git a/mobile/lib/entities/album.entity.g.dart b/mobile/lib/entities/album.entity.g.dart index 546101baca..e6ecde7f9a 100644 --- a/mobile/lib/entities/album.entity.g.dart +++ b/mobile/lib/entities/album.entity.g.dart @@ -42,31 +42,19 @@ const AlbumSchema = CollectionSchema( name: r'lastModifiedAssetTimestamp', type: IsarType.dateTime, ), - r'localId': PropertySchema( - id: 5, - name: r'localId', - type: IsarType.string, - ), + r'localId': PropertySchema(id: 5, name: r'localId', type: IsarType.string), r'modifiedAt': PropertySchema( id: 6, name: r'modifiedAt', type: IsarType.dateTime, ), - r'name': PropertySchema( - id: 7, - name: r'name', - type: IsarType.string, - ), + r'name': PropertySchema(id: 7, name: r'name', type: IsarType.string), r'remoteId': PropertySchema( id: 8, name: r'remoteId', type: IsarType.string, ), - r'shared': PropertySchema( - id: 9, - name: r'shared', - type: IsarType.bool, - ), + r'shared': PropertySchema(id: 9, name: r'shared', type: IsarType.bool), r'sortOrder': PropertySchema( id: 10, name: r'sortOrder', @@ -77,8 +65,9 @@ const AlbumSchema = CollectionSchema( id: 11, name: r'startDate', type: IsarType.dateTime, - ) + ), }, + estimateSize: _albumEstimateSize, serialize: _albumSerialize, deserialize: _albumDeserialize, @@ -95,7 +84,7 @@ const AlbumSchema = CollectionSchema( name: r'remoteId', type: IndexType.hash, caseSensitive: true, - ) + ), ], ), r'localId': IndexSchema( @@ -108,9 +97,9 @@ const AlbumSchema = CollectionSchema( name: r'localId', type: IndexType.hash, caseSensitive: true, - ) + ), ], - ) + ), }, links: { r'owner': LinkSchema( @@ -136,9 +125,10 @@ const AlbumSchema = CollectionSchema( name: r'assets', target: r'Asset', single: false, - ) + ), }, embeddedSchemas: {}, + getId: _albumGetId, getLinks: _albumGetLinks, attach: _albumAttach, @@ -212,7 +202,7 @@ Album _albumDeserialize( shared: reader.readBool(offsets[9]), sortOrder: _AlbumsortOrderValueEnumMap[reader.readByteOrNull(offsets[10])] ?? - SortOrder.desc, + SortOrder.desc, startDate: reader.readDateTimeOrNull(offsets[11]), ); object.id = id; @@ -248,7 +238,8 @@ P _albumDeserializeProp

( return (reader.readBool(offset)) as P; case 10: return (_AlbumsortOrderValueEnumMap[reader.readByteOrNull(offset)] ?? - SortOrder.desc) as P; + SortOrder.desc) + as P; case 11: return (reader.readDateTimeOrNull(offset)) as P; default: @@ -256,14 +247,8 @@ P _albumDeserializeProp

( } } -const _AlbumsortOrderEnumValueMap = { - 'asc': 0, - 'desc': 1, -}; -const _AlbumsortOrderValueEnumMap = { - 0: SortOrder.asc, - 1: SortOrder.desc, -}; +const _AlbumsortOrderEnumValueMap = {'asc': 0, 'desc': 1}; +const _AlbumsortOrderValueEnumMap = {0: SortOrder.asc, 1: SortOrder.desc}; Id _albumGetId(Album object) { return object.id; @@ -277,8 +262,12 @@ void _albumAttach(IsarCollection col, Id id, Album object) { object.id = id; object.owner.attach(col, col.isar.collection(), r'owner', id); object.thumbnail.attach(col, col.isar.collection(), r'thumbnail', id); - object.sharedUsers - .attach(col, col.isar.collection(), r'sharedUsers', id); + object.sharedUsers.attach( + col, + col.isar.collection(), + r'sharedUsers', + id, + ); object.assets.attach(col, col.isar.collection(), r'assets', id); } @@ -293,10 +282,7 @@ extension AlbumQueryWhereSort on QueryBuilder { extension AlbumQueryWhere on QueryBuilder { QueryBuilder idEqualTo(Id id) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: id, - upper: id, - )); + return query.addWhereClause(IdWhereClause.between(lower: id, upper: id)); }); } @@ -322,8 +308,10 @@ extension AlbumQueryWhere on QueryBuilder { }); } - QueryBuilder idGreaterThan(Id id, - {bool include = false}) { + QueryBuilder idGreaterThan( + Id id, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: id, includeLower: include), @@ -331,8 +319,10 @@ extension AlbumQueryWhere on QueryBuilder { }); } - QueryBuilder idLessThan(Id id, - {bool include = false}) { + QueryBuilder idLessThan( + Id id, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: id, includeUpper: include), @@ -347,141 +337,163 @@ extension AlbumQueryWhere on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerId, - includeLower: includeLower, - upper: upperId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder remoteIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'remoteId', - value: [null], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'remoteId', value: [null]), + ); }); } QueryBuilder remoteIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [null], - includeLower: false, - upper: [], - )); + return query.addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [null], + includeLower: false, + upper: [], + ), + ); }); } QueryBuilder remoteIdEqualTo( - String? remoteId) { + String? remoteId, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'remoteId', - value: [remoteId], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'remoteId', value: [remoteId]), + ); }); } QueryBuilder remoteIdNotEqualTo( - String? remoteId) { + String? remoteId, + ) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [], - upper: [remoteId], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [remoteId], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [], + upper: [remoteId], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [remoteId], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [remoteId], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [], - upper: [remoteId], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [remoteId], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [], + upper: [remoteId], + includeUpper: false, + ), + ); } }); } QueryBuilder localIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'localId', - value: [null], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'localId', value: [null]), + ); }); } QueryBuilder localIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [null], - includeLower: false, - upper: [], - )); + return query.addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [null], + includeLower: false, + upper: [], + ), + ); }); } QueryBuilder localIdEqualTo( - String? localId) { + String? localId, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'localId', - value: [localId], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'localId', value: [localId]), + ); }); } QueryBuilder localIdNotEqualTo( - String? localId) { + String? localId, + ) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [], - upper: [localId], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [localId], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [], + upper: [localId], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [localId], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [localId], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [], - upper: [localId], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [localId], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [], + upper: [localId], + includeUpper: false, + ), + ); } }); } @@ -489,22 +501,22 @@ extension AlbumQueryWhere on QueryBuilder { extension AlbumQueryFilter on QueryBuilder { QueryBuilder activityEnabledEqualTo( - bool value) { + bool value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'activityEnabled', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'activityEnabled', value: value), + ); }); } QueryBuilder createdAtEqualTo( - DateTime value) { + DateTime value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'createdAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'createdAt', value: value), + ); }); } @@ -513,11 +525,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'createdAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'createdAt', + value: value, + ), + ); }); } @@ -526,11 +540,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'createdAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'createdAt', + value: value, + ), + ); }); } @@ -541,29 +557,31 @@ extension AlbumQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'createdAt', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'createdAt', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder descriptionIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'description', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'description'), + ); }); } QueryBuilder descriptionIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'description', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'description'), + ); }); } @@ -572,11 +590,13 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -586,12 +606,14 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -601,12 +623,14 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -618,14 +642,16 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'description', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'description', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -634,11 +660,13 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -647,79 +675,85 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder descriptionContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder descriptionMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'description', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'description', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder descriptionIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'description', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'description', value: ''), + ); }); } QueryBuilder descriptionIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'description', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'description', value: ''), + ); }); } QueryBuilder endDateIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'endDate', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'endDate'), + ); }); } QueryBuilder endDateIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'endDate', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'endDate'), + ); }); } QueryBuilder endDateEqualTo( - DateTime? value) { + DateTime? value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'endDate', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'endDate', value: value), + ); }); } @@ -728,11 +762,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'endDate', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'endDate', + value: value, + ), + ); }); } @@ -741,11 +777,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'endDate', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'endDate', + value: value, + ), + ); }); } @@ -756,22 +794,23 @@ extension AlbumQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'endDate', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'endDate', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder idEqualTo(Id value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: value), + ); }); } @@ -780,11 +819,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + ), + ); }); } @@ -793,11 +834,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + ), + ); }); } @@ -808,103 +851,112 @@ extension AlbumQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder - lastModifiedAssetTimestampIsNull() { + lastModifiedAssetTimestampIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'lastModifiedAssetTimestamp', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'lastModifiedAssetTimestamp'), + ); }); } QueryBuilder - lastModifiedAssetTimestampIsNotNull() { + lastModifiedAssetTimestampIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'lastModifiedAssetTimestamp', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull( + property: r'lastModifiedAssetTimestamp', + ), + ); }); } QueryBuilder - lastModifiedAssetTimestampEqualTo(DateTime? value) { + lastModifiedAssetTimestampEqualTo(DateTime? value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'lastModifiedAssetTimestamp', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'lastModifiedAssetTimestamp', + value: value, + ), + ); }); } QueryBuilder - lastModifiedAssetTimestampGreaterThan( + lastModifiedAssetTimestampGreaterThan( DateTime? value, { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'lastModifiedAssetTimestamp', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'lastModifiedAssetTimestamp', + value: value, + ), + ); }); } QueryBuilder - lastModifiedAssetTimestampLessThan( - DateTime? value, { - bool include = false, - }) { + lastModifiedAssetTimestampLessThan(DateTime? value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'lastModifiedAssetTimestamp', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'lastModifiedAssetTimestamp', + value: value, + ), + ); }); } QueryBuilder - lastModifiedAssetTimestampBetween( + lastModifiedAssetTimestampBetween( DateTime? lower, DateTime? upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'lastModifiedAssetTimestamp', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'lastModifiedAssetTimestamp', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder localIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'localId', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'localId'), + ); }); } QueryBuilder localIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'localId', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'localId'), + ); }); } @@ -913,11 +965,13 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -927,12 +981,14 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -942,12 +998,14 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -959,14 +1017,16 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'localId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'localId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -975,11 +1035,13 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -988,63 +1050,69 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder localIdContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder localIdMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'localId', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'localId', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder localIdIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'localId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'localId', value: ''), + ); }); } QueryBuilder localIdIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'localId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'localId', value: ''), + ); }); } QueryBuilder modifiedAtEqualTo( - DateTime value) { + DateTime value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'modifiedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'modifiedAt', value: value), + ); }); } @@ -1053,11 +1121,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'modifiedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'modifiedAt', + value: value, + ), + ); }); } @@ -1066,11 +1136,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'modifiedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'modifiedAt', + value: value, + ), + ); }); } @@ -1081,13 +1153,15 @@ extension AlbumQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'modifiedAt', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'modifiedAt', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } @@ -1096,11 +1170,13 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1110,12 +1186,14 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1125,12 +1203,14 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1142,14 +1222,16 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'name', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'name', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1158,11 +1240,13 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1171,67 +1255,75 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder nameContains(String value, - {bool caseSensitive = true}) { + QueryBuilder nameContains( + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder nameMatches(String pattern, - {bool caseSensitive = true}) { + QueryBuilder nameMatches( + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'name', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'name', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder nameIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'name', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'name', value: ''), + ); }); } QueryBuilder nameIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'name', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'name', value: ''), + ); }); } QueryBuilder remoteIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'remoteId', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'remoteId'), + ); }); } QueryBuilder remoteIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'remoteId', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'remoteId'), + ); }); } @@ -1240,11 +1332,13 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1254,12 +1348,14 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1269,12 +1365,14 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1286,14 +1384,16 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'remoteId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'remoteId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1302,11 +1402,13 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1315,72 +1417,77 @@ extension AlbumQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder remoteIdContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder remoteIdMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'remoteId', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'remoteId', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder remoteIdIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'remoteId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'remoteId', value: ''), + ); }); } QueryBuilder remoteIdIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'remoteId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'remoteId', value: ''), + ); }); } QueryBuilder sharedEqualTo(bool value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'shared', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'shared', value: value), + ); }); } QueryBuilder sortOrderEqualTo( - SortOrder value) { + SortOrder value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'sortOrder', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'sortOrder', value: value), + ); }); } @@ -1389,11 +1496,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'sortOrder', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'sortOrder', + value: value, + ), + ); }); } @@ -1402,11 +1511,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'sortOrder', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'sortOrder', + value: value, + ), + ); }); } @@ -1417,39 +1528,41 @@ extension AlbumQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'sortOrder', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'sortOrder', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder startDateIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'startDate', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'startDate'), + ); }); } QueryBuilder startDateIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'startDate', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'startDate'), + ); }); } QueryBuilder startDateEqualTo( - DateTime? value) { + DateTime? value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'startDate', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'startDate', value: value), + ); }); } @@ -1458,11 +1571,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'startDate', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'startDate', + value: value, + ), + ); }); } @@ -1471,11 +1586,13 @@ extension AlbumQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'startDate', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'startDate', + value: value, + ), + ); }); } @@ -1486,13 +1603,15 @@ extension AlbumQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'startDate', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'startDate', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } } @@ -1513,7 +1632,8 @@ extension AlbumQueryLinks on QueryBuilder { } QueryBuilder thumbnail( - FilterQuery q) { + FilterQuery q, + ) { return QueryBuilder.apply(this, (query) { return query.link(q, r'thumbnail'); }); @@ -1526,14 +1646,16 @@ extension AlbumQueryLinks on QueryBuilder { } QueryBuilder sharedUsers( - FilterQuery q) { + FilterQuery q, + ) { return QueryBuilder.apply(this, (query) { return query.link(q, r'sharedUsers'); }); } QueryBuilder sharedUsersLengthEqualTo( - int length) { + int length, + ) { return QueryBuilder.apply(this, (query) { return query.linkLength(r'sharedUsers', length, true, length, true); }); @@ -1561,10 +1683,7 @@ extension AlbumQueryLinks on QueryBuilder { } QueryBuilder - sharedUsersLengthGreaterThan( - int length, { - bool include = false, - }) { + sharedUsersLengthGreaterThan(int length, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.linkLength(r'sharedUsers', length, include, 999999, true); }); @@ -1578,19 +1697,26 @@ extension AlbumQueryLinks on QueryBuilder { }) { return QueryBuilder.apply(this, (query) { return query.linkLength( - r'sharedUsers', lower, includeLower, upper, includeUpper); + r'sharedUsers', + lower, + includeLower, + upper, + includeUpper, + ); }); } QueryBuilder assets( - FilterQuery q) { + FilterQuery q, + ) { return QueryBuilder.apply(this, (query) { return query.link(q, r'assets'); }); } QueryBuilder assetsLengthEqualTo( - int length) { + int length, + ) { return QueryBuilder.apply(this, (query) { return query.linkLength(r'assets', length, true, length, true); }); @@ -1634,7 +1760,12 @@ extension AlbumQueryLinks on QueryBuilder { }) { return QueryBuilder.apply(this, (query) { return query.linkLength( - r'assets', lower, includeLower, upper, includeUpper); + r'assets', + lower, + includeLower, + upper, + includeUpper, + ); }); } } @@ -1695,7 +1826,7 @@ extension AlbumQuerySortBy on QueryBuilder { } QueryBuilder - sortByLastModifiedAssetTimestampDesc() { + sortByLastModifiedAssetTimestampDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'lastModifiedAssetTimestamp', Sort.desc); }); @@ -1854,7 +1985,7 @@ extension AlbumQuerySortThenBy on QueryBuilder { } QueryBuilder - thenByLastModifiedAssetTimestampDesc() { + thenByLastModifiedAssetTimestampDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'lastModifiedAssetTimestamp', Sort.desc); }); @@ -1958,8 +2089,9 @@ extension AlbumQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctByDescription( - {bool caseSensitive = true}) { + QueryBuilder distinctByDescription({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'description', caseSensitive: caseSensitive); }); @@ -1977,8 +2109,9 @@ extension AlbumQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctByLocalId( - {bool caseSensitive = true}) { + QueryBuilder distinctByLocalId({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'localId', caseSensitive: caseSensitive); }); @@ -1990,15 +2123,17 @@ extension AlbumQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctByName( - {bool caseSensitive = true}) { + QueryBuilder distinctByName({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'name', caseSensitive: caseSensitive); }); } - QueryBuilder distinctByRemoteId( - {bool caseSensitive = true}) { + QueryBuilder distinctByRemoteId({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'remoteId', caseSensitive: caseSensitive); }); @@ -2055,7 +2190,7 @@ extension AlbumQueryProperty on QueryBuilder { } QueryBuilder - lastModifiedAssetTimestampProperty() { + lastModifiedAssetTimestampProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'lastModifiedAssetTimestamp'); }); diff --git a/mobile/lib/entities/android_device_asset.entity.g.dart b/mobile/lib/entities/android_device_asset.entity.g.dart index eaa7658565..9034709b8e 100644 --- a/mobile/lib/entities/android_device_asset.entity.g.dart +++ b/mobile/lib/entities/android_device_asset.entity.g.dart @@ -18,12 +18,9 @@ const AndroidDeviceAssetSchema = CollectionSchema( name: r'AndroidDeviceAsset', id: -6758387181232899335, properties: { - r'hash': PropertySchema( - id: 0, - name: r'hash', - type: IsarType.byteList, - ) + r'hash': PropertySchema(id: 0, name: r'hash', type: IsarType.byteList), }, + estimateSize: _androidDeviceAssetEstimateSize, serialize: _androidDeviceAssetSerialize, deserialize: _androidDeviceAssetDeserialize, @@ -40,12 +37,13 @@ const AndroidDeviceAssetSchema = CollectionSchema( name: r'hash', type: IndexType.hash, caseSensitive: false, - ) + ), ], - ) + ), }, links: {}, embeddedSchemas: {}, + getId: _androidDeviceAssetGetId, getLinks: _androidDeviceAssetGetLinks, attach: _androidDeviceAssetAttach, @@ -103,12 +101,16 @@ Id _androidDeviceAssetGetId(AndroidDeviceAsset object) { } List> _androidDeviceAssetGetLinks( - AndroidDeviceAsset object) { + AndroidDeviceAsset object, +) { return []; } void _androidDeviceAssetAttach( - IsarCollection col, Id id, AndroidDeviceAsset object) { + IsarCollection col, + Id id, + AndroidDeviceAsset object, +) { object.id = id; } @@ -124,17 +126,14 @@ extension AndroidDeviceAssetQueryWhereSort extension AndroidDeviceAssetQueryWhere on QueryBuilder { QueryBuilder - idEqualTo(Id id) { + idEqualTo(Id id) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: id, - upper: id, - )); + return query.addWhereClause(IdWhereClause.between(lower: id, upper: id)); }); } QueryBuilder - idNotEqualTo(Id id) { + idNotEqualTo(Id id) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query @@ -157,7 +156,7 @@ extension AndroidDeviceAssetQueryWhere } QueryBuilder - idGreaterThan(Id id, {bool include = false}) { + idGreaterThan(Id id, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: id, includeLower: include), @@ -166,7 +165,7 @@ extension AndroidDeviceAssetQueryWhere } QueryBuilder - idLessThan(Id id, {bool include = false}) { + idLessThan(Id id, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: id, includeUpper: include), @@ -175,63 +174,72 @@ extension AndroidDeviceAssetQueryWhere } QueryBuilder - idBetween( + idBetween( Id lowerId, Id upperId, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerId, - includeLower: includeLower, - upper: upperId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder - hashEqualTo(List hash) { + hashEqualTo(List hash) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'hash', - value: [hash], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'hash', value: [hash]), + ); }); } QueryBuilder - hashNotEqualTo(List hash) { + hashNotEqualTo(List hash) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [], - upper: [hash], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [hash], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [], + upper: [hash], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [hash], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [hash], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [], - upper: [hash], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [hash], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [], + upper: [hash], + includeUpper: false, + ), + ); } }); } @@ -240,134 +248,97 @@ extension AndroidDeviceAssetQueryWhere extension AndroidDeviceAssetQueryFilter on QueryBuilder { QueryBuilder - hashElementEqualTo(int value) { + hashElementEqualTo(int value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'hash', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'hash', value: value), + ); }); } QueryBuilder - hashElementGreaterThan( - int value, { - bool include = false, - }) { + hashElementGreaterThan(int value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'hash', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'hash', + value: value, + ), + ); }); } QueryBuilder - hashElementLessThan( - int value, { - bool include = false, - }) { + hashElementLessThan(int value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'hash', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'hash', + value: value, + ), + ); }); } QueryBuilder - hashElementBetween( + hashElementBetween( int lower, int upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'hash', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); - }); - } - - QueryBuilder - hashLengthEqualTo(int length) { - return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - length, - true, - length, - true, + return query.addFilterCondition( + FilterCondition.between( + property: r'hash', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), ); }); } QueryBuilder - hashIsEmpty() { + hashLengthEqualTo(int length) { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - 0, - true, - 0, - true, - ); + return query.listLength(r'hash', length, true, length, true); }); } QueryBuilder - hashIsNotEmpty() { + hashIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - 0, - false, - 999999, - true, - ); + return query.listLength(r'hash', 0, true, 0, true); }); } QueryBuilder - hashLengthLessThan( - int length, { - bool include = false, - }) { + hashIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - 0, - true, - length, - include, - ); + return query.listLength(r'hash', 0, false, 999999, true); }); } QueryBuilder - hashLengthGreaterThan( - int length, { - bool include = false, - }) { + hashLengthLessThan(int length, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - length, - include, - 999999, - true, - ); + return query.listLength(r'hash', 0, true, length, include); }); } QueryBuilder - hashLengthBetween( + hashLengthGreaterThan(int length, {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.listLength(r'hash', length, include, 999999, true); + }); + } + + QueryBuilder + hashLengthBetween( int lower, int upper, { bool includeLower = true, @@ -385,58 +356,57 @@ extension AndroidDeviceAssetQueryFilter } QueryBuilder - idEqualTo(Id value) { + idEqualTo(Id value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: value), + ); }); } QueryBuilder - idGreaterThan( - Id value, { - bool include = false, - }) { + idGreaterThan(Id value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + ), + ); }); } QueryBuilder - idLessThan( - Id value, { - bool include = false, - }) { + idLessThan(Id value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + ), + ); }); } QueryBuilder - idBetween( + idBetween( Id lower, Id upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } } @@ -453,14 +423,14 @@ extension AndroidDeviceAssetQuerySortBy extension AndroidDeviceAssetQuerySortThenBy on QueryBuilder { QueryBuilder - thenById() { + thenById() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'id', Sort.asc); }); } QueryBuilder - thenByIdDesc() { + thenByIdDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'id', Sort.desc); }); @@ -470,7 +440,7 @@ extension AndroidDeviceAssetQuerySortThenBy extension AndroidDeviceAssetQueryWhereDistinct on QueryBuilder { QueryBuilder - distinctByHash() { + distinctByHash() { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'hash'); }); diff --git a/mobile/lib/entities/asset.entity.dart b/mobile/lib/entities/asset.entity.dart index d754b11dc5..0d549457a1 100644 --- a/mobile/lib/entities/asset.entity.dart +++ b/mobile/lib/entities/asset.entity.dart @@ -4,8 +4,7 @@ import 'dart:io'; import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/models/exif.model.dart'; import 'package:immich_mobile/extensions/string_extensions.dart'; -import 'package:immich_mobile/infrastructure/entities/exif.entity.dart' - as entity; +import 'package:immich_mobile/infrastructure/entities/exif.entity.dart' as entity; import 'package:immich_mobile/infrastructure/utils/exif.converter.dart'; import 'package:immich_mobile/utils/diff.dart'; import 'package:immich_mobile/utils/hash.dart'; @@ -20,34 +19,30 @@ part 'asset.entity.g.dart'; @Collection(inheritance: false) class Asset { Asset.remote(AssetResponseDto remote) - : remoteId = remote.id, - checksum = remote.checksum, - fileCreatedAt = remote.fileCreatedAt, - fileModifiedAt = remote.fileModifiedAt, - updatedAt = remote.updatedAt, - durationInSeconds = remote.duration.toDuration()?.inSeconds ?? 0, - type = remote.type.toAssetType(), - fileName = remote.originalFileName, - height = remote.exifInfo?.exifImageHeight?.toInt(), - width = remote.exifInfo?.exifImageWidth?.toInt(), - livePhotoVideoId = remote.livePhotoVideoId, - ownerId = fastHash(remote.ownerId), - exifInfo = remote.exifInfo == null - ? null - : ExifDtoConverter.fromDto(remote.exifInfo!), - isFavorite = remote.isFavorite, - isArchived = remote.isArchived, - isTrashed = remote.isTrashed, - isOffline = remote.isOffline, - // workaround to nullify stackPrimaryAssetId for the parent asset until we refactor the mobile app - // stack handling to properly handle it - stackPrimaryAssetId = remote.stack?.primaryAssetId == remote.id - ? null - : remote.stack?.primaryAssetId, - stackCount = remote.stack?.assetCount ?? 0, - stackId = remote.stack?.id, - thumbhash = remote.thumbhash, - visibility = getVisibility(remote.visibility); + : remoteId = remote.id, + checksum = remote.checksum, + fileCreatedAt = remote.fileCreatedAt, + fileModifiedAt = remote.fileModifiedAt, + updatedAt = remote.updatedAt, + durationInSeconds = remote.duration.toDuration()?.inSeconds ?? 0, + type = remote.type.toAssetType(), + fileName = remote.originalFileName, + height = remote.exifInfo?.exifImageHeight?.toInt(), + width = remote.exifInfo?.exifImageWidth?.toInt(), + livePhotoVideoId = remote.livePhotoVideoId, + ownerId = fastHash(remote.ownerId), + exifInfo = remote.exifInfo == null ? null : ExifDtoConverter.fromDto(remote.exifInfo!), + isFavorite = remote.isFavorite, + isArchived = remote.isArchived, + isTrashed = remote.isTrashed, + isOffline = remote.isOffline, + // workaround to nullify stackPrimaryAssetId for the parent asset until we refactor the mobile app + // stack handling to properly handle it + stackPrimaryAssetId = remote.stack?.primaryAssetId == remote.id ? null : remote.stack?.primaryAssetId, + stackCount = remote.stack?.assetCount ?? 0, + stackId = remote.stack?.id, + thumbhash = remote.thumbhash, + visibility = getVisibility(remote.visibility); Asset({ this.id = Isar.autoIncrement, @@ -108,8 +103,7 @@ class Asset { throw Exception('Asset $fileName has no local data'); } - final updatedLocal = - _didUpdateLocal ? local : await local.obtainForNewProperties(); + final updatedLocal = _didUpdateLocal ? local : await local.obtainForNewProperties(); if (updatedLocal == null) { throw Exception('Could not fetch local data for $fileName'); } @@ -133,11 +127,7 @@ class Asset { @Index(unique: false, replace: false, type: IndexType.hash) String? localId; - @Index( - unique: true, - replace: false, - composite: [CompositeIndex("checksum", type: IndexType.hash)], - ) + @Index(unique: true, replace: false, composite: [CompositeIndex("checksum", type: IndexType.hash)]) int ownerId; DateTime fileCreatedAt; @@ -185,10 +175,7 @@ class Asset { final orientatedWidth = this.orientatedWidth; final orientatedHeight = this.orientatedHeight; - if (orientatedWidth != null && - orientatedHeight != null && - orientatedWidth > 0 && - orientatedHeight > 0) { + if (orientatedWidth != null && orientatedHeight != null && orientatedWidth > 0 && orientatedHeight > 0) { return orientatedWidth.toDouble() / orientatedHeight.toDouble(); } @@ -389,8 +376,7 @@ class Asset { // workaround to nullify stackPrimaryAssetId for the parent asset until we refactor the mobile app // stack handling to properly handle it stackId: stackId, - stackPrimaryAssetId: - stackPrimaryAssetId == remoteId ? null : stackPrimaryAssetId, + stackPrimaryAssetId: stackPrimaryAssetId == remoteId ? null : stackPrimaryAssetId, stackCount: stackCount, isFavorite: isFavorite, isArchived: isArchived, @@ -410,9 +396,7 @@ class Asset { // workaround to nullify stackPrimaryAssetId for the parent asset until we refactor the mobile app // stack handling to properly handle it stackId: a.stackId, - stackPrimaryAssetId: a.stackPrimaryAssetId == a.remoteId - ? null - : a.stackPrimaryAssetId, + stackPrimaryAssetId: a.stackPrimaryAssetId == a.remoteId ? null : a.stackPrimaryAssetId, stackCount: a.stackCount, // isFavorite + isArchived are not set by device-only assets isFavorite: a.isFavorite, @@ -428,8 +412,7 @@ class Asset { localId: localId ?? a.localId, width: width ?? a.width, height: height ?? a.height, - exifInfo: exifInfo ?? - a.exifInfo?.copyWith(assetId: id), // updated to use assetId + exifInfo: exifInfo ?? a.exifInfo?.copyWith(assetId: id), // updated to use assetId ); } } @@ -460,49 +443,45 @@ class Asset { int? stackCount, String? thumbhash, AssetVisibilityEnum? visibility, - }) => - Asset( - id: id ?? this.id, - checksum: checksum ?? this.checksum, - remoteId: remoteId ?? this.remoteId, - localId: localId ?? this.localId, - ownerId: ownerId ?? this.ownerId, - fileCreatedAt: fileCreatedAt ?? this.fileCreatedAt, - fileModifiedAt: fileModifiedAt ?? this.fileModifiedAt, - updatedAt: updatedAt ?? this.updatedAt, - durationInSeconds: durationInSeconds ?? this.durationInSeconds, - type: type ?? this.type, - width: width ?? this.width, - height: height ?? this.height, - fileName: fileName ?? this.fileName, - livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, - isFavorite: isFavorite ?? this.isFavorite, - isArchived: isArchived ?? this.isArchived, - isTrashed: isTrashed ?? this.isTrashed, - isOffline: isOffline ?? this.isOffline, - exifInfo: exifInfo ?? this.exifInfo, - stackId: stackId ?? this.stackId, - stackPrimaryAssetId: stackPrimaryAssetId ?? this.stackPrimaryAssetId, - stackCount: stackCount ?? this.stackCount, - thumbhash: thumbhash ?? this.thumbhash, - visibility: visibility ?? this.visibility, - ); + }) => Asset( + id: id ?? this.id, + checksum: checksum ?? this.checksum, + remoteId: remoteId ?? this.remoteId, + localId: localId ?? this.localId, + ownerId: ownerId ?? this.ownerId, + fileCreatedAt: fileCreatedAt ?? this.fileCreatedAt, + fileModifiedAt: fileModifiedAt ?? this.fileModifiedAt, + updatedAt: updatedAt ?? this.updatedAt, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + type: type ?? this.type, + width: width ?? this.width, + height: height ?? this.height, + fileName: fileName ?? this.fileName, + livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, + isFavorite: isFavorite ?? this.isFavorite, + isArchived: isArchived ?? this.isArchived, + isTrashed: isTrashed ?? this.isTrashed, + isOffline: isOffline ?? this.isOffline, + exifInfo: exifInfo ?? this.exifInfo, + stackId: stackId ?? this.stackId, + stackPrimaryAssetId: stackPrimaryAssetId ?? this.stackPrimaryAssetId, + stackCount: stackCount ?? this.stackCount, + thumbhash: thumbhash ?? this.thumbhash, + visibility: visibility ?? this.visibility, + ); Future put(Isar db) async { await db.assets.put(this); if (exifInfo != null) { - await db.exifInfos - .put(entity.ExifInfo.fromDto(exifInfo!.copyWith(assetId: id))); + await db.exifInfos.put(entity.ExifInfo.fromDto(exifInfo!.copyWith(assetId: id))); } } static int compareById(Asset a, Asset b) => a.id.compareTo(b.id); - static int compareByLocalId(Asset a, Asset b) => - compareToNullable(a.localId, b.localId); + static int compareByLocalId(Asset a, Asset b) => compareToNullable(a.localId, b.localId); - static int compareByChecksum(Asset a, Asset b) => - a.checksum.compareTo(b.checksum); + static int compareByChecksum(Asset a, Asset b) => a.checksum.compareTo(b.checksum); static int compareByOwnerChecksum(Asset a, Asset b) { final int ownerIdOrder = a.ownerId.compareTo(b.ownerId); @@ -510,10 +489,7 @@ class Asset { return compareByChecksum(a, b); } - static int compareByOwnerChecksumCreatedModified( - Asset a, - Asset b, - ) { + static int compareByOwnerChecksumCreatedModified(Asset a, Asset b) { final int ownerIdOrder = a.ownerId.compareTo(b.ownerId); if (ownerIdOrder != 0) return ownerIdOrder; final int checksumOrder = compareByChecksum(a, b); @@ -555,11 +531,11 @@ class Asset { } static getVisibility(AssetVisibility visibility) => switch (visibility) { - AssetVisibility.archive => AssetVisibilityEnum.archive, - AssetVisibility.hidden => AssetVisibilityEnum.hidden, - AssetVisibility.locked => AssetVisibilityEnum.locked, - AssetVisibility.timeline || _ => AssetVisibilityEnum.timeline, - }; + AssetVisibility.archive => AssetVisibilityEnum.archive, + AssetVisibility.hidden => AssetVisibilityEnum.hidden, + AssetVisibility.locked => AssetVisibilityEnum.locked, + AssetVisibility.timeline || _ => AssetVisibilityEnum.timeline, + }; } enum AssetType { @@ -572,41 +548,28 @@ enum AssetType { extension AssetTypeEnumHelper on AssetTypeEnum { AssetType toAssetType() => switch (this) { - AssetTypeEnum.IMAGE => AssetType.image, - AssetTypeEnum.VIDEO => AssetType.video, - AssetTypeEnum.AUDIO => AssetType.audio, - AssetTypeEnum.OTHER => AssetType.other, - _ => throw Exception(), - }; + AssetTypeEnum.IMAGE => AssetType.image, + AssetTypeEnum.VIDEO => AssetType.video, + AssetTypeEnum.AUDIO => AssetType.audio, + AssetTypeEnum.OTHER => AssetType.other, + _ => throw Exception(), + }; } /// Describes where the information of this asset came from: /// only from the local device, only from the remote server or merged from both -enum AssetState { - local, - remote, - merged, -} +enum AssetState { local, remote, merged } extension AssetsHelper on IsarCollection { - Future deleteAllByRemoteId(Iterable ids) => - ids.isEmpty ? Future.value(0) : remote(ids).deleteAll(); - Future deleteAllByLocalId(Iterable ids) => - ids.isEmpty ? Future.value(0) : local(ids).deleteAll(); - Future> getAllByRemoteId(Iterable ids) => - ids.isEmpty ? Future.value([]) : remote(ids).findAll(); - Future> getAllByLocalId(Iterable ids) => - ids.isEmpty ? Future.value([]) : local(ids).findAll(); - Future getByRemoteId(String id) => - where().remoteIdEqualTo(id).findFirst(); + Future deleteAllByRemoteId(Iterable ids) => ids.isEmpty ? Future.value(0) : remote(ids).deleteAll(); + Future deleteAllByLocalId(Iterable ids) => ids.isEmpty ? Future.value(0) : local(ids).deleteAll(); + Future> getAllByRemoteId(Iterable ids) => ids.isEmpty ? Future.value([]) : remote(ids).findAll(); + Future> getAllByLocalId(Iterable ids) => ids.isEmpty ? Future.value([]) : local(ids).findAll(); + Future getByRemoteId(String id) => where().remoteIdEqualTo(id).findFirst(); - QueryBuilder remote( - Iterable ids, - ) => + QueryBuilder remote(Iterable ids) => where().anyOf(ids, (q, String e) => q.remoteIdEqualTo(e)); - QueryBuilder local( - Iterable ids, - ) { + QueryBuilder local(Iterable ids) { return where().anyOf(ids, (q, String e) => q.localIdEqualTo(e)); } } diff --git a/mobile/lib/entities/asset.entity.g.dart b/mobile/lib/entities/asset.entity.g.dart index b558690813..be5b427d01 100644 --- a/mobile/lib/entities/asset.entity.g.dart +++ b/mobile/lib/entities/asset.entity.g.dart @@ -42,11 +42,7 @@ const AssetSchema = CollectionSchema( name: r'fileName', type: IsarType.string, ), - r'height': PropertySchema( - id: 5, - name: r'height', - type: IsarType.int, - ), + r'height': PropertySchema(id: 5, name: r'height', type: IsarType.int), r'isArchived': PropertySchema( id: 6, name: r'isArchived', @@ -72,16 +68,8 @@ const AssetSchema = CollectionSchema( name: r'livePhotoVideoId', type: IsarType.string, ), - r'localId': PropertySchema( - id: 11, - name: r'localId', - type: IsarType.string, - ), - r'ownerId': PropertySchema( - id: 12, - name: r'ownerId', - type: IsarType.long, - ), + r'localId': PropertySchema(id: 11, name: r'localId', type: IsarType.string), + r'ownerId': PropertySchema(id: 12, name: r'ownerId', type: IsarType.long), r'remoteId': PropertySchema( id: 13, name: r'remoteId', @@ -92,11 +80,7 @@ const AssetSchema = CollectionSchema( name: r'stackCount', type: IsarType.long, ), - r'stackId': PropertySchema( - id: 15, - name: r'stackId', - type: IsarType.string, - ), + r'stackId': PropertySchema(id: 15, name: r'stackId', type: IsarType.string), r'stackPrimaryAssetId': PropertySchema( id: 16, name: r'stackPrimaryAssetId', @@ -124,12 +108,9 @@ const AssetSchema = CollectionSchema( type: IsarType.byte, enumMap: _AssetvisibilityEnumValueMap, ), - r'width': PropertySchema( - id: 21, - name: r'width', - type: IsarType.int, - ) + r'width': PropertySchema(id: 21, name: r'width', type: IsarType.int), }, + estimateSize: _assetEstimateSize, serialize: _assetSerialize, deserialize: _assetDeserialize, @@ -146,7 +127,7 @@ const AssetSchema = CollectionSchema( name: r'remoteId', type: IndexType.hash, caseSensitive: true, - ) + ), ], ), r'localId': IndexSchema( @@ -159,7 +140,7 @@ const AssetSchema = CollectionSchema( name: r'localId', type: IndexType.hash, caseSensitive: true, - ) + ), ], ), r'ownerId_checksum': IndexSchema( @@ -177,12 +158,13 @@ const AssetSchema = CollectionSchema( name: r'checksum', type: IndexType.hash, caseSensitive: true, - ) + ), ], - ) + ), }, links: {}, embeddedSchemas: {}, + getId: _assetGetId, getLinks: _assetGetLinks, attach: _assetAttach, @@ -292,12 +274,13 @@ Asset _assetDeserialize( stackId: reader.readStringOrNull(offsets[15]), stackPrimaryAssetId: reader.readStringOrNull(offsets[16]), thumbhash: reader.readStringOrNull(offsets[17]), - type: _AssettypeValueEnumMap[reader.readByteOrNull(offsets[18])] ?? + type: + _AssettypeValueEnumMap[reader.readByteOrNull(offsets[18])] ?? AssetType.other, updatedAt: reader.readDateTime(offsets[19]), visibility: _AssetvisibilityValueEnumMap[reader.readByteOrNull(offsets[20])] ?? - AssetVisibilityEnum.timeline, + AssetVisibilityEnum.timeline, width: reader.readIntOrNull(offsets[21]), ); return object; @@ -348,12 +331,14 @@ P _assetDeserializeProp

( return (reader.readStringOrNull(offset)) as P; case 18: return (_AssettypeValueEnumMap[reader.readByteOrNull(offset)] ?? - AssetType.other) as P; + AssetType.other) + as P; case 19: return (reader.readDateTime(offset)) as P; case 20: return (_AssetvisibilityValueEnumMap[reader.readByteOrNull(offset)] ?? - AssetVisibilityEnum.timeline) as P; + AssetVisibilityEnum.timeline) + as P; case 21: return (reader.readIntOrNull(offset)) as P; default: @@ -361,12 +346,7 @@ P _assetDeserializeProp

( } } -const _AssettypeEnumValueMap = { - 'other': 0, - 'image': 1, - 'video': 2, - 'audio': 3, -}; +const _AssettypeEnumValueMap = {'other': 0, 'image': 1, 'video': 2, 'audio': 3}; const _AssettypeValueEnumMap = { 0: AssetType.other, 1: AssetType.image, @@ -416,10 +396,14 @@ extension AssetByIndex on IsarCollection { } Future> getAllByOwnerIdChecksum( - List ownerIdValues, List checksumValues) { + List ownerIdValues, + List checksumValues, + ) { final len = ownerIdValues.length; - assert(checksumValues.length == len, - 'All index values must have the same length'); + assert( + checksumValues.length == len, + 'All index values must have the same length', + ); final values = >[]; for (var i = 0; i < len; i++) { values.add([ownerIdValues[i], checksumValues[i]]); @@ -429,10 +413,14 @@ extension AssetByIndex on IsarCollection { } List getAllByOwnerIdChecksumSync( - List ownerIdValues, List checksumValues) { + List ownerIdValues, + List checksumValues, + ) { final len = ownerIdValues.length; - assert(checksumValues.length == len, - 'All index values must have the same length'); + assert( + checksumValues.length == len, + 'All index values must have the same length', + ); final values = >[]; for (var i = 0; i < len; i++) { values.add([ownerIdValues[i], checksumValues[i]]); @@ -442,10 +430,14 @@ extension AssetByIndex on IsarCollection { } Future deleteAllByOwnerIdChecksum( - List ownerIdValues, List checksumValues) { + List ownerIdValues, + List checksumValues, + ) { final len = ownerIdValues.length; - assert(checksumValues.length == len, - 'All index values must have the same length'); + assert( + checksumValues.length == len, + 'All index values must have the same length', + ); final values = >[]; for (var i = 0; i < len; i++) { values.add([ownerIdValues[i], checksumValues[i]]); @@ -455,10 +447,14 @@ extension AssetByIndex on IsarCollection { } int deleteAllByOwnerIdChecksumSync( - List ownerIdValues, List checksumValues) { + List ownerIdValues, + List checksumValues, + ) { final len = ownerIdValues.length; - assert(checksumValues.length == len, - 'All index values must have the same length'); + assert( + checksumValues.length == len, + 'All index values must have the same length', + ); final values = >[]; for (var i = 0; i < len; i++) { values.add([ownerIdValues[i], checksumValues[i]]); @@ -479,10 +475,15 @@ extension AssetByIndex on IsarCollection { return putAllByIndex(r'ownerId_checksum', objects); } - List putAllByOwnerIdChecksumSync(List objects, - {bool saveLinks = true}) { - return putAllByIndexSync(r'ownerId_checksum', objects, - saveLinks: saveLinks); + List putAllByOwnerIdChecksumSync( + List objects, { + bool saveLinks = true, + }) { + return putAllByIndexSync( + r'ownerId_checksum', + objects, + saveLinks: saveLinks, + ); } } @@ -497,10 +498,7 @@ extension AssetQueryWhereSort on QueryBuilder { extension AssetQueryWhere on QueryBuilder { QueryBuilder idEqualTo(Id id) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: id, - upper: id, - )); + return query.addWhereClause(IdWhereClause.between(lower: id, upper: id)); }); } @@ -526,8 +524,10 @@ extension AssetQueryWhere on QueryBuilder { }); } - QueryBuilder idGreaterThan(Id id, - {bool include = false}) { + QueryBuilder idGreaterThan( + Id id, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: id, includeLower: include), @@ -535,8 +535,10 @@ extension AssetQueryWhere on QueryBuilder { }); } - QueryBuilder idLessThan(Id id, - {bool include = false}) { + QueryBuilder idLessThan( + Id id, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: id, includeUpper: include), @@ -551,186 +553,220 @@ extension AssetQueryWhere on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerId, - includeLower: includeLower, - upper: upperId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder remoteIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'remoteId', - value: [null], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'remoteId', value: [null]), + ); }); } QueryBuilder remoteIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [null], - includeLower: false, - upper: [], - )); + return query.addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [null], + includeLower: false, + upper: [], + ), + ); }); } QueryBuilder remoteIdEqualTo( - String? remoteId) { + String? remoteId, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'remoteId', - value: [remoteId], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'remoteId', value: [remoteId]), + ); }); } QueryBuilder remoteIdNotEqualTo( - String? remoteId) { + String? remoteId, + ) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [], - upper: [remoteId], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [remoteId], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [], + upper: [remoteId], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [remoteId], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [remoteId], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'remoteId', - lower: [], - upper: [remoteId], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [remoteId], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'remoteId', + lower: [], + upper: [remoteId], + includeUpper: false, + ), + ); } }); } QueryBuilder localIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'localId', - value: [null], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'localId', value: [null]), + ); }); } QueryBuilder localIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [null], - includeLower: false, - upper: [], - )); + return query.addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [null], + includeLower: false, + upper: [], + ), + ); }); } QueryBuilder localIdEqualTo( - String? localId) { + String? localId, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'localId', - value: [localId], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'localId', value: [localId]), + ); }); } QueryBuilder localIdNotEqualTo( - String? localId) { + String? localId, + ) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [], - upper: [localId], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [localId], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [], + upper: [localId], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [localId], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [localId], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'localId', - lower: [], - upper: [localId], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [localId], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'localId', + lower: [], + upper: [localId], + includeUpper: false, + ), + ); } }); } QueryBuilder ownerIdEqualToAnyChecksum( - int ownerId) { + int ownerId, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'ownerId_checksum', - value: [ownerId], - )); + return query.addWhereClause( + IndexWhereClause.equalTo( + indexName: r'ownerId_checksum', + value: [ownerId], + ), + ); }); } QueryBuilder ownerIdNotEqualToAnyChecksum( - int ownerId) { + int ownerId, + ) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [], - upper: [ownerId], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [ownerId], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [], + upper: [ownerId], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [ownerId], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [ownerId], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [], - upper: [ownerId], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [ownerId], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [], + upper: [ownerId], + includeUpper: false, + ), + ); } }); } @@ -740,12 +776,14 @@ extension AssetQueryWhere on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [ownerId], - includeLower: include, - upper: [], - )); + return query.addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [ownerId], + includeLower: include, + upper: [], + ), + ); }); } @@ -754,12 +792,14 @@ extension AssetQueryWhere on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [], - upper: [ownerId], - includeUpper: include, - )); + return query.addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [], + upper: [ownerId], + includeUpper: include, + ), + ); }); } @@ -770,57 +810,71 @@ extension AssetQueryWhere on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [lowerOwnerId], - includeLower: includeLower, - upper: [upperOwnerId], - includeUpper: includeUpper, - )); + return query.addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [lowerOwnerId], + includeLower: includeLower, + upper: [upperOwnerId], + includeUpper: includeUpper, + ), + ); }); } QueryBuilder ownerIdChecksumEqualTo( - int ownerId, String checksum) { + int ownerId, + String checksum, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'ownerId_checksum', - value: [ownerId, checksum], - )); + return query.addWhereClause( + IndexWhereClause.equalTo( + indexName: r'ownerId_checksum', + value: [ownerId, checksum], + ), + ); }); } QueryBuilder - ownerIdEqualToChecksumNotEqualTo(int ownerId, String checksum) { + ownerIdEqualToChecksumNotEqualTo(int ownerId, String checksum) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [ownerId], - upper: [ownerId, checksum], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [ownerId, checksum], - includeLower: false, - upper: [ownerId], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [ownerId], + upper: [ownerId, checksum], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [ownerId, checksum], + includeLower: false, + upper: [ownerId], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [ownerId, checksum], - includeLower: false, - upper: [ownerId], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'ownerId_checksum', - lower: [ownerId], - upper: [ownerId, checksum], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [ownerId, checksum], + includeLower: false, + upper: [ownerId], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'ownerId_checksum', + lower: [ownerId], + upper: [ownerId, checksum], + includeUpper: false, + ), + ); } }); } @@ -832,11 +886,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'checksum', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'checksum', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -846,12 +902,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'checksum', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'checksum', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -861,12 +919,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'checksum', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'checksum', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -878,14 +938,16 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'checksum', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'checksum', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -894,11 +956,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'checksum', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'checksum', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -907,77 +971,82 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'checksum', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'checksum', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder checksumContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'checksum', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'checksum', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder checksumMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'checksum', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'checksum', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder checksumIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'checksum', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'checksum', value: ''), + ); }); } QueryBuilder checksumIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'checksum', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'checksum', value: ''), + ); }); } QueryBuilder durationInSecondsEqualTo( - int value) { + int value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'durationInSeconds', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'durationInSeconds', value: value), + ); }); } QueryBuilder - durationInSecondsGreaterThan( - int value, { - bool include = false, - }) { + durationInSecondsGreaterThan(int value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'durationInSeconds', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'durationInSeconds', + value: value, + ), + ); }); } @@ -986,11 +1055,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'durationInSeconds', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'durationInSeconds', + value: value, + ), + ); }); } @@ -1001,23 +1072,25 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'durationInSeconds', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'durationInSeconds', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder fileCreatedAtEqualTo( - DateTime value) { + DateTime value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'fileCreatedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'fileCreatedAt', value: value), + ); }); } @@ -1026,11 +1099,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'fileCreatedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'fileCreatedAt', + value: value, + ), + ); }); } @@ -1039,11 +1114,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'fileCreatedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'fileCreatedAt', + value: value, + ), + ); }); } @@ -1054,23 +1131,25 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'fileCreatedAt', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'fileCreatedAt', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder fileModifiedAtEqualTo( - DateTime value) { + DateTime value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'fileModifiedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'fileModifiedAt', value: value), + ); }); } @@ -1079,11 +1158,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'fileModifiedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'fileModifiedAt', + value: value, + ), + ); }); } @@ -1092,11 +1173,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'fileModifiedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'fileModifiedAt', + value: value, + ), + ); }); } @@ -1107,13 +1190,15 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'fileModifiedAt', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'fileModifiedAt', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } @@ -1122,11 +1207,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'fileName', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'fileName', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1136,12 +1223,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'fileName', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'fileName', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1151,12 +1240,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'fileName', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'fileName', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1168,14 +1259,16 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'fileName', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'fileName', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1184,11 +1277,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'fileName', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'fileName', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1197,78 +1292,83 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'fileName', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'fileName', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder fileNameContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'fileName', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'fileName', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder fileNameMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'fileName', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'fileName', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder fileNameIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'fileName', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'fileName', value: ''), + ); }); } QueryBuilder fileNameIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'fileName', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'fileName', value: ''), + ); }); } QueryBuilder heightIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'height', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'height'), + ); }); } QueryBuilder heightIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'height', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'height'), + ); }); } QueryBuilder heightEqualTo(int? value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'height', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'height', value: value), + ); }); } @@ -1277,11 +1377,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'height', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'height', + value: value, + ), + ); }); } @@ -1290,11 +1392,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'height', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'height', + value: value, + ), + ); }); } @@ -1305,22 +1409,23 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'height', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'height', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder idEqualTo(Id value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: value), + ); }); } @@ -1329,11 +1434,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + ), + ); }); } @@ -1342,11 +1449,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + ), + ); }); } @@ -1357,70 +1466,72 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder isArchivedEqualTo( - bool value) { + bool value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isArchived', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isArchived', value: value), + ); }); } QueryBuilder isFavoriteEqualTo( - bool value) { + bool value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isFavorite', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isFavorite', value: value), + ); }); } QueryBuilder isOfflineEqualTo( - bool value) { + bool value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isOffline', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isOffline', value: value), + ); }); } QueryBuilder isTrashedEqualTo( - bool value) { + bool value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isTrashed', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isTrashed', value: value), + ); }); } QueryBuilder livePhotoVideoIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'livePhotoVideoId', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'livePhotoVideoId'), + ); }); } QueryBuilder - livePhotoVideoIdIsNotNull() { + livePhotoVideoIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'livePhotoVideoId', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'livePhotoVideoId'), + ); }); } @@ -1429,11 +1540,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'livePhotoVideoId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'livePhotoVideoId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1443,12 +1556,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'livePhotoVideoId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'livePhotoVideoId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1458,12 +1573,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'livePhotoVideoId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'livePhotoVideoId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1475,14 +1592,16 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'livePhotoVideoId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'livePhotoVideoId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1491,11 +1610,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'livePhotoVideoId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'livePhotoVideoId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1504,70 +1625,76 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'livePhotoVideoId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'livePhotoVideoId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder livePhotoVideoIdContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'livePhotoVideoId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'livePhotoVideoId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder livePhotoVideoIdMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'livePhotoVideoId', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'livePhotoVideoId', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder livePhotoVideoIdIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'livePhotoVideoId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'livePhotoVideoId', value: ''), + ); }); } QueryBuilder - livePhotoVideoIdIsNotEmpty() { + livePhotoVideoIdIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'livePhotoVideoId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'livePhotoVideoId', value: ''), + ); }); } QueryBuilder localIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'localId', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'localId'), + ); }); } QueryBuilder localIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'localId', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'localId'), + ); }); } @@ -1576,11 +1703,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1590,12 +1719,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1605,12 +1736,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1622,14 +1755,16 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'localId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'localId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1638,11 +1773,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1651,62 +1788,67 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder localIdContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'localId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'localId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder localIdMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'localId', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'localId', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder localIdIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'localId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'localId', value: ''), + ); }); } QueryBuilder localIdIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'localId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'localId', value: ''), + ); }); } QueryBuilder ownerIdEqualTo(int value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'ownerId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'ownerId', value: value), + ); }); } @@ -1715,11 +1857,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'ownerId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'ownerId', + value: value, + ), + ); }); } @@ -1728,11 +1872,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'ownerId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'ownerId', + value: value, + ), + ); }); } @@ -1743,29 +1889,31 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'ownerId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'ownerId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder remoteIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'remoteId', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'remoteId'), + ); }); } QueryBuilder remoteIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'remoteId', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'remoteId'), + ); }); } @@ -1774,11 +1922,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1788,12 +1938,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1803,12 +1955,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1820,14 +1974,16 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'remoteId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'remoteId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1836,11 +1992,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1849,63 +2007,69 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder remoteIdContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'remoteId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'remoteId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder remoteIdMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'remoteId', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'remoteId', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder remoteIdIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'remoteId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'remoteId', value: ''), + ); }); } QueryBuilder remoteIdIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'remoteId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'remoteId', value: ''), + ); }); } QueryBuilder stackCountEqualTo( - int value) { + int value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'stackCount', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'stackCount', value: value), + ); }); } @@ -1914,11 +2078,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'stackCount', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'stackCount', + value: value, + ), + ); }); } @@ -1927,11 +2093,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'stackCount', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'stackCount', + value: value, + ), + ); }); } @@ -1942,29 +2110,31 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'stackCount', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'stackCount', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder stackIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'stackId', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'stackId'), + ); }); } QueryBuilder stackIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'stackId', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'stackId'), + ); }); } @@ -1973,11 +2143,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'stackId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'stackId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1987,12 +2159,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'stackId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'stackId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2002,12 +2176,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'stackId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'stackId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2019,14 +2195,16 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'stackId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'stackId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2035,11 +2213,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'stackId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'stackId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2048,71 +2228,77 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'stackId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'stackId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder stackIdContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'stackId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'stackId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder stackIdMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'stackId', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'stackId', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder stackIdIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'stackId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'stackId', value: ''), + ); }); } QueryBuilder stackIdIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'stackId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'stackId', value: ''), + ); }); } QueryBuilder - stackPrimaryAssetIdIsNull() { + stackPrimaryAssetIdIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'stackPrimaryAssetId', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'stackPrimaryAssetId'), + ); }); } QueryBuilder - stackPrimaryAssetIdIsNotNull() { + stackPrimaryAssetIdIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'stackPrimaryAssetId', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'stackPrimaryAssetId'), + ); }); } @@ -2121,27 +2307,31 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'stackPrimaryAssetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'stackPrimaryAssetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - stackPrimaryAssetIdGreaterThan( + stackPrimaryAssetIdGreaterThan( String? value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'stackPrimaryAssetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'stackPrimaryAssetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2151,12 +2341,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'stackPrimaryAssetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'stackPrimaryAssetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2168,28 +2360,29 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'stackPrimaryAssetId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'stackPrimaryAssetId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - stackPrimaryAssetIdStartsWith( - String value, { - bool caseSensitive = true, - }) { + stackPrimaryAssetIdStartsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'stackPrimaryAssetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'stackPrimaryAssetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2198,71 +2391,80 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'stackPrimaryAssetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'stackPrimaryAssetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder stackPrimaryAssetIdContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'stackPrimaryAssetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'stackPrimaryAssetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder stackPrimaryAssetIdMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'stackPrimaryAssetId', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'stackPrimaryAssetId', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - stackPrimaryAssetIdIsEmpty() { + stackPrimaryAssetIdIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'stackPrimaryAssetId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'stackPrimaryAssetId', value: ''), + ); }); } QueryBuilder - stackPrimaryAssetIdIsNotEmpty() { + stackPrimaryAssetIdIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'stackPrimaryAssetId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + property: r'stackPrimaryAssetId', + value: '', + ), + ); }); } QueryBuilder thumbhashIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'thumbhash', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'thumbhash'), + ); }); } QueryBuilder thumbhashIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'thumbhash', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'thumbhash'), + ); }); } @@ -2271,11 +2473,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'thumbhash', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'thumbhash', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2285,12 +2489,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'thumbhash', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'thumbhash', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2300,12 +2506,14 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'thumbhash', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'thumbhash', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2317,14 +2525,16 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'thumbhash', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'thumbhash', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2333,11 +2543,13 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'thumbhash', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'thumbhash', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2346,63 +2558,69 @@ extension AssetQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'thumbhash', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'thumbhash', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder thumbhashContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'thumbhash', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'thumbhash', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder thumbhashMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'thumbhash', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'thumbhash', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder thumbhashIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'thumbhash', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'thumbhash', value: ''), + ); }); } QueryBuilder thumbhashIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'thumbhash', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'thumbhash', value: ''), + ); }); } QueryBuilder typeEqualTo( - AssetType value) { + AssetType value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'type', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'type', value: value), + ); }); } @@ -2411,11 +2629,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'type', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'type', + value: value, + ), + ); }); } @@ -2424,11 +2644,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'type', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'type', + value: value, + ), + ); }); } @@ -2439,23 +2661,25 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'type', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'type', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder updatedAtEqualTo( - DateTime value) { + DateTime value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'updatedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'updatedAt', value: value), + ); }); } @@ -2464,11 +2688,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'updatedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'updatedAt', + value: value, + ), + ); }); } @@ -2477,11 +2703,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'updatedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'updatedAt', + value: value, + ), + ); }); } @@ -2492,23 +2720,25 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'updatedAt', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'updatedAt', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder visibilityEqualTo( - AssetVisibilityEnum value) { + AssetVisibilityEnum value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'visibility', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'visibility', value: value), + ); }); } @@ -2517,11 +2747,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'visibility', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'visibility', + value: value, + ), + ); }); } @@ -2530,11 +2762,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'visibility', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'visibility', + value: value, + ), + ); }); } @@ -2545,38 +2779,39 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'visibility', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'visibility', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder widthIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'width', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'width'), + ); }); } QueryBuilder widthIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'width', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'width'), + ); }); } QueryBuilder widthEqualTo(int? value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'width', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'width', value: value), + ); }); } @@ -2585,11 +2820,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'width', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'width', + value: value, + ), + ); }); } @@ -2598,11 +2835,13 @@ extension AssetQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'width', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'width', + value: value, + ), + ); }); } @@ -2613,13 +2852,15 @@ extension AssetQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'width', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'width', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } } @@ -3173,8 +3414,9 @@ extension AssetQuerySortThenBy on QueryBuilder { } extension AssetQueryWhereDistinct on QueryBuilder { - QueryBuilder distinctByChecksum( - {bool caseSensitive = true}) { + QueryBuilder distinctByChecksum({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'checksum', caseSensitive: caseSensitive); }); @@ -3198,8 +3440,9 @@ extension AssetQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctByFileName( - {bool caseSensitive = true}) { + QueryBuilder distinctByFileName({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'fileName', caseSensitive: caseSensitive); }); @@ -3235,16 +3478,20 @@ extension AssetQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctByLivePhotoVideoId( - {bool caseSensitive = true}) { + QueryBuilder distinctByLivePhotoVideoId({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(r'livePhotoVideoId', - caseSensitive: caseSensitive); + return query.addDistinctBy( + r'livePhotoVideoId', + caseSensitive: caseSensitive, + ); }); } - QueryBuilder distinctByLocalId( - {bool caseSensitive = true}) { + QueryBuilder distinctByLocalId({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'localId', caseSensitive: caseSensitive); }); @@ -3256,8 +3503,9 @@ extension AssetQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctByRemoteId( - {bool caseSensitive = true}) { + QueryBuilder distinctByRemoteId({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'remoteId', caseSensitive: caseSensitive); }); @@ -3269,23 +3517,28 @@ extension AssetQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctByStackId( - {bool caseSensitive = true}) { + QueryBuilder distinctByStackId({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'stackId', caseSensitive: caseSensitive); }); } - QueryBuilder distinctByStackPrimaryAssetId( - {bool caseSensitive = true}) { + QueryBuilder distinctByStackPrimaryAssetId({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(r'stackPrimaryAssetId', - caseSensitive: caseSensitive); + return query.addDistinctBy( + r'stackPrimaryAssetId', + caseSensitive: caseSensitive, + ); }); } - QueryBuilder distinctByThumbhash( - {bool caseSensitive = true}) { + QueryBuilder distinctByThumbhash({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'thumbhash', caseSensitive: caseSensitive); }); @@ -3444,7 +3697,7 @@ extension AssetQueryProperty on QueryBuilder { } QueryBuilder - visibilityProperty() { + visibilityProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'visibility'); }); diff --git a/mobile/lib/entities/backup_album.entity.dart b/mobile/lib/entities/backup_album.entity.dart index 4d4d7b3aa3..ad2a5d6718 100644 --- a/mobile/lib/entities/backup_album.entity.dart +++ b/mobile/lib/entities/backup_album.entity.dart @@ -13,10 +13,10 @@ class BackupAlbum { BackupAlbum(this.id, this.lastBackup, this.selection); Id get isarId => fastHash(id); + + BackupAlbum copyWith({String? id, DateTime? lastBackup, BackupSelection? selection}) { + return BackupAlbum(id ?? this.id, lastBackup ?? this.lastBackup, selection ?? this.selection); + } } -enum BackupSelection { - none, - select, - exclude; -} +enum BackupSelection { none, select, exclude } diff --git a/mobile/lib/entities/backup_album.entity.g.dart b/mobile/lib/entities/backup_album.entity.g.dart index 23d00e43ca..ed98503119 100644 --- a/mobile/lib/entities/backup_album.entity.g.dart +++ b/mobile/lib/entities/backup_album.entity.g.dart @@ -17,11 +17,7 @@ const BackupAlbumSchema = CollectionSchema( name: r'BackupAlbum', id: 8308487201128361847, properties: { - r'id': PropertySchema( - id: 0, - name: r'id', - type: IsarType.string, - ), + r'id': PropertySchema(id: 0, name: r'id', type: IsarType.string), r'lastBackup': PropertySchema( id: 1, name: r'lastBackup', @@ -32,8 +28,9 @@ const BackupAlbumSchema = CollectionSchema( name: r'selection', type: IsarType.byte, enumMap: _BackupAlbumselectionEnumValueMap, - ) + ), }, + estimateSize: _backupAlbumEstimateSize, serialize: _backupAlbumSerialize, deserialize: _backupAlbumDeserialize, @@ -42,6 +39,7 @@ const BackupAlbumSchema = CollectionSchema( indexes: {}, links: {}, embeddedSchemas: {}, + getId: _backupAlbumGetId, getLinks: _backupAlbumGetLinks, attach: _backupAlbumAttach, @@ -96,9 +94,11 @@ P _backupAlbumDeserializeProp

( case 1: return (reader.readDateTime(offset)) as P; case 2: - return (_BackupAlbumselectionValueEnumMap[ - reader.readByteOrNull(offset)] ?? - BackupSelection.none) as P; + return (_BackupAlbumselectionValueEnumMap[reader.readByteOrNull( + offset, + )] ?? + BackupSelection.none) + as P; default: throw IsarError('Unknown property with id $propertyId'); } @@ -124,7 +124,10 @@ List> _backupAlbumGetLinks(BackupAlbum object) { } void _backupAlbumAttach( - IsarCollection col, Id id, BackupAlbum object) {} + IsarCollection col, + Id id, + BackupAlbum object, +) {} extension BackupAlbumQueryWhereSort on QueryBuilder { @@ -138,17 +141,18 @@ extension BackupAlbumQueryWhereSort extension BackupAlbumQueryWhere on QueryBuilder { QueryBuilder isarIdEqualTo( - Id isarId) { + Id isarId, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: isarId, - upper: isarId, - )); + return query.addWhereClause( + IdWhereClause.between(lower: isarId, upper: isarId), + ); }); } QueryBuilder isarIdNotEqualTo( - Id isarId) { + Id isarId, + ) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query @@ -171,8 +175,9 @@ extension BackupAlbumQueryWhere } QueryBuilder isarIdGreaterThan( - Id isarId, - {bool include = false}) { + Id isarId, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: isarId, includeLower: include), @@ -181,8 +186,9 @@ extension BackupAlbumQueryWhere } QueryBuilder isarIdLessThan( - Id isarId, - {bool include = false}) { + Id isarId, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: isarId, includeUpper: include), @@ -197,12 +203,14 @@ extension BackupAlbumQueryWhere bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerIsarId, - includeLower: includeLower, - upper: upperIsarId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerIsarId, + includeLower: includeLower, + upper: upperIsarId, + includeUpper: includeUpper, + ), + ); }); } } @@ -214,11 +222,13 @@ extension BackupAlbumQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -228,12 +238,14 @@ extension BackupAlbumQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -243,12 +255,14 @@ extension BackupAlbumQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -260,14 +274,16 @@ extension BackupAlbumQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -276,11 +292,13 @@ extension BackupAlbumQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -289,77 +307,82 @@ extension BackupAlbumQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder idContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder idMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'id', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'id', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder idIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: ''), + ); }); } QueryBuilder idIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'id', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'id', value: ''), + ); }); } QueryBuilder isarIdEqualTo( - Id value) { + Id value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isarId', value: value), + ); }); } QueryBuilder - isarIdGreaterThan( - Id value, { - bool include = false, - }) { + isarIdGreaterThan(Id value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'isarId', + value: value, + ), + ); }); } @@ -368,11 +391,13 @@ extension BackupAlbumQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'isarId', + value: value, + ), + ); }); } @@ -383,125 +408,125 @@ extension BackupAlbumQueryFilter bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'isarId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'isarId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder - lastBackupEqualTo(DateTime value) { + lastBackupEqualTo(DateTime value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'lastBackup', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'lastBackup', value: value), + ); }); } QueryBuilder - lastBackupGreaterThan( - DateTime value, { - bool include = false, - }) { + lastBackupGreaterThan(DateTime value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'lastBackup', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'lastBackup', + value: value, + ), + ); }); } QueryBuilder - lastBackupLessThan( - DateTime value, { - bool include = false, - }) { + lastBackupLessThan(DateTime value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'lastBackup', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'lastBackup', + value: value, + ), + ); }); } QueryBuilder - lastBackupBetween( + lastBackupBetween( DateTime lower, DateTime upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'lastBackup', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'lastBackup', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder - selectionEqualTo(BackupSelection value) { + selectionEqualTo(BackupSelection value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'selection', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'selection', value: value), + ); }); } QueryBuilder - selectionGreaterThan( - BackupSelection value, { - bool include = false, - }) { + selectionGreaterThan(BackupSelection value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'selection', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'selection', + value: value, + ), + ); }); } QueryBuilder - selectionLessThan( - BackupSelection value, { - bool include = false, - }) { + selectionLessThan(BackupSelection value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'selection', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'selection', + value: value, + ), + ); }); } QueryBuilder - selectionBetween( + selectionBetween( BackupSelection lower, BackupSelection upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'selection', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'selection', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } } @@ -604,8 +629,9 @@ extension BackupAlbumQuerySortThenBy extension BackupAlbumQueryWhereDistinct on QueryBuilder { - QueryBuilder distinctById( - {bool caseSensitive = true}) { + QueryBuilder distinctById({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'id', caseSensitive: caseSensitive); }); @@ -645,7 +671,7 @@ extension BackupAlbumQueryProperty } QueryBuilder - selectionProperty() { + selectionProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'selection'); }); diff --git a/mobile/lib/entities/duplicated_asset.entity.g.dart b/mobile/lib/entities/duplicated_asset.entity.g.dart index 8965d47c97..6cf08ad9cc 100644 --- a/mobile/lib/entities/duplicated_asset.entity.g.dart +++ b/mobile/lib/entities/duplicated_asset.entity.g.dart @@ -17,12 +17,9 @@ const DuplicatedAssetSchema = CollectionSchema( name: r'DuplicatedAsset', id: -2679334728174694496, properties: { - r'id': PropertySchema( - id: 0, - name: r'id', - type: IsarType.string, - ) + r'id': PropertySchema(id: 0, name: r'id', type: IsarType.string), }, + estimateSize: _duplicatedAssetEstimateSize, serialize: _duplicatedAssetSerialize, deserialize: _duplicatedAssetDeserialize, @@ -31,6 +28,7 @@ const DuplicatedAssetSchema = CollectionSchema( indexes: {}, links: {}, embeddedSchemas: {}, + getId: _duplicatedAssetGetId, getLinks: _duplicatedAssetGetLinks, attach: _duplicatedAssetAttach, @@ -62,9 +60,7 @@ DuplicatedAsset _duplicatedAssetDeserialize( List offsets, Map> allOffsets, ) { - final object = DuplicatedAsset( - reader.readString(offsets[0]), - ); + final object = DuplicatedAsset(reader.readString(offsets[0])); return object; } @@ -91,7 +87,10 @@ List> _duplicatedAssetGetLinks(DuplicatedAsset object) { } void _duplicatedAssetAttach( - IsarCollection col, Id id, DuplicatedAsset object) {} + IsarCollection col, + Id id, + DuplicatedAsset object, +) {} extension DuplicatedAssetQueryWhereSort on QueryBuilder { @@ -105,17 +104,16 @@ extension DuplicatedAssetQueryWhereSort extension DuplicatedAssetQueryWhere on QueryBuilder { QueryBuilder - isarIdEqualTo(Id isarId) { + isarIdEqualTo(Id isarId) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: isarId, - upper: isarId, - )); + return query.addWhereClause( + IdWhereClause.between(lower: isarId, upper: isarId), + ); }); } QueryBuilder - isarIdNotEqualTo(Id isarId) { + isarIdNotEqualTo(Id isarId) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query @@ -138,7 +136,7 @@ extension DuplicatedAssetQueryWhere } QueryBuilder - isarIdGreaterThan(Id isarId, {bool include = false}) { + isarIdGreaterThan(Id isarId, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: isarId, includeLower: include), @@ -147,7 +145,7 @@ extension DuplicatedAssetQueryWhere } QueryBuilder - isarIdLessThan(Id isarId, {bool include = false}) { + isarIdLessThan(Id isarId, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: isarId, includeUpper: include), @@ -156,19 +154,21 @@ extension DuplicatedAssetQueryWhere } QueryBuilder - isarIdBetween( + isarIdBetween( Id lowerIsarId, Id upperIsarId, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerIsarId, - includeLower: includeLower, - upper: upperIsarId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerIsarId, + includeLower: includeLower, + upper: upperIsarId, + includeUpper: includeUpper, + ), + ); }); } } @@ -176,53 +176,52 @@ extension DuplicatedAssetQueryWhere extension DuplicatedAssetQueryFilter on QueryBuilder { QueryBuilder - idEqualTo( - String value, { - bool caseSensitive = true, - }) { + idEqualTo(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idGreaterThan( + idGreaterThan( String value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idLessThan( - String value, { - bool include = false, - bool caseSensitive = true, - }) { + idLessThan(String value, {bool include = false, bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idBetween( + idBetween( String lower, String upper, { bool includeLower = true, @@ -230,140 +229,141 @@ extension DuplicatedAssetQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idStartsWith( - String value, { - bool caseSensitive = true, - }) { + idStartsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idEndsWith( - String value, { - bool caseSensitive = true, - }) { + idEndsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idContains(String value, {bool caseSensitive = true}) { + idContains(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idMatches(String pattern, {bool caseSensitive = true}) { + idMatches(String pattern, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'id', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'id', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idIsEmpty() { + idIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: ''), + ); }); } QueryBuilder - idIsNotEmpty() { + idIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'id', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'id', value: ''), + ); }); } QueryBuilder - isarIdEqualTo(Id value) { + isarIdEqualTo(Id value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isarId', value: value), + ); }); } QueryBuilder - isarIdGreaterThan( - Id value, { - bool include = false, - }) { + isarIdGreaterThan(Id value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'isarId', + value: value, + ), + ); }); } QueryBuilder - isarIdLessThan( - Id value, { - bool include = false, - }) { + isarIdLessThan(Id value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'isarId', + value: value, + ), + ); }); } QueryBuilder - isarIdBetween( + isarIdBetween( Id lower, Id upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'isarId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'isarId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } } @@ -410,7 +410,7 @@ extension DuplicatedAssetQuerySortThenBy } QueryBuilder - thenByIsarIdDesc() { + thenByIsarIdDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'isarId', Sort.desc); }); @@ -419,8 +419,9 @@ extension DuplicatedAssetQuerySortThenBy extension DuplicatedAssetQueryWhereDistinct on QueryBuilder { - QueryBuilder distinctById( - {bool caseSensitive = true}) { + QueryBuilder distinctById({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'id', caseSensitive: caseSensitive); }); diff --git a/mobile/lib/entities/etag.entity.g.dart b/mobile/lib/entities/etag.entity.g.dart index afabca4aea..b1abba6bb7 100644 --- a/mobile/lib/entities/etag.entity.g.dart +++ b/mobile/lib/entities/etag.entity.g.dart @@ -22,17 +22,10 @@ const ETagSchema = CollectionSchema( name: r'assetCount', type: IsarType.long, ), - r'id': PropertySchema( - id: 1, - name: r'id', - type: IsarType.string, - ), - r'time': PropertySchema( - id: 2, - name: r'time', - type: IsarType.dateTime, - ) + r'id': PropertySchema(id: 1, name: r'id', type: IsarType.string), + r'time': PropertySchema(id: 2, name: r'time', type: IsarType.dateTime), }, + estimateSize: _eTagEstimateSize, serialize: _eTagSerialize, deserialize: _eTagDeserialize, @@ -49,12 +42,13 @@ const ETagSchema = CollectionSchema( name: r'id', type: IndexType.hash, caseSensitive: true, - ) + ), ], - ) + ), }, links: {}, embeddedSchemas: {}, + getId: _eTagGetId, getLinks: _eTagGetLinks, attach: _eTagAttach, @@ -189,10 +183,9 @@ extension ETagQueryWhereSort on QueryBuilder { extension ETagQueryWhere on QueryBuilder { QueryBuilder isarIdEqualTo(Id isarId) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: isarId, - upper: isarId, - )); + return query.addWhereClause( + IdWhereClause.between(lower: isarId, upper: isarId), + ); }); } @@ -218,8 +211,10 @@ extension ETagQueryWhere on QueryBuilder { }); } - QueryBuilder isarIdGreaterThan(Id isarId, - {bool include = false}) { + QueryBuilder isarIdGreaterThan( + Id isarId, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: isarId, includeLower: include), @@ -227,8 +222,10 @@ extension ETagQueryWhere on QueryBuilder { }); } - QueryBuilder isarIdLessThan(Id isarId, - {bool include = false}) { + QueryBuilder isarIdLessThan( + Id isarId, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: isarId, includeUpper: include), @@ -243,21 +240,22 @@ extension ETagQueryWhere on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerIsarId, - includeLower: includeLower, - upper: upperIsarId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerIsarId, + includeLower: includeLower, + upper: upperIsarId, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder idEqualTo(String id) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'id', - value: [id], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'id', value: [id]), + ); }); } @@ -265,32 +263,40 @@ extension ETagQueryWhere on QueryBuilder { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [], - upper: [id], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [id], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [], + upper: [id], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [id], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [id], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [], - upper: [id], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [id], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [], + upper: [id], + includeUpper: false, + ), + ); } }); } @@ -299,27 +305,27 @@ extension ETagQueryWhere on QueryBuilder { extension ETagQueryFilter on QueryBuilder { QueryBuilder assetCountIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'assetCount', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'assetCount'), + ); }); } QueryBuilder assetCountIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'assetCount', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'assetCount'), + ); }); } QueryBuilder assetCountEqualTo( - int? value) { + int? value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'assetCount', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'assetCount', value: value), + ); }); } @@ -328,11 +334,13 @@ extension ETagQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'assetCount', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'assetCount', + value: value, + ), + ); }); } @@ -341,11 +349,13 @@ extension ETagQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'assetCount', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'assetCount', + value: value, + ), + ); }); } @@ -356,13 +366,15 @@ extension ETagQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'assetCount', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'assetCount', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } @@ -371,11 +383,13 @@ extension ETagQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -385,12 +399,14 @@ extension ETagQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -400,12 +416,14 @@ extension ETagQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -417,14 +435,16 @@ extension ETagQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -433,11 +453,13 @@ extension ETagQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -446,60 +468,67 @@ extension ETagQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder idContains(String value, - {bool caseSensitive = true}) { + QueryBuilder idContains( + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder idMatches(String pattern, - {bool caseSensitive = true}) { + QueryBuilder idMatches( + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'id', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'id', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder idIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: ''), + ); }); } QueryBuilder idIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'id', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'id', value: ''), + ); }); } QueryBuilder isarIdEqualTo(Id value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isarId', value: value), + ); }); } @@ -508,11 +537,13 @@ extension ETagQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'isarId', + value: value, + ), + ); }); } @@ -521,11 +552,13 @@ extension ETagQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'isarId', + value: value, + ), + ); }); } @@ -536,38 +569,39 @@ extension ETagQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'isarId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'isarId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder timeIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'time', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'time'), + ); }); } QueryBuilder timeIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'time', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'time'), + ); }); } QueryBuilder timeEqualTo(DateTime? value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'time', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'time', value: value), + ); }); } @@ -576,11 +610,13 @@ extension ETagQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'time', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'time', + value: value, + ), + ); }); } @@ -589,11 +625,13 @@ extension ETagQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'time', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'time', + value: value, + ), + ); }); } @@ -604,13 +642,15 @@ extension ETagQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'time', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'time', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } } @@ -714,8 +754,9 @@ extension ETagQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctById( - {bool caseSensitive = true}) { + QueryBuilder distinctById({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'id', caseSensitive: caseSensitive); }); diff --git a/mobile/lib/entities/ios_device_asset.entity.g.dart b/mobile/lib/entities/ios_device_asset.entity.g.dart index ffed338c91..8d8fec945b 100644 --- a/mobile/lib/entities/ios_device_asset.entity.g.dart +++ b/mobile/lib/entities/ios_device_asset.entity.g.dart @@ -17,17 +17,10 @@ const IOSDeviceAssetSchema = CollectionSchema( name: r'IOSDeviceAsset', id: -1671546753821948030, properties: { - r'hash': PropertySchema( - id: 0, - name: r'hash', - type: IsarType.byteList, - ), - r'id': PropertySchema( - id: 1, - name: r'id', - type: IsarType.string, - ) + r'hash': PropertySchema(id: 0, name: r'hash', type: IsarType.byteList), + r'id': PropertySchema(id: 1, name: r'id', type: IsarType.string), }, + estimateSize: _iOSDeviceAssetEstimateSize, serialize: _iOSDeviceAssetSerialize, deserialize: _iOSDeviceAssetDeserialize, @@ -44,7 +37,7 @@ const IOSDeviceAssetSchema = CollectionSchema( name: r'id', type: IndexType.hash, caseSensitive: true, - ) + ), ], ), r'hash': IndexSchema( @@ -57,12 +50,13 @@ const IOSDeviceAssetSchema = CollectionSchema( name: r'hash', type: IndexType.hash, caseSensitive: false, - ) + ), ], - ) + ), }, links: {}, embeddedSchemas: {}, + getId: _iOSDeviceAssetGetId, getLinks: _iOSDeviceAssetGetLinks, attach: _iOSDeviceAssetAttach, @@ -128,7 +122,10 @@ List> _iOSDeviceAssetGetLinks(IOSDeviceAsset object) { } void _iOSDeviceAssetAttach( - IsarCollection col, Id id, IOSDeviceAsset object) {} + IsarCollection col, + Id id, + IOSDeviceAsset object, +) {} extension IOSDeviceAssetByIndex on IsarCollection { Future getById(String id) { @@ -179,8 +176,10 @@ extension IOSDeviceAssetByIndex on IsarCollection { return putAllByIndex(r'id', objects); } - List putAllByIdSync(List objects, - {bool saveLinks = true}) { + List putAllByIdSync( + List objects, { + bool saveLinks = true, + }) { return putAllByIndexSync(r'id', objects, saveLinks: saveLinks); } } @@ -197,17 +196,17 @@ extension IOSDeviceAssetQueryWhereSort extension IOSDeviceAssetQueryWhere on QueryBuilder { QueryBuilder isarIdEqualTo( - Id isarId) { + Id isarId, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: isarId, - upper: isarId, - )); + return query.addWhereClause( + IdWhereClause.between(lower: isarId, upper: isarId), + ); }); } QueryBuilder - isarIdNotEqualTo(Id isarId) { + isarIdNotEqualTo(Id isarId) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query @@ -230,7 +229,7 @@ extension IOSDeviceAssetQueryWhere } QueryBuilder - isarIdGreaterThan(Id isarId, {bool include = false}) { + isarIdGreaterThan(Id isarId, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: isarId, includeLower: include), @@ -239,7 +238,7 @@ extension IOSDeviceAssetQueryWhere } QueryBuilder - isarIdLessThan(Id isarId, {bool include = false}) { + isarIdLessThan(Id isarId, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: isarId, includeUpper: include), @@ -254,101 +253,120 @@ extension IOSDeviceAssetQueryWhere bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerIsarId, - includeLower: includeLower, - upper: upperIsarId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerIsarId, + includeLower: includeLower, + upper: upperIsarId, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder idEqualTo( - String id) { + String id, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'id', - value: [id], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'id', value: [id]), + ); }); } QueryBuilder idNotEqualTo( - String id) { + String id, + ) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [], - upper: [id], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [id], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [], + upper: [id], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [id], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [id], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [], - upper: [id], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [id], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [], + upper: [id], + includeUpper: false, + ), + ); } }); } QueryBuilder hashEqualTo( - List hash) { + List hash, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'hash', - value: [hash], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'hash', value: [hash]), + ); }); } QueryBuilder - hashNotEqualTo(List hash) { + hashNotEqualTo(List hash) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [], - upper: [hash], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [hash], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [], + upper: [hash], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [hash], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [hash], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [], - upper: [hash], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [hash], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [], + upper: [hash], + includeUpper: false, + ), + ); } }); } @@ -357,134 +375,97 @@ extension IOSDeviceAssetQueryWhere extension IOSDeviceAssetQueryFilter on QueryBuilder { QueryBuilder - hashElementEqualTo(int value) { + hashElementEqualTo(int value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'hash', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'hash', value: value), + ); }); } QueryBuilder - hashElementGreaterThan( - int value, { - bool include = false, - }) { + hashElementGreaterThan(int value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'hash', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'hash', + value: value, + ), + ); }); } QueryBuilder - hashElementLessThan( - int value, { - bool include = false, - }) { + hashElementLessThan(int value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'hash', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'hash', + value: value, + ), + ); }); } QueryBuilder - hashElementBetween( + hashElementBetween( int lower, int upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'hash', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); - }); - } - - QueryBuilder - hashLengthEqualTo(int length) { - return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - length, - true, - length, - true, + return query.addFilterCondition( + FilterCondition.between( + property: r'hash', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), ); }); } QueryBuilder - hashIsEmpty() { + hashLengthEqualTo(int length) { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - 0, - true, - 0, - true, - ); + return query.listLength(r'hash', length, true, length, true); }); } QueryBuilder - hashIsNotEmpty() { + hashIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - 0, - false, - 999999, - true, - ); + return query.listLength(r'hash', 0, true, 0, true); }); } QueryBuilder - hashLengthLessThan( - int length, { - bool include = false, - }) { + hashIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - 0, - true, - length, - include, - ); + return query.listLength(r'hash', 0, false, 999999, true); }); } QueryBuilder - hashLengthGreaterThan( - int length, { - bool include = false, - }) { + hashLengthLessThan(int length, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - length, - include, - 999999, - true, - ); + return query.listLength(r'hash', 0, true, length, include); }); } QueryBuilder - hashLengthBetween( + hashLengthGreaterThan(int length, {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.listLength(r'hash', length, include, 999999, true); + }); + } + + QueryBuilder + hashLengthBetween( int lower, int upper, { bool includeLower = true, @@ -506,43 +487,45 @@ extension IOSDeviceAssetQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idGreaterThan( + idGreaterThan( String value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idLessThan( - String value, { - bool include = false, - bool caseSensitive = true, - }) { + idLessThan(String value, {bool include = false, bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -554,141 +537,143 @@ extension IOSDeviceAssetQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idStartsWith( - String value, { - bool caseSensitive = true, - }) { + idStartsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idEndsWith( - String value, { - bool caseSensitive = true, - }) { + idEndsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - idContains(String value, {bool caseSensitive = true}) { + idContains(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder idMatches( - String pattern, - {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'id', - wildcard: pattern, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - idIsEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: '', - )); - }); - } - - QueryBuilder - idIsNotEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'id', - value: '', - )); - }); - } - - QueryBuilder - isarIdEqualTo(Id value) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isarId', - value: value, - )); - }); - } - - QueryBuilder - isarIdGreaterThan( - Id value, { - bool include = false, + String pattern, { + bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'id', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - isarIdLessThan( - Id value, { - bool include = false, - }) { + idIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: ''), + ); }); } QueryBuilder - isarIdBetween( + idIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'id', value: ''), + ); + }); + } + + QueryBuilder + isarIdEqualTo(Id value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isarId', value: value), + ); + }); + } + + QueryBuilder + isarIdGreaterThan(Id value, {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'isarId', + value: value, + ), + ); + }); + } + + QueryBuilder + isarIdLessThan(Id value, {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'isarId', + value: value, + ), + ); + }); + } + + QueryBuilder + isarIdBetween( Id lower, Id upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'isarId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'isarId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } } @@ -735,7 +720,7 @@ extension IOSDeviceAssetQuerySortThenBy } QueryBuilder - thenByIsarIdDesc() { + thenByIsarIdDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'isarId', Sort.desc); }); @@ -750,8 +735,9 @@ extension IOSDeviceAssetQueryWhereDistinct }); } - QueryBuilder distinctById( - {bool caseSensitive = true}) { + QueryBuilder distinctById({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'id', caseSensitive: caseSensitive); }); diff --git a/mobile/lib/entities/store.entity.dart b/mobile/lib/entities/store.entity.dart index c937697149..7b59e119d6 100644 --- a/mobile/lib/entities/store.entity.dart +++ b/mobile/lib/entities/store.entity.dart @@ -13,11 +13,11 @@ class SSLClientCertStoreVal { const SSLClientCertStoreVal(this.data, this.password); - void save() { + Future save() async { final b64Str = base64Encode(data); - Store.put(StoreKey.sslClientCertData, b64Str); + await Store.put(StoreKey.sslClientCertData, b64Str); if (password != null) { - Store.put(StoreKey.sslClientPasswd, password!); + await Store.put(StoreKey.sslClientPasswd, password!); } } @@ -31,8 +31,8 @@ class SSLClientCertStoreVal { return SSLClientCertStoreVal(certData, passwd); } - static void delete() { - Store.delete(StoreKey.sslClientCertData); - Store.delete(StoreKey.sslClientPasswd); + static Future delete() async { + await Store.delete(StoreKey.sslClientCertData); + await Store.delete(StoreKey.sslClientPasswd); } } diff --git a/mobile/lib/extensions/asset_extensions.dart b/mobile/lib/extensions/asset_extensions.dart index a5fa50983a..22d5d5030a 100644 --- a/mobile/lib/extensions/asset_extensions.dart +++ b/mobile/lib/extensions/asset_extensions.dart @@ -15,16 +15,10 @@ extension TZExtension on Asset { final location = getLocation(exifInfo!.timeZone!); dt = TZDateTime.from(dt, location); } on LocationNotFoundException { - RegExp re = RegExp( - r'^utc(?:([+-]\d{1,2})(?::(\d{2}))?)?$', - caseSensitive: false, - ); + RegExp re = RegExp(r'^utc(?:([+-]\d{1,2})(?::(\d{2}))?)?$', caseSensitive: false); final m = re.firstMatch(exifInfo!.timeZone!); if (m != null) { - final duration = Duration( - hours: int.parse(m.group(1) ?? '0'), - minutes: int.parse(m.group(2) ?? '0'), - ); + final duration = Duration(hours: int.parse(m.group(1) ?? '0'), minutes: int.parse(m.group(2) ?? '0')); dt = dt.add(duration); return (dt, duration); } diff --git a/mobile/lib/extensions/asyncvalue_extensions.dart b/mobile/lib/extensions/asyncvalue_extensions.dart index 554c3a8a8a..c76d7e48d8 100644 --- a/mobile/lib/extensions/asyncvalue_extensions.dart +++ b/mobile/lib/extensions/asyncvalue_extensions.dart @@ -22,15 +22,13 @@ extension LogOnError on AsyncValue { } if (!skip) { - return onLoading?.call() ?? - const Center(child: ImmichLoadingIndicator()); + return onLoading?.call() ?? const Center(child: ImmichLoadingIndicator()); } } if (hasError && !hasValue) { _asyncErrorLogger.severe('Could not load value', error, stackTrace); - return onError?.call(error, stackTrace) ?? - ScaffoldErrorBody(errorMsg: error?.toString()); + return onError?.call(error, stackTrace) ?? ScaffoldErrorBody(errorMsg: error?.toString()); } return onData(requireValue); diff --git a/mobile/lib/extensions/build_context_extensions.dart b/mobile/lib/extensions/build_context_extensions.dart index 69a9c3b347..a624337954 100644 --- a/mobile/lib/extensions/build_context_extensions.dart +++ b/mobile/lib/extensions/build_context_extensions.dart @@ -33,6 +33,10 @@ extension ContextHelper on BuildContext { // Returns the current Primary color of the Theme Color get primaryColor => themeData.colorScheme.primary; + Color get logoYellow => const Color.fromARGB(255, 255, 184, 0); + Color get logoRed => const Color.fromARGB(255, 230, 65, 30); + Color get logoPink => const Color.fromARGB(255, 222, 127, 179); + Color get logoGreen => const Color.fromARGB(255, 49, 164, 82); // Returns the Scaffold background color of the Theme Color get scaffoldBackgroundColor => colorScheme.surface; @@ -56,6 +60,5 @@ extension ContextHelper on BuildContext { FocusScopeNode get focusScope => FocusScope.of(this); // Show SnackBars from the current context - void showSnackBar(SnackBar snackBar) => - ScaffoldMessenger.of(this).showSnackBar(snackBar); + void showSnackBar(SnackBar snackBar) => ScaffoldMessenger.of(this).showSnackBar(snackBar); } diff --git a/mobile/lib/extensions/collection_extensions.dart b/mobile/lib/extensions/collection_extensions.dart index 95d2f74df8..541db7ccaf 100644 --- a/mobile/lib/extensions/collection_extensions.dart +++ b/mobile/lib/extensions/collection_extensions.dart @@ -6,10 +6,7 @@ import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/utils/hash.dart'; extension ListExtension on List { - List uniqueConsecutive({ - int Function(E a, E b)? compare, - void Function(E a, E b)? onDuplicate, - }) { + List uniqueConsecutive({int Function(E a, E b)? compare, void Function(E a, E b)? onDuplicate}) { compare ??= (E a, E b) => a == b ? 0 : 1; int i = 1, j = 1; for (; i < length; i++) { @@ -45,9 +42,7 @@ extension IntListExtension on Iterable { extension AssetListExtension on Iterable { /// Returns the assets that are already available in the Immich server - Iterable remoteOnly({ - void Function()? errorCallback, - }) { + Iterable remoteOnly({void Function()? errorCallback}) { final bool onlyRemote = every((e) => e.isRemote); if (!onlyRemote) { if (errorCallback != null) errorCallback(); @@ -58,10 +53,7 @@ extension AssetListExtension on Iterable { /// Returns the assets that are owned by the user passed to the [owner] param /// If [owner] is null, an empty list is returned - Iterable ownedOnly( - UserDto? owner, { - void Function()? errorCallback, - }) { + Iterable ownedOnly(UserDto? owner, {void Function()? errorCallback}) { if (owner == null) return []; final isarUserId = fastHash(owner.id); final bool onlyOwned = every((e) => e.ownerId == isarUserId); diff --git a/mobile/lib/extensions/datetime_extensions.dart b/mobile/lib/extensions/datetime_extensions.dart index e23bf5210f..0bc95565a6 100644 --- a/mobile/lib/extensions/datetime_extensions.dart +++ b/mobile/lib/extensions/datetime_extensions.dart @@ -47,19 +47,13 @@ extension DateRangeFormatting on DateTime { /// - Date range of this year: "Mar 23-May 31" /// - Date range of other year: "Aug 28 - Sep 30, 2023" /// - Date range over multiple years: "Apr 17, 2021 - Apr 9, 2022" - static String formatDateRange( - DateTime startDate, - DateTime endDate, - Locale? locale, - ) { + static String formatDateRange(DateTime startDate, DateTime endDate, Locale? locale) { final now = DateTime.now(); final currentYear = now.year; final localeString = locale?.toString() ?? 'en_US'; // Check if it's a single date (same day) - if (startDate.year == endDate.year && - startDate.month == endDate.month && - startDate.day == endDate.day) { + if (startDate.year == endDate.year && startDate.month == endDate.month && startDate.day == endDate.day) { if (startDate.year == currentYear) { // Single date of this year: "Aug 28" return DateFormat.MMMd(localeString).format(startDate); diff --git a/mobile/lib/extensions/maplibrecontroller_extensions.dart b/mobile/lib/extensions/maplibrecontroller_extensions.dart index 42d5e2c1d0..e1f32a4d8c 100644 --- a/mobile/lib/extensions/maplibrecontroller_extensions.dart +++ b/mobile/lib/extensions/maplibrecontroller_extensions.dart @@ -13,9 +13,7 @@ extension MapMarkers on MapLibreMapController { Future addGeoJSONSourceForMarkers(List markers) async { return addSource( MapUtils.defaultSourceId, - GeojsonSourceProperties( - data: MapUtils.generateGeoJsonForMarkers(markers.toList()), - ), + GeojsonSourceProperties(data: MapUtils.generateGeoJsonForMarkers(markers.toList())), ); } @@ -73,23 +71,13 @@ extension MapMarkers on MapLibreMapController { try { final ByteData bytes = await rootBundle.load("assets/location-pin.png"); await addImage("mapMarker", bytes.buffer.asUint8List()); - return addSymbol( - SymbolOptions( - geometry: centre, - iconImage: "mapMarker", - iconSize: 0.15, - iconAnchor: "bottom", - ), - ); + return addSymbol(SymbolOptions(geometry: centre, iconImage: "mapMarker", iconSize: 0.15, iconAnchor: "bottom")); } finally { // no-op } } - Future getBoundsFromPoint( - Point point, - double distance, - ) async { + Future getBoundsFromPoint(Point point, double distance) async { final southWestPx = Point(point.x - distance, point.y + distance); final northEastPx = Point(point.x + distance, point.y - distance); diff --git a/mobile/lib/extensions/scroll_extensions.dart b/mobile/lib/extensions/scroll_extensions.dart index 2752d0b77a..169032ff5d 100644 --- a/mobile/lib/extensions/scroll_extensions.dart +++ b/mobile/lib/extensions/scroll_extensions.dart @@ -10,11 +10,7 @@ class FastScrollPhysics extends ScrollPhysics { } @override - SpringDescription get spring => const SpringDescription( - mass: 1, - stiffness: 402.49984375, - damping: 40, - ); + SpringDescription get spring => const SpringDescription(mass: 1, stiffness: 402.49984375, damping: 40); } class FastClampingScrollPhysics extends ClampingScrollPhysics { @@ -27,12 +23,12 @@ class FastClampingScrollPhysics extends ClampingScrollPhysics { @override SpringDescription get spring => const SpringDescription( - // When swiping between videos on Android, the placeholder of the first opened video - // can briefly be seen and cause a flicker effect if the video begins to initialize - // before the animation finishes - probably a bug in PhotoViewGallery's animation handling - // Making the animation faster is not just stylistic, but also helps to avoid this flicker - mass: 1, - stiffness: 1601.2499609375, - damping: 80, - ); + // When swiping between videos on Android, the placeholder of the first opened video + // can briefly be seen and cause a flicker effect if the video begins to initialize + // before the animation finishes - probably a bug in PhotoViewGallery's animation handling + // Making the animation faster is not just stylistic, but also helps to avoid this flicker + mass: 1, + stiffness: 1601.2499609375, + damping: 80, + ); } diff --git a/mobile/lib/extensions/string_extensions.dart b/mobile/lib/extensions/string_extensions.dart index 67411013ee..6cd6e1e4b4 100644 --- a/mobile/lib/extensions/string_extensions.dart +++ b/mobile/lib/extensions/string_extensions.dart @@ -1,10 +1,6 @@ extension StringExtension on String { String capitalize() { - return split(" ") - .map( - (str) => str.isEmpty ? str : str[0].toUpperCase() + str.substring(1), - ) - .join(" "); + return split(" ").map((str) => str.isEmpty ? str : str[0].toUpperCase() + str.substring(1)).join(" "); } } @@ -12,9 +8,7 @@ extension DurationExtension on String { /// Parses and returns the string of format HH:MM:SS as a duration object else null Duration? toDuration() { try { - final parts = split(':') - .map((e) => double.parse(e).toInt()) - .toList(growable: false); + final parts = split(':').map((e) => double.parse(e).toInt()).toList(growable: false); return Duration(hours: parts[0], minutes: parts[1], seconds: parts[2]); } catch (e) { return null; diff --git a/mobile/lib/extensions/theme_extensions.dart b/mobile/lib/extensions/theme_extensions.dart index b81e4476e0..332dc58fc0 100644 --- a/mobile/lib/extensions/theme_extensions.dart +++ b/mobile/lib/extensions/theme_extensions.dart @@ -2,23 +2,15 @@ import 'package:flutter/material.dart'; extension ImmichColorSchemeExtensions on ColorScheme { bool get _isDarkMode => brightness == Brightness.dark; - Color get onSurfaceSecondary => _isDarkMode - ? onSurface.darken(amount: .3) - : onSurface.lighten(amount: .3); + Color get onSurfaceSecondary => _isDarkMode ? onSurface.darken(amount: .3) : onSurface.lighten(amount: .3); } extension ColorExtensions on Color { Color lighten({double amount = 0.1}) { - return Color.alphaBlend( - Colors.white.withValues(alpha: amount), - this, - ); + return Color.alphaBlend(Colors.white.withValues(alpha: amount), this); } Color darken({double amount = 0.1}) { - return Color.alphaBlend( - Colors.black.withValues(alpha: amount), - this, - ); + return Color.alphaBlend(Colors.black.withValues(alpha: amount), this); } } diff --git a/mobile/lib/extensions/translate_extensions.dart b/mobile/lib/extensions/translate_extensions.dart index 122830843d..cfd8c8cd1f 100644 --- a/mobile/lib/extensions/translate_extensions.dart +++ b/mobile/lib/extensions/translate_extensions.dart @@ -29,19 +29,14 @@ extension TextTranslateExtension on Text { } } -String _translateHelper( - BuildContext? context, - String key, [ - Map? args, -]) { +String _translateHelper(BuildContext? context, String key, [Map? args]) { if (key.isEmpty) { return ''; } try { final translatedMessage = key.tr(context: context); return args != null - ? MessageFormat(translatedMessage, locale: Intl.defaultLocale ?? 'en') - .format(args) + ? MessageFormat(translatedMessage, locale: Intl.defaultLocale ?? 'en').format(args) : translatedMessage; } catch (e) { debugPrint('Translation failed for key "$key". Error: $e'); diff --git a/mobile/lib/infrastructure/entities/asset_face.entity.dart b/mobile/lib/infrastructure/entities/asset_face.entity.dart new file mode 100644 index 0000000000..5f793030c3 --- /dev/null +++ b/mobile/lib/infrastructure/entities/asset_face.entity.dart @@ -0,0 +1,31 @@ +import 'package:drift/drift.dart'; +import 'package:immich_mobile/infrastructure/entities/person.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart'; +import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; + +class AssetFaceEntity extends Table with DriftDefaultsMixin { + const AssetFaceEntity(); + + TextColumn get id => text()(); + + TextColumn get assetId => text().references(RemoteAssetEntity, #id, onDelete: KeyAction.cascade)(); + + TextColumn get personId => text().nullable().references(PersonEntity, #id, onDelete: KeyAction.setNull)(); + + IntColumn get imageWidth => integer()(); + + IntColumn get imageHeight => integer()(); + + IntColumn get boundingBoxX1 => integer()(); + + IntColumn get boundingBoxY1 => integer()(); + + IntColumn get boundingBoxX2 => integer()(); + + IntColumn get boundingBoxY2 => integer()(); + + TextColumn get sourceType => text()(); + + @override + Set get primaryKey => {id}; +} diff --git a/mobile/lib/infrastructure/entities/asset_face.entity.drift.dart b/mobile/lib/infrastructure/entities/asset_face.entity.drift.dart new file mode 100644 index 0000000000..092fcc5859 --- /dev/null +++ b/mobile/lib/infrastructure/entities/asset_face.entity.drift.dart @@ -0,0 +1,1209 @@ +// dart format width=80 +// ignore_for_file: type=lint +import 'package:drift/drift.dart' as i0; +import 'package:immich_mobile/infrastructure/entities/asset_face.entity.drift.dart' + as i1; +import 'package:immich_mobile/infrastructure/entities/asset_face.entity.dart' + as i2; +import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart' + as i3; +import 'package:drift/internal/modular.dart' as i4; +import 'package:immich_mobile/infrastructure/entities/person.entity.drift.dart' + as i5; + +typedef $$AssetFaceEntityTableCreateCompanionBuilder = + i1.AssetFaceEntityCompanion Function({ + required String id, + required String assetId, + i0.Value personId, + required int imageWidth, + required int imageHeight, + required int boundingBoxX1, + required int boundingBoxY1, + required int boundingBoxX2, + required int boundingBoxY2, + required String sourceType, + }); +typedef $$AssetFaceEntityTableUpdateCompanionBuilder = + i1.AssetFaceEntityCompanion Function({ + i0.Value id, + i0.Value assetId, + i0.Value personId, + i0.Value imageWidth, + i0.Value imageHeight, + i0.Value boundingBoxX1, + i0.Value boundingBoxY1, + i0.Value boundingBoxX2, + i0.Value boundingBoxY2, + i0.Value sourceType, + }); + +final class $$AssetFaceEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$AssetFaceEntityTable, + i1.AssetFaceEntityData + > { + $$AssetFaceEntityTableReferences( + super.$_db, + super.$_table, + super.$_typedResult, + ); + + static i3.$RemoteAssetEntityTable _assetIdTable(i0.GeneratedDatabase db) => + i4.ReadDatabaseContainer(db) + .resultSet('remote_asset_entity') + .createAlias( + i0.$_aliasNameGenerator( + i4.ReadDatabaseContainer(db) + .resultSet('asset_face_entity') + .assetId, + i4.ReadDatabaseContainer( + db, + ).resultSet('remote_asset_entity').id, + ), + ); + + i3.$$RemoteAssetEntityTableProcessedTableManager get assetId { + final $_column = $_itemColumn('asset_id')!; + + final manager = i3 + .$$RemoteAssetEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer( + $_db, + ).resultSet('remote_asset_entity'), + ) + .filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_assetIdTable($_db)); + if (item == null) return manager; + return i0.ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item]), + ); + } + + static i5.$PersonEntityTable _personIdTable(i0.GeneratedDatabase db) => + i4.ReadDatabaseContainer(db) + .resultSet('person_entity') + .createAlias( + i0.$_aliasNameGenerator( + i4.ReadDatabaseContainer(db) + .resultSet('asset_face_entity') + .personId, + i4.ReadDatabaseContainer( + db, + ).resultSet('person_entity').id, + ), + ); + + i5.$$PersonEntityTableProcessedTableManager? get personId { + final $_column = $_itemColumn('person_id'); + if ($_column == null) return null; + final manager = i5 + .$$PersonEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer( + $_db, + ).resultSet('person_entity'), + ) + .filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_personIdTable($_db)); + if (item == null) return manager; + return i0.ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item]), + ); + } +} + +class $$AssetFaceEntityTableFilterComposer + extends i0.Composer { + $$AssetFaceEntityTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.ColumnFilters get id => $composableBuilder( + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get imageWidth => $composableBuilder( + column: $table.imageWidth, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get imageHeight => $composableBuilder( + column: $table.imageHeight, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get boundingBoxX1 => $composableBuilder( + column: $table.boundingBoxX1, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get boundingBoxY1 => $composableBuilder( + column: $table.boundingBoxY1, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get boundingBoxX2 => $composableBuilder( + column: $table.boundingBoxX2, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get boundingBoxY2 => $composableBuilder( + column: $table.boundingBoxY2, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get sourceType => $composableBuilder( + column: $table.sourceType, + builder: (column) => i0.ColumnFilters(column), + ); + + i3.$$RemoteAssetEntityTableFilterComposer get assetId { + final i3.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } + + i5.$$PersonEntityTableFilterComposer get personId { + final i5.$$PersonEntityTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.personId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('person_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$PersonEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('person_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$AssetFaceEntityTableOrderingComposer + extends i0.Composer { + $$AssetFaceEntityTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.ColumnOrderings get id => $composableBuilder( + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get imageWidth => $composableBuilder( + column: $table.imageWidth, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get imageHeight => $composableBuilder( + column: $table.imageHeight, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get boundingBoxX1 => $composableBuilder( + column: $table.boundingBoxX1, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get boundingBoxY1 => $composableBuilder( + column: $table.boundingBoxY1, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get boundingBoxX2 => $composableBuilder( + column: $table.boundingBoxX2, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get boundingBoxY2 => $composableBuilder( + column: $table.boundingBoxY2, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get sourceType => $composableBuilder( + column: $table.sourceType, + builder: (column) => i0.ColumnOrderings(column), + ); + + i3.$$RemoteAssetEntityTableOrderingComposer get assetId { + final i3.$$RemoteAssetEntityTableOrderingComposer composer = + $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } + + i5.$$PersonEntityTableOrderingComposer get personId { + final i5.$$PersonEntityTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.personId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('person_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$PersonEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('person_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$AssetFaceEntityTableAnnotationComposer + extends i0.Composer { + $$AssetFaceEntityTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.GeneratedColumn get id => + $composableBuilder(column: $table.id, builder: (column) => column); + + i0.GeneratedColumn get imageWidth => $composableBuilder( + column: $table.imageWidth, + builder: (column) => column, + ); + + i0.GeneratedColumn get imageHeight => $composableBuilder( + column: $table.imageHeight, + builder: (column) => column, + ); + + i0.GeneratedColumn get boundingBoxX1 => $composableBuilder( + column: $table.boundingBoxX1, + builder: (column) => column, + ); + + i0.GeneratedColumn get boundingBoxY1 => $composableBuilder( + column: $table.boundingBoxY1, + builder: (column) => column, + ); + + i0.GeneratedColumn get boundingBoxX2 => $composableBuilder( + column: $table.boundingBoxX2, + builder: (column) => column, + ); + + i0.GeneratedColumn get boundingBoxY2 => $composableBuilder( + column: $table.boundingBoxY2, + builder: (column) => column, + ); + + i0.GeneratedColumn get sourceType => $composableBuilder( + column: $table.sourceType, + builder: (column) => column, + ); + + i3.$$RemoteAssetEntityTableAnnotationComposer get assetId { + final i3.$$RemoteAssetEntityTableAnnotationComposer composer = + $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } + + i5.$$PersonEntityTableAnnotationComposer get personId { + final i5.$$PersonEntityTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.personId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('person_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$PersonEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('person_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$AssetFaceEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$AssetFaceEntityTable, + i1.AssetFaceEntityData, + i1.$$AssetFaceEntityTableFilterComposer, + i1.$$AssetFaceEntityTableOrderingComposer, + i1.$$AssetFaceEntityTableAnnotationComposer, + $$AssetFaceEntityTableCreateCompanionBuilder, + $$AssetFaceEntityTableUpdateCompanionBuilder, + (i1.AssetFaceEntityData, i1.$$AssetFaceEntityTableReferences), + i1.AssetFaceEntityData, + i0.PrefetchHooks Function({bool assetId, bool personId}) + > { + $$AssetFaceEntityTableTableManager( + i0.GeneratedDatabase db, + i1.$AssetFaceEntityTable table, + ) : super( + i0.TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + i1.$$AssetFaceEntityTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => + i1.$$AssetFaceEntityTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => i1 + .$$AssetFaceEntityTableAnnotationComposer($db: db, $table: table), + updateCompanionCallback: + ({ + i0.Value id = const i0.Value.absent(), + i0.Value assetId = const i0.Value.absent(), + i0.Value personId = const i0.Value.absent(), + i0.Value imageWidth = const i0.Value.absent(), + i0.Value imageHeight = const i0.Value.absent(), + i0.Value boundingBoxX1 = const i0.Value.absent(), + i0.Value boundingBoxY1 = const i0.Value.absent(), + i0.Value boundingBoxX2 = const i0.Value.absent(), + i0.Value boundingBoxY2 = const i0.Value.absent(), + i0.Value sourceType = const i0.Value.absent(), + }) => i1.AssetFaceEntityCompanion( + id: id, + assetId: assetId, + personId: personId, + imageWidth: imageWidth, + imageHeight: imageHeight, + boundingBoxX1: boundingBoxX1, + boundingBoxY1: boundingBoxY1, + boundingBoxX2: boundingBoxX2, + boundingBoxY2: boundingBoxY2, + sourceType: sourceType, + ), + createCompanionCallback: + ({ + required String id, + required String assetId, + i0.Value personId = const i0.Value.absent(), + required int imageWidth, + required int imageHeight, + required int boundingBoxX1, + required int boundingBoxY1, + required int boundingBoxX2, + required int boundingBoxY2, + required String sourceType, + }) => i1.AssetFaceEntityCompanion.insert( + id: id, + assetId: assetId, + personId: personId, + imageWidth: imageWidth, + imageHeight: imageHeight, + boundingBoxX1: boundingBoxX1, + boundingBoxY1: boundingBoxY1, + boundingBoxX2: boundingBoxX2, + boundingBoxY2: boundingBoxY2, + sourceType: sourceType, + ), + withReferenceMapper: (p0) => p0 + .map( + (e) => ( + e.readTable(table), + i1.$$AssetFaceEntityTableReferences(db, table, e), + ), + ) + .toList(), + prefetchHooksCallback: ({assetId = false, personId = false}) { + return i0.PrefetchHooks( + db: db, + explicitlyWatchedTables: [], + addJoins: + < + T extends i0.TableManagerState< + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic + > + >(state) { + if (assetId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.assetId, + referencedTable: i1 + .$$AssetFaceEntityTableReferences + ._assetIdTable(db), + referencedColumn: i1 + .$$AssetFaceEntityTableReferences + ._assetIdTable(db) + .id, + ) + as T; + } + if (personId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.personId, + referencedTable: i1 + .$$AssetFaceEntityTableReferences + ._personIdTable(db), + referencedColumn: i1 + .$$AssetFaceEntityTableReferences + ._personIdTable(db) + .id, + ) + as T; + } + + return state; + }, + getPrefetchedDataCallback: (items) async { + return []; + }, + ); + }, + ), + ); +} + +typedef $$AssetFaceEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$AssetFaceEntityTable, + i1.AssetFaceEntityData, + i1.$$AssetFaceEntityTableFilterComposer, + i1.$$AssetFaceEntityTableOrderingComposer, + i1.$$AssetFaceEntityTableAnnotationComposer, + $$AssetFaceEntityTableCreateCompanionBuilder, + $$AssetFaceEntityTableUpdateCompanionBuilder, + (i1.AssetFaceEntityData, i1.$$AssetFaceEntityTableReferences), + i1.AssetFaceEntityData, + i0.PrefetchHooks Function({bool assetId, bool personId}) + >; + +class $AssetFaceEntityTable extends i2.AssetFaceEntity + with i0.TableInfo<$AssetFaceEntityTable, i1.AssetFaceEntityData> { + @override + final i0.GeneratedDatabase attachedDatabase; + final String? _alias; + $AssetFaceEntityTable(this.attachedDatabase, [this._alias]); + static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); + @override + late final i0.GeneratedColumn id = i0.GeneratedColumn( + 'id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _assetIdMeta = const i0.VerificationMeta( + 'assetId', + ); + @override + late final i0.GeneratedColumn assetId = i0.GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + static const i0.VerificationMeta _personIdMeta = const i0.VerificationMeta( + 'personId', + ); + @override + late final i0.GeneratedColumn personId = i0.GeneratedColumn( + 'person_id', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES person_entity (id) ON DELETE SET NULL', + ), + ); + static const i0.VerificationMeta _imageWidthMeta = const i0.VerificationMeta( + 'imageWidth', + ); + @override + late final i0.GeneratedColumn imageWidth = i0.GeneratedColumn( + 'image_width', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _imageHeightMeta = const i0.VerificationMeta( + 'imageHeight', + ); + @override + late final i0.GeneratedColumn imageHeight = i0.GeneratedColumn( + 'image_height', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _boundingBoxX1Meta = + const i0.VerificationMeta('boundingBoxX1'); + @override + late final i0.GeneratedColumn boundingBoxX1 = i0.GeneratedColumn( + 'bounding_box_x1', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _boundingBoxY1Meta = + const i0.VerificationMeta('boundingBoxY1'); + @override + late final i0.GeneratedColumn boundingBoxY1 = i0.GeneratedColumn( + 'bounding_box_y1', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _boundingBoxX2Meta = + const i0.VerificationMeta('boundingBoxX2'); + @override + late final i0.GeneratedColumn boundingBoxX2 = i0.GeneratedColumn( + 'bounding_box_x2', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _boundingBoxY2Meta = + const i0.VerificationMeta('boundingBoxY2'); + @override + late final i0.GeneratedColumn boundingBoxY2 = i0.GeneratedColumn( + 'bounding_box_y2', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _sourceTypeMeta = const i0.VerificationMeta( + 'sourceType', + ); + @override + late final i0.GeneratedColumn sourceType = i0.GeneratedColumn( + 'source_type', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'asset_face_entity'; + @override + i0.VerificationContext validateIntegrity( + i0.Insertable instance, { + bool isInserting = false, + }) { + final context = i0.VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('id')) { + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); + } else if (isInserting) { + context.missing(_idMeta); + } + if (data.containsKey('asset_id')) { + context.handle( + _assetIdMeta, + assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta), + ); + } else if (isInserting) { + context.missing(_assetIdMeta); + } + if (data.containsKey('person_id')) { + context.handle( + _personIdMeta, + personId.isAcceptableOrUnknown(data['person_id']!, _personIdMeta), + ); + } + if (data.containsKey('image_width')) { + context.handle( + _imageWidthMeta, + imageWidth.isAcceptableOrUnknown(data['image_width']!, _imageWidthMeta), + ); + } else if (isInserting) { + context.missing(_imageWidthMeta); + } + if (data.containsKey('image_height')) { + context.handle( + _imageHeightMeta, + imageHeight.isAcceptableOrUnknown( + data['image_height']!, + _imageHeightMeta, + ), + ); + } else if (isInserting) { + context.missing(_imageHeightMeta); + } + if (data.containsKey('bounding_box_x1')) { + context.handle( + _boundingBoxX1Meta, + boundingBoxX1.isAcceptableOrUnknown( + data['bounding_box_x1']!, + _boundingBoxX1Meta, + ), + ); + } else if (isInserting) { + context.missing(_boundingBoxX1Meta); + } + if (data.containsKey('bounding_box_y1')) { + context.handle( + _boundingBoxY1Meta, + boundingBoxY1.isAcceptableOrUnknown( + data['bounding_box_y1']!, + _boundingBoxY1Meta, + ), + ); + } else if (isInserting) { + context.missing(_boundingBoxY1Meta); + } + if (data.containsKey('bounding_box_x2')) { + context.handle( + _boundingBoxX2Meta, + boundingBoxX2.isAcceptableOrUnknown( + data['bounding_box_x2']!, + _boundingBoxX2Meta, + ), + ); + } else if (isInserting) { + context.missing(_boundingBoxX2Meta); + } + if (data.containsKey('bounding_box_y2')) { + context.handle( + _boundingBoxY2Meta, + boundingBoxY2.isAcceptableOrUnknown( + data['bounding_box_y2']!, + _boundingBoxY2Meta, + ), + ); + } else if (isInserting) { + context.missing(_boundingBoxY2Meta); + } + if (data.containsKey('source_type')) { + context.handle( + _sourceTypeMeta, + sourceType.isAcceptableOrUnknown(data['source_type']!, _sourceTypeMeta), + ); + } else if (isInserting) { + context.missing(_sourceTypeMeta); + } + return context; + } + + @override + Set get $primaryKey => {id}; + @override + i1.AssetFaceEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return i1.AssetFaceEntityData( + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + assetId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + personId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}person_id'], + ), + imageWidth: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}image_width'], + )!, + imageHeight: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}image_height'], + )!, + boundingBoxX1: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}bounding_box_x1'], + )!, + boundingBoxY1: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}bounding_box_y1'], + )!, + boundingBoxX2: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}bounding_box_x2'], + )!, + boundingBoxY2: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}bounding_box_y2'], + )!, + sourceType: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}source_type'], + )!, + ); + } + + @override + $AssetFaceEntityTable createAlias(String alias) { + return $AssetFaceEntityTable(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class AssetFaceEntityData extends i0.DataClass + implements i0.Insertable { + final String id; + final String assetId; + final String? personId; + final int imageWidth; + final int imageHeight; + final int boundingBoxX1; + final int boundingBoxY1; + final int boundingBoxX2; + final int boundingBoxY2; + final String sourceType; + const AssetFaceEntityData({ + required this.id, + required this.assetId, + this.personId, + required this.imageWidth, + required this.imageHeight, + required this.boundingBoxX1, + required this.boundingBoxY1, + required this.boundingBoxX2, + required this.boundingBoxY2, + required this.sourceType, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = i0.Variable(id); + map['asset_id'] = i0.Variable(assetId); + if (!nullToAbsent || personId != null) { + map['person_id'] = i0.Variable(personId); + } + map['image_width'] = i0.Variable(imageWidth); + map['image_height'] = i0.Variable(imageHeight); + map['bounding_box_x1'] = i0.Variable(boundingBoxX1); + map['bounding_box_y1'] = i0.Variable(boundingBoxY1); + map['bounding_box_x2'] = i0.Variable(boundingBoxX2); + map['bounding_box_y2'] = i0.Variable(boundingBoxY2); + map['source_type'] = i0.Variable(sourceType); + return map; + } + + factory AssetFaceEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { + serializer ??= i0.driftRuntimeOptions.defaultSerializer; + return AssetFaceEntityData( + id: serializer.fromJson(json['id']), + assetId: serializer.fromJson(json['assetId']), + personId: serializer.fromJson(json['personId']), + imageWidth: serializer.fromJson(json['imageWidth']), + imageHeight: serializer.fromJson(json['imageHeight']), + boundingBoxX1: serializer.fromJson(json['boundingBoxX1']), + boundingBoxY1: serializer.fromJson(json['boundingBoxY1']), + boundingBoxX2: serializer.fromJson(json['boundingBoxX2']), + boundingBoxY2: serializer.fromJson(json['boundingBoxY2']), + sourceType: serializer.fromJson(json['sourceType']), + ); + } + @override + Map toJson({i0.ValueSerializer? serializer}) { + serializer ??= i0.driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'assetId': serializer.toJson(assetId), + 'personId': serializer.toJson(personId), + 'imageWidth': serializer.toJson(imageWidth), + 'imageHeight': serializer.toJson(imageHeight), + 'boundingBoxX1': serializer.toJson(boundingBoxX1), + 'boundingBoxY1': serializer.toJson(boundingBoxY1), + 'boundingBoxX2': serializer.toJson(boundingBoxX2), + 'boundingBoxY2': serializer.toJson(boundingBoxY2), + 'sourceType': serializer.toJson(sourceType), + }; + } + + i1.AssetFaceEntityData copyWith({ + String? id, + String? assetId, + i0.Value personId = const i0.Value.absent(), + int? imageWidth, + int? imageHeight, + int? boundingBoxX1, + int? boundingBoxY1, + int? boundingBoxX2, + int? boundingBoxY2, + String? sourceType, + }) => i1.AssetFaceEntityData( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId.present ? personId.value : this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + AssetFaceEntityData copyWithCompanion(i1.AssetFaceEntityCompanion data) { + return AssetFaceEntityData( + id: data.id.present ? data.id.value : this.id, + assetId: data.assetId.present ? data.assetId.value : this.assetId, + personId: data.personId.present ? data.personId.value : this.personId, + imageWidth: data.imageWidth.present + ? data.imageWidth.value + : this.imageWidth, + imageHeight: data.imageHeight.present + ? data.imageHeight.value + : this.imageHeight, + boundingBoxX1: data.boundingBoxX1.present + ? data.boundingBoxX1.value + : this.boundingBoxX1, + boundingBoxY1: data.boundingBoxY1.present + ? data.boundingBoxY1.value + : this.boundingBoxY1, + boundingBoxX2: data.boundingBoxX2.present + ? data.boundingBoxX2.value + : this.boundingBoxX2, + boundingBoxY2: data.boundingBoxY2.present + ? data.boundingBoxY2.value + : this.boundingBoxY2, + sourceType: data.sourceType.present + ? data.sourceType.value + : this.sourceType, + ); + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityData(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is i1.AssetFaceEntityData && + other.id == this.id && + other.assetId == this.assetId && + other.personId == this.personId && + other.imageWidth == this.imageWidth && + other.imageHeight == this.imageHeight && + other.boundingBoxX1 == this.boundingBoxX1 && + other.boundingBoxY1 == this.boundingBoxY1 && + other.boundingBoxX2 == this.boundingBoxX2 && + other.boundingBoxY2 == this.boundingBoxY2 && + other.sourceType == this.sourceType); +} + +class AssetFaceEntityCompanion + extends i0.UpdateCompanion { + final i0.Value id; + final i0.Value assetId; + final i0.Value personId; + final i0.Value imageWidth; + final i0.Value imageHeight; + final i0.Value boundingBoxX1; + final i0.Value boundingBoxY1; + final i0.Value boundingBoxX2; + final i0.Value boundingBoxY2; + final i0.Value sourceType; + const AssetFaceEntityCompanion({ + this.id = const i0.Value.absent(), + this.assetId = const i0.Value.absent(), + this.personId = const i0.Value.absent(), + this.imageWidth = const i0.Value.absent(), + this.imageHeight = const i0.Value.absent(), + this.boundingBoxX1 = const i0.Value.absent(), + this.boundingBoxY1 = const i0.Value.absent(), + this.boundingBoxX2 = const i0.Value.absent(), + this.boundingBoxY2 = const i0.Value.absent(), + this.sourceType = const i0.Value.absent(), + }); + AssetFaceEntityCompanion.insert({ + required String id, + required String assetId, + this.personId = const i0.Value.absent(), + required int imageWidth, + required int imageHeight, + required int boundingBoxX1, + required int boundingBoxY1, + required int boundingBoxX2, + required int boundingBoxY2, + required String sourceType, + }) : id = i0.Value(id), + assetId = i0.Value(assetId), + imageWidth = i0.Value(imageWidth), + imageHeight = i0.Value(imageHeight), + boundingBoxX1 = i0.Value(boundingBoxX1), + boundingBoxY1 = i0.Value(boundingBoxY1), + boundingBoxX2 = i0.Value(boundingBoxX2), + boundingBoxY2 = i0.Value(boundingBoxY2), + sourceType = i0.Value(sourceType); + static i0.Insertable custom({ + i0.Expression? id, + i0.Expression? assetId, + i0.Expression? personId, + i0.Expression? imageWidth, + i0.Expression? imageHeight, + i0.Expression? boundingBoxX1, + i0.Expression? boundingBoxY1, + i0.Expression? boundingBoxX2, + i0.Expression? boundingBoxY2, + i0.Expression? sourceType, + }) { + return i0.RawValuesInsertable({ + if (id != null) 'id': id, + if (assetId != null) 'asset_id': assetId, + if (personId != null) 'person_id': personId, + if (imageWidth != null) 'image_width': imageWidth, + if (imageHeight != null) 'image_height': imageHeight, + if (boundingBoxX1 != null) 'bounding_box_x1': boundingBoxX1, + if (boundingBoxY1 != null) 'bounding_box_y1': boundingBoxY1, + if (boundingBoxX2 != null) 'bounding_box_x2': boundingBoxX2, + if (boundingBoxY2 != null) 'bounding_box_y2': boundingBoxY2, + if (sourceType != null) 'source_type': sourceType, + }); + } + + i1.AssetFaceEntityCompanion copyWith({ + i0.Value? id, + i0.Value? assetId, + i0.Value? personId, + i0.Value? imageWidth, + i0.Value? imageHeight, + i0.Value? boundingBoxX1, + i0.Value? boundingBoxY1, + i0.Value? boundingBoxX2, + i0.Value? boundingBoxY2, + i0.Value? sourceType, + }) { + return i1.AssetFaceEntityCompanion( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId ?? this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = i0.Variable(id.value); + } + if (assetId.present) { + map['asset_id'] = i0.Variable(assetId.value); + } + if (personId.present) { + map['person_id'] = i0.Variable(personId.value); + } + if (imageWidth.present) { + map['image_width'] = i0.Variable(imageWidth.value); + } + if (imageHeight.present) { + map['image_height'] = i0.Variable(imageHeight.value); + } + if (boundingBoxX1.present) { + map['bounding_box_x1'] = i0.Variable(boundingBoxX1.value); + } + if (boundingBoxY1.present) { + map['bounding_box_y1'] = i0.Variable(boundingBoxY1.value); + } + if (boundingBoxX2.present) { + map['bounding_box_x2'] = i0.Variable(boundingBoxX2.value); + } + if (boundingBoxY2.present) { + map['bounding_box_y2'] = i0.Variable(boundingBoxY2.value); + } + if (sourceType.present) { + map['source_type'] = i0.Variable(sourceType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityCompanion(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } +} diff --git a/mobile/lib/infrastructure/entities/device_asset.entity.dart b/mobile/lib/infrastructure/entities/device_asset.entity.dart index d8bfb2aa45..e3e4a0d4f4 100644 --- a/mobile/lib/infrastructure/entities/device_asset.entity.dart +++ b/mobile/lib/infrastructure/entities/device_asset.entity.dart @@ -16,21 +16,10 @@ class DeviceAssetEntity { final List hash; final DateTime modifiedTime; - const DeviceAssetEntity({ - required this.assetId, - required this.hash, - required this.modifiedTime, - }); + const DeviceAssetEntity({required this.assetId, required this.hash, required this.modifiedTime}); - DeviceAsset toModel() => DeviceAsset( - assetId: assetId, - hash: Uint8List.fromList(hash), - modifiedTime: modifiedTime, - ); + DeviceAsset toModel() => DeviceAsset(assetId: assetId, hash: Uint8List.fromList(hash), modifiedTime: modifiedTime); - static DeviceAssetEntity fromDto(DeviceAsset dto) => DeviceAssetEntity( - assetId: dto.assetId, - hash: dto.hash, - modifiedTime: dto.modifiedTime, - ); + static DeviceAssetEntity fromDto(DeviceAsset dto) => + DeviceAssetEntity(assetId: dto.assetId, hash: dto.hash, modifiedTime: dto.modifiedTime); } diff --git a/mobile/lib/infrastructure/entities/device_asset.entity.g.dart b/mobile/lib/infrastructure/entities/device_asset.entity.g.dart index a66f8288ef..87ae54ad40 100644 --- a/mobile/lib/infrastructure/entities/device_asset.entity.g.dart +++ b/mobile/lib/infrastructure/entities/device_asset.entity.g.dart @@ -17,22 +17,15 @@ const DeviceAssetEntitySchema = CollectionSchema( name: r'DeviceAssetEntity', id: 6967030785073446271, properties: { - r'assetId': PropertySchema( - id: 0, - name: r'assetId', - type: IsarType.string, - ), - r'hash': PropertySchema( - id: 1, - name: r'hash', - type: IsarType.byteList, - ), + r'assetId': PropertySchema(id: 0, name: r'assetId', type: IsarType.string), + r'hash': PropertySchema(id: 1, name: r'hash', type: IsarType.byteList), r'modifiedTime': PropertySchema( id: 2, name: r'modifiedTime', type: IsarType.dateTime, - ) + ), }, + estimateSize: _deviceAssetEntityEstimateSize, serialize: _deviceAssetEntitySerialize, deserialize: _deviceAssetEntityDeserialize, @@ -49,7 +42,7 @@ const DeviceAssetEntitySchema = CollectionSchema( name: r'assetId', type: IndexType.hash, caseSensitive: true, - ) + ), ], ), r'hash': IndexSchema( @@ -62,12 +55,13 @@ const DeviceAssetEntitySchema = CollectionSchema( name: r'hash', type: IndexType.hash, caseSensitive: false, - ) + ), ], - ) + ), }, links: {}, embeddedSchemas: {}, + getId: _deviceAssetEntityGetId, getLinks: _deviceAssetEntityGetLinks, attach: _deviceAssetEntityAttach, @@ -133,12 +127,16 @@ Id _deviceAssetEntityGetId(DeviceAssetEntity object) { } List> _deviceAssetEntityGetLinks( - DeviceAssetEntity object) { + DeviceAssetEntity object, +) { return []; } void _deviceAssetEntityAttach( - IsarCollection col, Id id, DeviceAssetEntity object) {} + IsarCollection col, + Id id, + DeviceAssetEntity object, +) {} extension DeviceAssetEntityByIndex on IsarCollection { Future getByAssetId(String assetId) { @@ -189,8 +187,10 @@ extension DeviceAssetEntityByIndex on IsarCollection { return putAllByIndex(r'assetId', objects); } - List putAllByAssetIdSync(List objects, - {bool saveLinks = true}) { + List putAllByAssetIdSync( + List objects, { + bool saveLinks = true, + }) { return putAllByIndexSync(r'assetId', objects, saveLinks: saveLinks); } } @@ -207,17 +207,14 @@ extension DeviceAssetEntityQueryWhereSort extension DeviceAssetEntityQueryWhere on QueryBuilder { QueryBuilder - idEqualTo(Id id) { + idEqualTo(Id id) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: id, - upper: id, - )); + return query.addWhereClause(IdWhereClause.between(lower: id, upper: id)); }); } QueryBuilder - idNotEqualTo(Id id) { + idNotEqualTo(Id id) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query @@ -240,7 +237,7 @@ extension DeviceAssetEntityQueryWhere } QueryBuilder - idGreaterThan(Id id, {bool include = false}) { + idGreaterThan(Id id, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: id, includeLower: include), @@ -249,7 +246,7 @@ extension DeviceAssetEntityQueryWhere } QueryBuilder - idLessThan(Id id, {bool include = false}) { + idLessThan(Id id, {bool include = false}) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: id, includeUpper: include), @@ -258,108 +255,124 @@ extension DeviceAssetEntityQueryWhere } QueryBuilder - idBetween( + idBetween( Id lowerId, Id upperId, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerId, - includeLower: includeLower, - upper: upperId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder - assetIdEqualTo(String assetId) { + assetIdEqualTo(String assetId) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'assetId', - value: [assetId], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'assetId', value: [assetId]), + ); }); } QueryBuilder - assetIdNotEqualTo(String assetId) { + assetIdNotEqualTo(String assetId) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'assetId', - lower: [], - upper: [assetId], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'assetId', - lower: [assetId], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'assetId', + lower: [], + upper: [assetId], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'assetId', + lower: [assetId], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'assetId', - lower: [assetId], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'assetId', - lower: [], - upper: [assetId], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'assetId', + lower: [assetId], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'assetId', + lower: [], + upper: [assetId], + includeUpper: false, + ), + ); } }); } QueryBuilder - hashEqualTo(List hash) { + hashEqualTo(List hash) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'hash', - value: [hash], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'hash', value: [hash]), + ); }); } QueryBuilder - hashNotEqualTo(List hash) { + hashNotEqualTo(List hash) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [], - upper: [hash], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [hash], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [], + upper: [hash], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [hash], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [hash], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'hash', - lower: [], - upper: [hash], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [hash], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'hash', + lower: [], + upper: [hash], + includeUpper: false, + ), + ); } }); } @@ -368,53 +381,56 @@ extension DeviceAssetEntityQueryWhere extension DeviceAssetEntityQueryFilter on QueryBuilder { QueryBuilder - assetIdEqualTo( - String value, { - bool caseSensitive = true, - }) { + assetIdEqualTo(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'assetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'assetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - assetIdGreaterThan( + assetIdGreaterThan( String value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'assetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'assetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - assetIdLessThan( + assetIdLessThan( String value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'assetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'assetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - assetIdBetween( + assetIdBetween( String lower, String upper, { bool includeLower = true, @@ -422,216 +438,181 @@ extension DeviceAssetEntityQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'assetId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'assetId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - assetIdStartsWith( - String value, { - bool caseSensitive = true, - }) { + assetIdStartsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'assetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'assetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - assetIdEndsWith( - String value, { - bool caseSensitive = true, - }) { + assetIdEndsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'assetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'assetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - assetIdContains(String value, {bool caseSensitive = true}) { + assetIdContains(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'assetId', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'assetId', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - assetIdMatches(String pattern, {bool caseSensitive = true}) { + assetIdMatches(String pattern, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'assetId', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'assetId', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - assetIdIsEmpty() { + assetIdIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'assetId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'assetId', value: ''), + ); }); } QueryBuilder - assetIdIsNotEmpty() { + assetIdIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'assetId', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'assetId', value: ''), + ); }); } QueryBuilder - hashElementEqualTo(int value) { + hashElementEqualTo(int value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'hash', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'hash', value: value), + ); }); } QueryBuilder - hashElementGreaterThan( - int value, { - bool include = false, - }) { + hashElementGreaterThan(int value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'hash', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'hash', + value: value, + ), + ); }); } QueryBuilder - hashElementLessThan( - int value, { - bool include = false, - }) { + hashElementLessThan(int value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'hash', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'hash', + value: value, + ), + ); }); } QueryBuilder - hashElementBetween( + hashElementBetween( int lower, int upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'hash', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); - }); - } - - QueryBuilder - hashLengthEqualTo(int length) { - return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - length, - true, - length, - true, + return query.addFilterCondition( + FilterCondition.between( + property: r'hash', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), ); }); } QueryBuilder - hashIsEmpty() { + hashLengthEqualTo(int length) { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - 0, - true, - 0, - true, - ); + return query.listLength(r'hash', length, true, length, true); }); } QueryBuilder - hashIsNotEmpty() { + hashIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - 0, - false, - 999999, - true, - ); + return query.listLength(r'hash', 0, true, 0, true); }); } QueryBuilder - hashLengthLessThan( - int length, { - bool include = false, - }) { + hashIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - 0, - true, - length, - include, - ); + return query.listLength(r'hash', 0, false, 999999, true); }); } QueryBuilder - hashLengthGreaterThan( - int length, { - bool include = false, - }) { + hashLengthLessThan(int length, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.listLength( - r'hash', - length, - include, - 999999, - true, - ); + return query.listLength(r'hash', 0, true, length, include); }); } QueryBuilder - hashLengthBetween( + hashLengthGreaterThan(int length, {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.listLength(r'hash', length, include, 999999, true); + }); + } + + QueryBuilder + hashLengthBetween( int lower, int upper, { bool includeLower = true, @@ -649,114 +630,112 @@ extension DeviceAssetEntityQueryFilter } QueryBuilder - idEqualTo(Id value) { + idEqualTo(Id value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: value), + ); }); } QueryBuilder - idGreaterThan( - Id value, { - bool include = false, - }) { + idGreaterThan(Id value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + ), + ); }); } QueryBuilder - idLessThan( - Id value, { - bool include = false, - }) { + idLessThan(Id value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + ), + ); }); } QueryBuilder - idBetween( + idBetween( Id lower, Id upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder - modifiedTimeEqualTo(DateTime value) { + modifiedTimeEqualTo(DateTime value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'modifiedTime', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'modifiedTime', value: value), + ); }); } QueryBuilder - modifiedTimeGreaterThan( - DateTime value, { - bool include = false, - }) { + modifiedTimeGreaterThan(DateTime value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'modifiedTime', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'modifiedTime', + value: value, + ), + ); }); } QueryBuilder - modifiedTimeLessThan( - DateTime value, { - bool include = false, - }) { + modifiedTimeLessThan(DateTime value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'modifiedTime', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'modifiedTime', + value: value, + ), + ); }); } QueryBuilder - modifiedTimeBetween( + modifiedTimeBetween( DateTime lower, DateTime upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'modifiedTime', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'modifiedTime', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } } @@ -770,28 +749,28 @@ extension DeviceAssetEntityQueryLinks extension DeviceAssetEntityQuerySortBy on QueryBuilder { QueryBuilder - sortByAssetId() { + sortByAssetId() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'assetId', Sort.asc); }); } QueryBuilder - sortByAssetIdDesc() { + sortByAssetIdDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'assetId', Sort.desc); }); } QueryBuilder - sortByModifiedTime() { + sortByModifiedTime() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'modifiedTime', Sort.asc); }); } QueryBuilder - sortByModifiedTimeDesc() { + sortByModifiedTimeDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'modifiedTime', Sort.desc); }); @@ -801,14 +780,14 @@ extension DeviceAssetEntityQuerySortBy extension DeviceAssetEntityQuerySortThenBy on QueryBuilder { QueryBuilder - thenByAssetId() { + thenByAssetId() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'assetId', Sort.asc); }); } QueryBuilder - thenByAssetIdDesc() { + thenByAssetIdDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'assetId', Sort.desc); }); @@ -821,21 +800,21 @@ extension DeviceAssetEntityQuerySortThenBy } QueryBuilder - thenByIdDesc() { + thenByIdDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'id', Sort.desc); }); } QueryBuilder - thenByModifiedTime() { + thenByModifiedTime() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'modifiedTime', Sort.asc); }); } QueryBuilder - thenByModifiedTimeDesc() { + thenByModifiedTimeDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'modifiedTime', Sort.desc); }); @@ -845,21 +824,21 @@ extension DeviceAssetEntityQuerySortThenBy extension DeviceAssetEntityQueryWhereDistinct on QueryBuilder { QueryBuilder - distinctByAssetId({bool caseSensitive = true}) { + distinctByAssetId({bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'assetId', caseSensitive: caseSensitive); }); } QueryBuilder - distinctByHash() { + distinctByHash() { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'hash'); }); } QueryBuilder - distinctByModifiedTime() { + distinctByModifiedTime() { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'modifiedTime'); }); @@ -887,7 +866,7 @@ extension DeviceAssetEntityQueryProperty } QueryBuilder - modifiedTimeProperty() { + modifiedTimeProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'modifiedTime'); }); diff --git a/mobile/lib/infrastructure/entities/exif.entity.dart b/mobile/lib/infrastructure/entities/exif.entity.dart index 2ec8f0023f..e8c7541349 100644 --- a/mobile/lib/infrastructure/entities/exif.entity.dart +++ b/mobile/lib/infrastructure/entities/exif.entity.dart @@ -52,54 +52,53 @@ class ExifInfo { }); static ExifInfo fromDto(domain.ExifInfo dto) => ExifInfo( - id: dto.assetId, - fileSize: dto.fileSize, - dateTimeOriginal: dto.dateTimeOriginal, - timeZone: dto.timeZone, - make: dto.make, - model: dto.model, - lens: dto.lens, - f: dto.f, - mm: dto.mm, - iso: dto.iso?.toInt(), - exposureSeconds: dto.exposureSeconds, - lat: dto.latitude, - long: dto.longitude, - city: dto.city, - state: dto.state, - country: dto.country, - description: dto.description, - orientation: dto.orientation, - ); + id: dto.assetId, + fileSize: dto.fileSize, + dateTimeOriginal: dto.dateTimeOriginal, + timeZone: dto.timeZone, + make: dto.make, + model: dto.model, + lens: dto.lens, + f: dto.f, + mm: dto.mm, + iso: dto.iso?.toInt(), + exposureSeconds: dto.exposureSeconds, + lat: dto.latitude, + long: dto.longitude, + city: dto.city, + state: dto.state, + country: dto.country, + description: dto.description, + orientation: dto.orientation, + ); domain.ExifInfo toDto() => domain.ExifInfo( - assetId: id, - fileSize: fileSize, - description: description, - orientation: orientation, - timeZone: timeZone, - dateTimeOriginal: dateTimeOriginal, - isFlipped: ExifDtoConverter.isOrientationFlipped(orientation), - latitude: lat, - longitude: long, - city: city, - state: state, - country: country, - make: make, - model: model, - lens: lens, - f: f, - mm: mm, - iso: iso?.toInt(), - exposureSeconds: exposureSeconds, - ); + assetId: id, + fileSize: fileSize, + description: description, + orientation: orientation, + timeZone: timeZone, + dateTimeOriginal: dateTimeOriginal, + isFlipped: ExifDtoConverter.isOrientationFlipped(orientation), + latitude: lat, + longitude: long, + city: city, + state: state, + country: country, + make: make, + model: model, + lens: lens, + f: f, + mm: mm, + iso: iso?.toInt(), + exposureSeconds: exposureSeconds, + ); } class RemoteExifEntity extends Table with DriftDefaultsMixin { const RemoteExifEntity(); - TextColumn get assetId => - text().references(RemoteAssetEntity, #id, onDelete: KeyAction.cascade)(); + TextColumn get assetId => text().references(RemoteAssetEntity, #id, onDelete: KeyAction.cascade)(); TextColumn get city => text().nullable()(); @@ -149,24 +148,24 @@ class RemoteExifEntity extends Table with DriftDefaultsMixin { extension RemoteExifEntityDataDomainEx on RemoteExifEntityData { domain.ExifInfo toDto() => domain.ExifInfo( - fileSize: fileSize, - dateTimeOriginal: dateTimeOriginal, - timeZone: timeZone, - make: make, - model: model, - iso: iso, - city: city, - state: state, - country: country, - description: description, - orientation: orientation, - latitude: latitude, - longitude: longitude, - f: fNumber?.toDouble(), - mm: focalLength?.toDouble(), - lens: lens, - width: width?.toDouble(), - height: height?.toDouble(), - isFlipped: ExifDtoConverter.isOrientationFlipped(orientation), - ); + fileSize: fileSize, + dateTimeOriginal: dateTimeOriginal, + timeZone: timeZone, + make: make, + model: model, + iso: iso, + city: city, + state: state, + country: country, + description: description, + orientation: orientation, + latitude: latitude, + longitude: longitude, + f: fNumber?.toDouble(), + mm: focalLength?.toDouble(), + lens: lens, + width: width?.toDouble(), + height: height?.toDouble(), + isFlipped: ExifDtoConverter.isOrientationFlipped(orientation), + ); } diff --git a/mobile/lib/infrastructure/entities/exif.entity.drift.dart b/mobile/lib/infrastructure/entities/exif.entity.drift.dart index 10712948ea..d45d6e8ef6 100644 --- a/mobile/lib/infrastructure/entities/exif.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/exif.entity.drift.dart @@ -8,86 +8,100 @@ import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift. as i3; import 'package:drift/internal/modular.dart' as i4; -typedef $$RemoteExifEntityTableCreateCompanionBuilder - = i1.RemoteExifEntityCompanion Function({ - required String assetId, - i0.Value city, - i0.Value state, - i0.Value country, - i0.Value dateTimeOriginal, - i0.Value description, - i0.Value height, - i0.Value width, - i0.Value exposureTime, - i0.Value fNumber, - i0.Value fileSize, - i0.Value focalLength, - i0.Value latitude, - i0.Value longitude, - i0.Value iso, - i0.Value make, - i0.Value model, - i0.Value lens, - i0.Value orientation, - i0.Value timeZone, - i0.Value rating, - i0.Value projectionType, -}); -typedef $$RemoteExifEntityTableUpdateCompanionBuilder - = i1.RemoteExifEntityCompanion Function({ - i0.Value assetId, - i0.Value city, - i0.Value state, - i0.Value country, - i0.Value dateTimeOriginal, - i0.Value description, - i0.Value height, - i0.Value width, - i0.Value exposureTime, - i0.Value fNumber, - i0.Value fileSize, - i0.Value focalLength, - i0.Value latitude, - i0.Value longitude, - i0.Value iso, - i0.Value make, - i0.Value model, - i0.Value lens, - i0.Value orientation, - i0.Value timeZone, - i0.Value rating, - i0.Value projectionType, -}); +typedef $$RemoteExifEntityTableCreateCompanionBuilder = + i1.RemoteExifEntityCompanion Function({ + required String assetId, + i0.Value city, + i0.Value state, + i0.Value country, + i0.Value dateTimeOriginal, + i0.Value description, + i0.Value height, + i0.Value width, + i0.Value exposureTime, + i0.Value fNumber, + i0.Value fileSize, + i0.Value focalLength, + i0.Value latitude, + i0.Value longitude, + i0.Value iso, + i0.Value make, + i0.Value model, + i0.Value lens, + i0.Value orientation, + i0.Value timeZone, + i0.Value rating, + i0.Value projectionType, + }); +typedef $$RemoteExifEntityTableUpdateCompanionBuilder = + i1.RemoteExifEntityCompanion Function({ + i0.Value assetId, + i0.Value city, + i0.Value state, + i0.Value country, + i0.Value dateTimeOriginal, + i0.Value description, + i0.Value height, + i0.Value width, + i0.Value exposureTime, + i0.Value fNumber, + i0.Value fileSize, + i0.Value focalLength, + i0.Value latitude, + i0.Value longitude, + i0.Value iso, + i0.Value make, + i0.Value model, + i0.Value lens, + i0.Value orientation, + i0.Value timeZone, + i0.Value rating, + i0.Value projectionType, + }); -final class $$RemoteExifEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, i1.$RemoteExifEntityTable, i1.RemoteExifEntityData> { +final class $$RemoteExifEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$RemoteExifEntityTable, + i1.RemoteExifEntityData + > { $$RemoteExifEntityTableReferences( - super.$_db, super.$_table, super.$_typedResult); + super.$_db, + super.$_table, + super.$_typedResult, + ); static i3.$RemoteAssetEntityTable _assetIdTable(i0.GeneratedDatabase db) => i4.ReadDatabaseContainer(db) .resultSet('remote_asset_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i4.ReadDatabaseContainer(db) .resultSet('remote_exif_entity') .assetId, - i4.ReadDatabaseContainer(db) - .resultSet('remote_asset_entity') - .id)); + i4.ReadDatabaseContainer( + db, + ).resultSet('remote_asset_entity').id, + ), + ); i3.$$RemoteAssetEntityTableProcessedTableManager get assetId { final $_column = $_itemColumn('asset_id')!; final manager = i3 .$$RemoteAssetEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer( $_db, - i4.ReadDatabaseContainer($_db) - .resultSet('remote_asset_entity')) + ).resultSet('remote_asset_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_assetIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -101,93 +115,134 @@ class $$RemoteExifEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get city => $composableBuilder( - column: $table.city, builder: (column) => i0.ColumnFilters(column)); + column: $table.city, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get state => $composableBuilder( - column: $table.state, builder: (column) => i0.ColumnFilters(column)); + column: $table.state, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get country => $composableBuilder( - column: $table.country, builder: (column) => i0.ColumnFilters(column)); + column: $table.country, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get dateTimeOriginal => $composableBuilder( - column: $table.dateTimeOriginal, - builder: (column) => i0.ColumnFilters(column)); + column: $table.dateTimeOriginal, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get description => $composableBuilder( - column: $table.description, - builder: (column) => i0.ColumnFilters(column)); + column: $table.description, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get height => $composableBuilder( - column: $table.height, builder: (column) => i0.ColumnFilters(column)); + column: $table.height, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get width => $composableBuilder( - column: $table.width, builder: (column) => i0.ColumnFilters(column)); + column: $table.width, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get exposureTime => $composableBuilder( - column: $table.exposureTime, - builder: (column) => i0.ColumnFilters(column)); + column: $table.exposureTime, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get fNumber => $composableBuilder( - column: $table.fNumber, builder: (column) => i0.ColumnFilters(column)); + column: $table.fNumber, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get fileSize => $composableBuilder( - column: $table.fileSize, builder: (column) => i0.ColumnFilters(column)); + column: $table.fileSize, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get focalLength => $composableBuilder( - column: $table.focalLength, - builder: (column) => i0.ColumnFilters(column)); + column: $table.focalLength, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get latitude => $composableBuilder( - column: $table.latitude, builder: (column) => i0.ColumnFilters(column)); + column: $table.latitude, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get longitude => $composableBuilder( - column: $table.longitude, builder: (column) => i0.ColumnFilters(column)); + column: $table.longitude, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get iso => $composableBuilder( - column: $table.iso, builder: (column) => i0.ColumnFilters(column)); + column: $table.iso, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get make => $composableBuilder( - column: $table.make, builder: (column) => i0.ColumnFilters(column)); + column: $table.make, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get model => $composableBuilder( - column: $table.model, builder: (column) => i0.ColumnFilters(column)); + column: $table.model, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get lens => $composableBuilder( - column: $table.lens, builder: (column) => i0.ColumnFilters(column)); + column: $table.lens, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get orientation => $composableBuilder( - column: $table.orientation, - builder: (column) => i0.ColumnFilters(column)); + column: $table.orientation, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get timeZone => $composableBuilder( - column: $table.timeZone, builder: (column) => i0.ColumnFilters(column)); + column: $table.timeZone, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get rating => $composableBuilder( - column: $table.rating, builder: (column) => i0.ColumnFilters(column)); + column: $table.rating, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get projectionType => $composableBuilder( - column: $table.projectionType, - builder: (column) => i0.ColumnFilters(column)); + column: $table.projectionType, + builder: (column) => i0.ColumnFilters(column), + ); i3.$$RemoteAssetEntityTableFilterComposer get assetId { final i3.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableFilterComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -202,96 +257,135 @@ class $$RemoteExifEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get city => $composableBuilder( - column: $table.city, builder: (column) => i0.ColumnOrderings(column)); + column: $table.city, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get state => $composableBuilder( - column: $table.state, builder: (column) => i0.ColumnOrderings(column)); + column: $table.state, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get country => $composableBuilder( - column: $table.country, builder: (column) => i0.ColumnOrderings(column)); + column: $table.country, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get dateTimeOriginal => $composableBuilder( - column: $table.dateTimeOriginal, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.dateTimeOriginal, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get description => $composableBuilder( - column: $table.description, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.description, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get height => $composableBuilder( - column: $table.height, builder: (column) => i0.ColumnOrderings(column)); + column: $table.height, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get width => $composableBuilder( - column: $table.width, builder: (column) => i0.ColumnOrderings(column)); + column: $table.width, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get exposureTime => $composableBuilder( - column: $table.exposureTime, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.exposureTime, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get fNumber => $composableBuilder( - column: $table.fNumber, builder: (column) => i0.ColumnOrderings(column)); + column: $table.fNumber, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get fileSize => $composableBuilder( - column: $table.fileSize, builder: (column) => i0.ColumnOrderings(column)); + column: $table.fileSize, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get focalLength => $composableBuilder( - column: $table.focalLength, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.focalLength, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get latitude => $composableBuilder( - column: $table.latitude, builder: (column) => i0.ColumnOrderings(column)); + column: $table.latitude, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get longitude => $composableBuilder( - column: $table.longitude, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.longitude, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get iso => $composableBuilder( - column: $table.iso, builder: (column) => i0.ColumnOrderings(column)); + column: $table.iso, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get make => $composableBuilder( - column: $table.make, builder: (column) => i0.ColumnOrderings(column)); + column: $table.make, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get model => $composableBuilder( - column: $table.model, builder: (column) => i0.ColumnOrderings(column)); + column: $table.model, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get lens => $composableBuilder( - column: $table.lens, builder: (column) => i0.ColumnOrderings(column)); + column: $table.lens, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get orientation => $composableBuilder( - column: $table.orientation, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.orientation, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get timeZone => $composableBuilder( - column: $table.timeZone, builder: (column) => i0.ColumnOrderings(column)); + column: $table.timeZone, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get rating => $composableBuilder( - column: $table.rating, builder: (column) => i0.ColumnOrderings(column)); + column: $table.rating, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get projectionType => $composableBuilder( - column: $table.projectionType, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.projectionType, + builder: (column) => i0.ColumnOrderings(column), + ); i3.$$RemoteAssetEntityTableOrderingComposer get assetId { final i3.$$RemoteAssetEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableOrderingComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -315,10 +409,14 @@ class $$RemoteExifEntityTableAnnotationComposer $composableBuilder(column: $table.country, builder: (column) => column); i0.GeneratedColumn get dateTimeOriginal => $composableBuilder( - column: $table.dateTimeOriginal, builder: (column) => column); + column: $table.dateTimeOriginal, + builder: (column) => column, + ); i0.GeneratedColumn get description => $composableBuilder( - column: $table.description, builder: (column) => column); + column: $table.description, + builder: (column) => column, + ); i0.GeneratedColumn get height => $composableBuilder(column: $table.height, builder: (column) => column); @@ -327,7 +425,9 @@ class $$RemoteExifEntityTableAnnotationComposer $composableBuilder(column: $table.width, builder: (column) => column); i0.GeneratedColumn get exposureTime => $composableBuilder( - column: $table.exposureTime, builder: (column) => column); + column: $table.exposureTime, + builder: (column) => column, + ); i0.GeneratedColumn get fNumber => $composableBuilder(column: $table.fNumber, builder: (column) => column); @@ -336,7 +436,9 @@ class $$RemoteExifEntityTableAnnotationComposer $composableBuilder(column: $table.fileSize, builder: (column) => column); i0.GeneratedColumn get focalLength => $composableBuilder( - column: $table.focalLength, builder: (column) => column); + column: $table.focalLength, + builder: (column) => column, + ); i0.GeneratedColumn get latitude => $composableBuilder(column: $table.latitude, builder: (column) => column); @@ -357,7 +459,9 @@ class $$RemoteExifEntityTableAnnotationComposer $composableBuilder(column: $table.lens, builder: (column) => column); i0.GeneratedColumn get orientation => $composableBuilder( - column: $table.orientation, builder: (column) => column); + column: $table.orientation, + builder: (column) => column, + ); i0.GeneratedColumn get timeZone => $composableBuilder(column: $table.timeZone, builder: (column) => column); @@ -366,48 +470,59 @@ class $$RemoteExifEntityTableAnnotationComposer $composableBuilder(column: $table.rating, builder: (column) => column); i0.GeneratedColumn get projectionType => $composableBuilder( - column: $table.projectionType, builder: (column) => column); + column: $table.projectionType, + builder: (column) => column, + ); i3.$$RemoteAssetEntityTableAnnotationComposer get assetId { final i3.$$RemoteAssetEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableAnnotationComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$RemoteExifEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$RemoteExifEntityTable, - i1.RemoteExifEntityData, - i1.$$RemoteExifEntityTableFilterComposer, - i1.$$RemoteExifEntityTableOrderingComposer, - i1.$$RemoteExifEntityTableAnnotationComposer, - $$RemoteExifEntityTableCreateCompanionBuilder, - $$RemoteExifEntityTableUpdateCompanionBuilder, - (i1.RemoteExifEntityData, i1.$$RemoteExifEntityTableReferences), - i1.RemoteExifEntityData, - i0.PrefetchHooks Function({bool assetId})> { +class $$RemoteExifEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$RemoteExifEntityTable, + i1.RemoteExifEntityData, + i1.$$RemoteExifEntityTableFilterComposer, + i1.$$RemoteExifEntityTableOrderingComposer, + i1.$$RemoteExifEntityTableAnnotationComposer, + $$RemoteExifEntityTableCreateCompanionBuilder, + $$RemoteExifEntityTableUpdateCompanionBuilder, + (i1.RemoteExifEntityData, i1.$$RemoteExifEntityTableReferences), + i1.RemoteExifEntityData, + i0.PrefetchHooks Function({bool assetId}) + > { $$RemoteExifEntityTableTableManager( - i0.GeneratedDatabase db, i1.$RemoteExifEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$RemoteExifEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -416,115 +531,120 @@ class $$RemoteExifEntityTableTableManager extends i0.RootTableManager< .$$RemoteExifEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$RemoteExifEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value assetId = const i0.Value.absent(), - i0.Value city = const i0.Value.absent(), - i0.Value state = const i0.Value.absent(), - i0.Value country = const i0.Value.absent(), - i0.Value dateTimeOriginal = const i0.Value.absent(), - i0.Value description = const i0.Value.absent(), - i0.Value height = const i0.Value.absent(), - i0.Value width = const i0.Value.absent(), - i0.Value exposureTime = const i0.Value.absent(), - i0.Value fNumber = const i0.Value.absent(), - i0.Value fileSize = const i0.Value.absent(), - i0.Value focalLength = const i0.Value.absent(), - i0.Value latitude = const i0.Value.absent(), - i0.Value longitude = const i0.Value.absent(), - i0.Value iso = const i0.Value.absent(), - i0.Value make = const i0.Value.absent(), - i0.Value model = const i0.Value.absent(), - i0.Value lens = const i0.Value.absent(), - i0.Value orientation = const i0.Value.absent(), - i0.Value timeZone = const i0.Value.absent(), - i0.Value rating = const i0.Value.absent(), - i0.Value projectionType = const i0.Value.absent(), - }) => - i1.RemoteExifEntityCompanion( - assetId: assetId, - city: city, - state: state, - country: country, - dateTimeOriginal: dateTimeOriginal, - description: description, - height: height, - width: width, - exposureTime: exposureTime, - fNumber: fNumber, - fileSize: fileSize, - focalLength: focalLength, - latitude: latitude, - longitude: longitude, - iso: iso, - make: make, - model: model, - lens: lens, - orientation: orientation, - timeZone: timeZone, - rating: rating, - projectionType: projectionType, - ), - createCompanionCallback: ({ - required String assetId, - i0.Value city = const i0.Value.absent(), - i0.Value state = const i0.Value.absent(), - i0.Value country = const i0.Value.absent(), - i0.Value dateTimeOriginal = const i0.Value.absent(), - i0.Value description = const i0.Value.absent(), - i0.Value height = const i0.Value.absent(), - i0.Value width = const i0.Value.absent(), - i0.Value exposureTime = const i0.Value.absent(), - i0.Value fNumber = const i0.Value.absent(), - i0.Value fileSize = const i0.Value.absent(), - i0.Value focalLength = const i0.Value.absent(), - i0.Value latitude = const i0.Value.absent(), - i0.Value longitude = const i0.Value.absent(), - i0.Value iso = const i0.Value.absent(), - i0.Value make = const i0.Value.absent(), - i0.Value model = const i0.Value.absent(), - i0.Value lens = const i0.Value.absent(), - i0.Value orientation = const i0.Value.absent(), - i0.Value timeZone = const i0.Value.absent(), - i0.Value rating = const i0.Value.absent(), - i0.Value projectionType = const i0.Value.absent(), - }) => - i1.RemoteExifEntityCompanion.insert( - assetId: assetId, - city: city, - state: state, - country: country, - dateTimeOriginal: dateTimeOriginal, - description: description, - height: height, - width: width, - exposureTime: exposureTime, - fNumber: fNumber, - fileSize: fileSize, - focalLength: focalLength, - latitude: latitude, - longitude: longitude, - iso: iso, - make: make, - model: model, - lens: lens, - orientation: orientation, - timeZone: timeZone, - rating: rating, - projectionType: projectionType, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value assetId = const i0.Value.absent(), + i0.Value city = const i0.Value.absent(), + i0.Value state = const i0.Value.absent(), + i0.Value country = const i0.Value.absent(), + i0.Value dateTimeOriginal = const i0.Value.absent(), + i0.Value description = const i0.Value.absent(), + i0.Value height = const i0.Value.absent(), + i0.Value width = const i0.Value.absent(), + i0.Value exposureTime = const i0.Value.absent(), + i0.Value fNumber = const i0.Value.absent(), + i0.Value fileSize = const i0.Value.absent(), + i0.Value focalLength = const i0.Value.absent(), + i0.Value latitude = const i0.Value.absent(), + i0.Value longitude = const i0.Value.absent(), + i0.Value iso = const i0.Value.absent(), + i0.Value make = const i0.Value.absent(), + i0.Value model = const i0.Value.absent(), + i0.Value lens = const i0.Value.absent(), + i0.Value orientation = const i0.Value.absent(), + i0.Value timeZone = const i0.Value.absent(), + i0.Value rating = const i0.Value.absent(), + i0.Value projectionType = const i0.Value.absent(), + }) => i1.RemoteExifEntityCompanion( + assetId: assetId, + city: city, + state: state, + country: country, + dateTimeOriginal: dateTimeOriginal, + description: description, + height: height, + width: width, + exposureTime: exposureTime, + fNumber: fNumber, + fileSize: fileSize, + focalLength: focalLength, + latitude: latitude, + longitude: longitude, + iso: iso, + make: make, + model: model, + lens: lens, + orientation: orientation, + timeZone: timeZone, + rating: rating, + projectionType: projectionType, + ), + createCompanionCallback: + ({ + required String assetId, + i0.Value city = const i0.Value.absent(), + i0.Value state = const i0.Value.absent(), + i0.Value country = const i0.Value.absent(), + i0.Value dateTimeOriginal = const i0.Value.absent(), + i0.Value description = const i0.Value.absent(), + i0.Value height = const i0.Value.absent(), + i0.Value width = const i0.Value.absent(), + i0.Value exposureTime = const i0.Value.absent(), + i0.Value fNumber = const i0.Value.absent(), + i0.Value fileSize = const i0.Value.absent(), + i0.Value focalLength = const i0.Value.absent(), + i0.Value latitude = const i0.Value.absent(), + i0.Value longitude = const i0.Value.absent(), + i0.Value iso = const i0.Value.absent(), + i0.Value make = const i0.Value.absent(), + i0.Value model = const i0.Value.absent(), + i0.Value lens = const i0.Value.absent(), + i0.Value orientation = const i0.Value.absent(), + i0.Value timeZone = const i0.Value.absent(), + i0.Value rating = const i0.Value.absent(), + i0.Value projectionType = const i0.Value.absent(), + }) => i1.RemoteExifEntityCompanion.insert( + assetId: assetId, + city: city, + state: state, + country: country, + dateTimeOriginal: dateTimeOriginal, + description: description, + height: height, + width: width, + exposureTime: exposureTime, + fNumber: fNumber, + fileSize: fileSize, + focalLength: focalLength, + latitude: latitude, + longitude: longitude, + iso: iso, + make: make, + model: model, + lens: lens, + orientation: orientation, + timeZone: timeZone, + rating: rating, + projectionType: projectionType, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$RemoteExifEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$RemoteExifEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({assetId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -535,41 +655,50 @@ class $$RemoteExifEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (assetId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.assetId, - referencedTable: - i1.$$RemoteExifEntityTableReferences._assetIdTable(db), - referencedColumn: i1.$$RemoteExifEntityTableReferences - ._assetIdTable(db) - .id, - ) as T; - } + dynamic + > + >(state) { + if (assetId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.assetId, + referencedTable: i1 + .$$RemoteExifEntityTableReferences + ._assetIdTable(db), + referencedColumn: i1 + .$$RemoteExifEntityTableReferences + ._assetIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$RemoteExifEntityTableProcessedTableManager = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$RemoteExifEntityTable, - i1.RemoteExifEntityData, - i1.$$RemoteExifEntityTableFilterComposer, - i1.$$RemoteExifEntityTableOrderingComposer, - i1.$$RemoteExifEntityTableAnnotationComposer, - $$RemoteExifEntityTableCreateCompanionBuilder, - $$RemoteExifEntityTableUpdateCompanionBuilder, - (i1.RemoteExifEntityData, i1.$$RemoteExifEntityTableReferences), - i1.RemoteExifEntityData, - i0.PrefetchHooks Function({bool assetId})>; +typedef $$RemoteExifEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$RemoteExifEntityTable, + i1.RemoteExifEntityData, + i1.$$RemoteExifEntityTableFilterComposer, + i1.$$RemoteExifEntityTableOrderingComposer, + i1.$$RemoteExifEntityTableAnnotationComposer, + $$RemoteExifEntityTableCreateCompanionBuilder, + $$RemoteExifEntityTableUpdateCompanionBuilder, + (i1.RemoteExifEntityData, i1.$$RemoteExifEntityTableReferences), + i1.RemoteExifEntityData, + i0.PrefetchHooks Function({bool assetId}) + >; class $RemoteExifEntityTable extends i2.RemoteExifEntity with i0.TableInfo<$RemoteExifEntityTable, i1.RemoteExifEntityData> { @@ -577,165 +706,277 @@ class $RemoteExifEntityTable extends i2.RemoteExifEntity final i0.GeneratedDatabase attachedDatabase; final String? _alias; $RemoteExifEntityTable(this.attachedDatabase, [this._alias]); - static const i0.VerificationMeta _assetIdMeta = - const i0.VerificationMeta('assetId'); + static const i0.VerificationMeta _assetIdMeta = const i0.VerificationMeta( + 'assetId', + ); @override late final i0.GeneratedColumn assetId = i0.GeneratedColumn( - 'asset_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - static const i0.VerificationMeta _cityMeta = - const i0.VerificationMeta('city'); + 'asset_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + static const i0.VerificationMeta _cityMeta = const i0.VerificationMeta( + 'city', + ); @override late final i0.GeneratedColumn city = i0.GeneratedColumn( - 'city', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _stateMeta = - const i0.VerificationMeta('state'); + 'city', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _stateMeta = const i0.VerificationMeta( + 'state', + ); @override late final i0.GeneratedColumn state = i0.GeneratedColumn( - 'state', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _countryMeta = - const i0.VerificationMeta('country'); + 'state', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _countryMeta = const i0.VerificationMeta( + 'country', + ); @override late final i0.GeneratedColumn country = i0.GeneratedColumn( - 'country', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); + 'country', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); static const i0.VerificationMeta _dateTimeOriginalMeta = const i0.VerificationMeta('dateTimeOriginal'); @override late final i0.GeneratedColumn dateTimeOriginal = - i0.GeneratedColumn('date_time_original', aliasedName, true, - type: i0.DriftSqlType.dateTime, requiredDuringInsert: false); - static const i0.VerificationMeta _descriptionMeta = - const i0.VerificationMeta('description'); + i0.GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _descriptionMeta = const i0.VerificationMeta( + 'description', + ); @override late final i0.GeneratedColumn description = - i0.GeneratedColumn('description', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _heightMeta = - const i0.VerificationMeta('height'); + i0.GeneratedColumn( + 'description', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _heightMeta = const i0.VerificationMeta( + 'height', + ); @override late final i0.GeneratedColumn height = i0.GeneratedColumn( - 'height', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); - static const i0.VerificationMeta _widthMeta = - const i0.VerificationMeta('width'); + 'height', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _widthMeta = const i0.VerificationMeta( + 'width', + ); @override late final i0.GeneratedColumn width = i0.GeneratedColumn( - 'width', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); + 'width', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); static const i0.VerificationMeta _exposureTimeMeta = const i0.VerificationMeta('exposureTime'); @override late final i0.GeneratedColumn exposureTime = - i0.GeneratedColumn('exposure_time', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _fNumberMeta = - const i0.VerificationMeta('fNumber'); + i0.GeneratedColumn( + 'exposure_time', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _fNumberMeta = const i0.VerificationMeta( + 'fNumber', + ); @override late final i0.GeneratedColumn fNumber = i0.GeneratedColumn( - 'f_number', aliasedName, true, - type: i0.DriftSqlType.double, requiredDuringInsert: false); - static const i0.VerificationMeta _fileSizeMeta = - const i0.VerificationMeta('fileSize'); + 'f_number', + aliasedName, + true, + type: i0.DriftSqlType.double, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _fileSizeMeta = const i0.VerificationMeta( + 'fileSize', + ); @override late final i0.GeneratedColumn fileSize = i0.GeneratedColumn( - 'file_size', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); - static const i0.VerificationMeta _focalLengthMeta = - const i0.VerificationMeta('focalLength'); + 'file_size', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _focalLengthMeta = const i0.VerificationMeta( + 'focalLength', + ); @override late final i0.GeneratedColumn focalLength = - i0.GeneratedColumn('focal_length', aliasedName, true, - type: i0.DriftSqlType.double, requiredDuringInsert: false); - static const i0.VerificationMeta _latitudeMeta = - const i0.VerificationMeta('latitude'); + i0.GeneratedColumn( + 'focal_length', + aliasedName, + true, + type: i0.DriftSqlType.double, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _latitudeMeta = const i0.VerificationMeta( + 'latitude', + ); @override late final i0.GeneratedColumn latitude = i0.GeneratedColumn( - 'latitude', aliasedName, true, - type: i0.DriftSqlType.double, requiredDuringInsert: false); - static const i0.VerificationMeta _longitudeMeta = - const i0.VerificationMeta('longitude'); + 'latitude', + aliasedName, + true, + type: i0.DriftSqlType.double, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _longitudeMeta = const i0.VerificationMeta( + 'longitude', + ); @override late final i0.GeneratedColumn longitude = i0.GeneratedColumn( - 'longitude', aliasedName, true, - type: i0.DriftSqlType.double, requiredDuringInsert: false); + 'longitude', + aliasedName, + true, + type: i0.DriftSqlType.double, + requiredDuringInsert: false, + ); static const i0.VerificationMeta _isoMeta = const i0.VerificationMeta('iso'); @override late final i0.GeneratedColumn iso = i0.GeneratedColumn( - 'iso', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); - static const i0.VerificationMeta _makeMeta = - const i0.VerificationMeta('make'); + 'iso', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _makeMeta = const i0.VerificationMeta( + 'make', + ); @override late final i0.GeneratedColumn make = i0.GeneratedColumn( - 'make', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _modelMeta = - const i0.VerificationMeta('model'); + 'make', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _modelMeta = const i0.VerificationMeta( + 'model', + ); @override late final i0.GeneratedColumn model = i0.GeneratedColumn( - 'model', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _lensMeta = - const i0.VerificationMeta('lens'); + 'model', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _lensMeta = const i0.VerificationMeta( + 'lens', + ); @override late final i0.GeneratedColumn lens = i0.GeneratedColumn( - 'lens', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _orientationMeta = - const i0.VerificationMeta('orientation'); + 'lens', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _orientationMeta = const i0.VerificationMeta( + 'orientation', + ); @override late final i0.GeneratedColumn orientation = - i0.GeneratedColumn('orientation', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _timeZoneMeta = - const i0.VerificationMeta('timeZone'); + i0.GeneratedColumn( + 'orientation', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _timeZoneMeta = const i0.VerificationMeta( + 'timeZone', + ); @override late final i0.GeneratedColumn timeZone = i0.GeneratedColumn( - 'time_zone', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _ratingMeta = - const i0.VerificationMeta('rating'); + 'time_zone', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _ratingMeta = const i0.VerificationMeta( + 'rating', + ); @override late final i0.GeneratedColumn rating = i0.GeneratedColumn( - 'rating', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); + 'rating', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); static const i0.VerificationMeta _projectionTypeMeta = const i0.VerificationMeta('projectionType'); @override late final i0.GeneratedColumn projectionType = - i0.GeneratedColumn('projection_type', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); + i0.GeneratedColumn( + 'projection_type', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); @override List get $columns => [ - assetId, - city, - state, - country, - dateTimeOriginal, - description, - height, - width, - exposureTime, - fNumber, - fileSize, - focalLength, - latitude, - longitude, - iso, - make, - model, - lens, - orientation, - timeZone, - rating, - projectionType - ]; + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -743,111 +984,162 @@ class $RemoteExifEntityTable extends i2.RemoteExifEntity static const String $name = 'remote_exif_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('asset_id')) { - context.handle(_assetIdMeta, - assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta)); + context.handle( + _assetIdMeta, + assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta), + ); } else if (isInserting) { context.missing(_assetIdMeta); } if (data.containsKey('city')) { context.handle( - _cityMeta, city.isAcceptableOrUnknown(data['city']!, _cityMeta)); + _cityMeta, + city.isAcceptableOrUnknown(data['city']!, _cityMeta), + ); } if (data.containsKey('state')) { context.handle( - _stateMeta, state.isAcceptableOrUnknown(data['state']!, _stateMeta)); + _stateMeta, + state.isAcceptableOrUnknown(data['state']!, _stateMeta), + ); } if (data.containsKey('country')) { - context.handle(_countryMeta, - country.isAcceptableOrUnknown(data['country']!, _countryMeta)); + context.handle( + _countryMeta, + country.isAcceptableOrUnknown(data['country']!, _countryMeta), + ); } if (data.containsKey('date_time_original')) { context.handle( + _dateTimeOriginalMeta, + dateTimeOriginal.isAcceptableOrUnknown( + data['date_time_original']!, _dateTimeOriginalMeta, - dateTimeOriginal.isAcceptableOrUnknown( - data['date_time_original']!, _dateTimeOriginalMeta)); + ), + ); } if (data.containsKey('description')) { context.handle( + _descriptionMeta, + description.isAcceptableOrUnknown( + data['description']!, _descriptionMeta, - description.isAcceptableOrUnknown( - data['description']!, _descriptionMeta)); + ), + ); } if (data.containsKey('height')) { - context.handle(_heightMeta, - height.isAcceptableOrUnknown(data['height']!, _heightMeta)); + context.handle( + _heightMeta, + height.isAcceptableOrUnknown(data['height']!, _heightMeta), + ); } if (data.containsKey('width')) { context.handle( - _widthMeta, width.isAcceptableOrUnknown(data['width']!, _widthMeta)); + _widthMeta, + width.isAcceptableOrUnknown(data['width']!, _widthMeta), + ); } if (data.containsKey('exposure_time')) { context.handle( + _exposureTimeMeta, + exposureTime.isAcceptableOrUnknown( + data['exposure_time']!, _exposureTimeMeta, - exposureTime.isAcceptableOrUnknown( - data['exposure_time']!, _exposureTimeMeta)); + ), + ); } if (data.containsKey('f_number')) { - context.handle(_fNumberMeta, - fNumber.isAcceptableOrUnknown(data['f_number']!, _fNumberMeta)); + context.handle( + _fNumberMeta, + fNumber.isAcceptableOrUnknown(data['f_number']!, _fNumberMeta), + ); } if (data.containsKey('file_size')) { - context.handle(_fileSizeMeta, - fileSize.isAcceptableOrUnknown(data['file_size']!, _fileSizeMeta)); + context.handle( + _fileSizeMeta, + fileSize.isAcceptableOrUnknown(data['file_size']!, _fileSizeMeta), + ); } if (data.containsKey('focal_length')) { context.handle( + _focalLengthMeta, + focalLength.isAcceptableOrUnknown( + data['focal_length']!, _focalLengthMeta, - focalLength.isAcceptableOrUnknown( - data['focal_length']!, _focalLengthMeta)); + ), + ); } if (data.containsKey('latitude')) { - context.handle(_latitudeMeta, - latitude.isAcceptableOrUnknown(data['latitude']!, _latitudeMeta)); + context.handle( + _latitudeMeta, + latitude.isAcceptableOrUnknown(data['latitude']!, _latitudeMeta), + ); } if (data.containsKey('longitude')) { - context.handle(_longitudeMeta, - longitude.isAcceptableOrUnknown(data['longitude']!, _longitudeMeta)); + context.handle( + _longitudeMeta, + longitude.isAcceptableOrUnknown(data['longitude']!, _longitudeMeta), + ); } if (data.containsKey('iso')) { context.handle( - _isoMeta, iso.isAcceptableOrUnknown(data['iso']!, _isoMeta)); + _isoMeta, + iso.isAcceptableOrUnknown(data['iso']!, _isoMeta), + ); } if (data.containsKey('make')) { context.handle( - _makeMeta, make.isAcceptableOrUnknown(data['make']!, _makeMeta)); + _makeMeta, + make.isAcceptableOrUnknown(data['make']!, _makeMeta), + ); } if (data.containsKey('model')) { context.handle( - _modelMeta, model.isAcceptableOrUnknown(data['model']!, _modelMeta)); + _modelMeta, + model.isAcceptableOrUnknown(data['model']!, _modelMeta), + ); } if (data.containsKey('lens')) { context.handle( - _lensMeta, lens.isAcceptableOrUnknown(data['lens']!, _lensMeta)); + _lensMeta, + lens.isAcceptableOrUnknown(data['lens']!, _lensMeta), + ); } if (data.containsKey('orientation')) { context.handle( + _orientationMeta, + orientation.isAcceptableOrUnknown( + data['orientation']!, _orientationMeta, - orientation.isAcceptableOrUnknown( - data['orientation']!, _orientationMeta)); + ), + ); } if (data.containsKey('time_zone')) { - context.handle(_timeZoneMeta, - timeZone.isAcceptableOrUnknown(data['time_zone']!, _timeZoneMeta)); + context.handle( + _timeZoneMeta, + timeZone.isAcceptableOrUnknown(data['time_zone']!, _timeZoneMeta), + ); } if (data.containsKey('rating')) { - context.handle(_ratingMeta, - rating.isAcceptableOrUnknown(data['rating']!, _ratingMeta)); + context.handle( + _ratingMeta, + rating.isAcceptableOrUnknown(data['rating']!, _ratingMeta), + ); } if (data.containsKey('projection_type')) { context.handle( + _projectionTypeMeta, + projectionType.isAcceptableOrUnknown( + data['projection_type']!, _projectionTypeMeta, - projectionType.isAcceptableOrUnknown( - data['projection_type']!, _projectionTypeMeta)); + ), + ); } return context; } @@ -855,55 +1147,100 @@ class $RemoteExifEntityTable extends i2.RemoteExifEntity @override Set get $primaryKey => {assetId}; @override - i1.RemoteExifEntityData map(Map data, - {String? tablePrefix}) { + i1.RemoteExifEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.RemoteExifEntityData( - assetId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - city: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}city']), - state: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}state']), - country: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}country']), + assetId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + city: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}city'], + ), + state: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}state'], + ), + country: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}country'], + ), dateTimeOriginal: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, - data['${effectivePrefix}date_time_original']), - description: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}description']), - height: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}height']), - width: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}width']), + i0.DriftSqlType.dateTime, + data['${effectivePrefix}date_time_original'], + ), + description: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}description'], + ), + height: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}height'], + ), + width: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}width'], + ), exposureTime: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, data['${effectivePrefix}exposure_time']), - fNumber: attachedDatabase.typeMapping - .read(i0.DriftSqlType.double, data['${effectivePrefix}f_number']), - fileSize: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}file_size']), - focalLength: attachedDatabase.typeMapping - .read(i0.DriftSqlType.double, data['${effectivePrefix}focal_length']), - latitude: attachedDatabase.typeMapping - .read(i0.DriftSqlType.double, data['${effectivePrefix}latitude']), - longitude: attachedDatabase.typeMapping - .read(i0.DriftSqlType.double, data['${effectivePrefix}longitude']), - iso: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}iso']), - make: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}make']), - model: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}model']), - lens: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}lens']), - orientation: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}orientation']), - timeZone: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}time_zone']), - rating: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}rating']), + i0.DriftSqlType.string, + data['${effectivePrefix}exposure_time'], + ), + fNumber: attachedDatabase.typeMapping.read( + i0.DriftSqlType.double, + data['${effectivePrefix}f_number'], + ), + fileSize: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}file_size'], + ), + focalLength: attachedDatabase.typeMapping.read( + i0.DriftSqlType.double, + data['${effectivePrefix}focal_length'], + ), + latitude: attachedDatabase.typeMapping.read( + i0.DriftSqlType.double, + data['${effectivePrefix}latitude'], + ), + longitude: attachedDatabase.typeMapping.read( + i0.DriftSqlType.double, + data['${effectivePrefix}longitude'], + ), + iso: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}iso'], + ), + make: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}make'], + ), + model: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}model'], + ), + lens: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}lens'], + ), + orientation: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}orientation'], + ), + timeZone: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}time_zone'], + ), + rating: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}rating'], + ), projectionType: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, data['${effectivePrefix}projection_type']), + i0.DriftSqlType.string, + data['${effectivePrefix}projection_type'], + ), ); } @@ -942,29 +1279,30 @@ class RemoteExifEntityData extends i0.DataClass final String? timeZone; final int? rating; final String? projectionType; - const RemoteExifEntityData( - {required this.assetId, - this.city, - this.state, - this.country, - this.dateTimeOriginal, - this.description, - this.height, - this.width, - this.exposureTime, - this.fNumber, - this.fileSize, - this.focalLength, - this.latitude, - this.longitude, - this.iso, - this.make, - this.model, - this.lens, - this.orientation, - this.timeZone, - this.rating, - this.projectionType}); + const RemoteExifEntityData({ + required this.assetId, + this.city, + this.state, + this.country, + this.dateTimeOriginal, + this.description, + this.height, + this.width, + this.exposureTime, + this.fNumber, + this.fileSize, + this.focalLength, + this.latitude, + this.longitude, + this.iso, + this.make, + this.model, + this.lens, + this.orientation, + this.timeZone, + this.rating, + this.projectionType, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1035,16 +1373,19 @@ class RemoteExifEntityData extends i0.DataClass return map; } - factory RemoteExifEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory RemoteExifEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return RemoteExifEntityData( assetId: serializer.fromJson(json['assetId']), city: serializer.fromJson(json['city']), state: serializer.fromJson(json['state']), country: serializer.fromJson(json['country']), - dateTimeOriginal: - serializer.fromJson(json['dateTimeOriginal']), + dateTimeOriginal: serializer.fromJson( + json['dateTimeOriginal'], + ), description: serializer.fromJson(json['description']), height: serializer.fromJson(json['height']), width: serializer.fromJson(json['width']), @@ -1093,57 +1434,57 @@ class RemoteExifEntityData extends i0.DataClass }; } - i1.RemoteExifEntityData copyWith( - {String? assetId, - i0.Value city = const i0.Value.absent(), - i0.Value state = const i0.Value.absent(), - i0.Value country = const i0.Value.absent(), - i0.Value dateTimeOriginal = const i0.Value.absent(), - i0.Value description = const i0.Value.absent(), - i0.Value height = const i0.Value.absent(), - i0.Value width = const i0.Value.absent(), - i0.Value exposureTime = const i0.Value.absent(), - i0.Value fNumber = const i0.Value.absent(), - i0.Value fileSize = const i0.Value.absent(), - i0.Value focalLength = const i0.Value.absent(), - i0.Value latitude = const i0.Value.absent(), - i0.Value longitude = const i0.Value.absent(), - i0.Value iso = const i0.Value.absent(), - i0.Value make = const i0.Value.absent(), - i0.Value model = const i0.Value.absent(), - i0.Value lens = const i0.Value.absent(), - i0.Value orientation = const i0.Value.absent(), - i0.Value timeZone = const i0.Value.absent(), - i0.Value rating = const i0.Value.absent(), - i0.Value projectionType = const i0.Value.absent()}) => - i1.RemoteExifEntityData( - assetId: assetId ?? this.assetId, - city: city.present ? city.value : this.city, - state: state.present ? state.value : this.state, - country: country.present ? country.value : this.country, - dateTimeOriginal: dateTimeOriginal.present - ? dateTimeOriginal.value - : this.dateTimeOriginal, - description: description.present ? description.value : this.description, - height: height.present ? height.value : this.height, - width: width.present ? width.value : this.width, - exposureTime: - exposureTime.present ? exposureTime.value : this.exposureTime, - fNumber: fNumber.present ? fNumber.value : this.fNumber, - fileSize: fileSize.present ? fileSize.value : this.fileSize, - focalLength: focalLength.present ? focalLength.value : this.focalLength, - latitude: latitude.present ? latitude.value : this.latitude, - longitude: longitude.present ? longitude.value : this.longitude, - iso: iso.present ? iso.value : this.iso, - make: make.present ? make.value : this.make, - model: model.present ? model.value : this.model, - lens: lens.present ? lens.value : this.lens, - orientation: orientation.present ? orientation.value : this.orientation, - timeZone: timeZone.present ? timeZone.value : this.timeZone, - rating: rating.present ? rating.value : this.rating, - projectionType: - projectionType.present ? projectionType.value : this.projectionType, - ); + i1.RemoteExifEntityData copyWith({ + String? assetId, + i0.Value city = const i0.Value.absent(), + i0.Value state = const i0.Value.absent(), + i0.Value country = const i0.Value.absent(), + i0.Value dateTimeOriginal = const i0.Value.absent(), + i0.Value description = const i0.Value.absent(), + i0.Value height = const i0.Value.absent(), + i0.Value width = const i0.Value.absent(), + i0.Value exposureTime = const i0.Value.absent(), + i0.Value fNumber = const i0.Value.absent(), + i0.Value fileSize = const i0.Value.absent(), + i0.Value focalLength = const i0.Value.absent(), + i0.Value latitude = const i0.Value.absent(), + i0.Value longitude = const i0.Value.absent(), + i0.Value iso = const i0.Value.absent(), + i0.Value make = const i0.Value.absent(), + i0.Value model = const i0.Value.absent(), + i0.Value lens = const i0.Value.absent(), + i0.Value orientation = const i0.Value.absent(), + i0.Value timeZone = const i0.Value.absent(), + i0.Value rating = const i0.Value.absent(), + i0.Value projectionType = const i0.Value.absent(), + }) => i1.RemoteExifEntityData( + assetId: assetId ?? this.assetId, + city: city.present ? city.value : this.city, + state: state.present ? state.value : this.state, + country: country.present ? country.value : this.country, + dateTimeOriginal: dateTimeOriginal.present + ? dateTimeOriginal.value + : this.dateTimeOriginal, + description: description.present ? description.value : this.description, + height: height.present ? height.value : this.height, + width: width.present ? width.value : this.width, + exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, + fNumber: fNumber.present ? fNumber.value : this.fNumber, + fileSize: fileSize.present ? fileSize.value : this.fileSize, + focalLength: focalLength.present ? focalLength.value : this.focalLength, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + iso: iso.present ? iso.value : this.iso, + make: make.present ? make.value : this.make, + model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, + orientation: orientation.present ? orientation.value : this.orientation, + timeZone: timeZone.present ? timeZone.value : this.timeZone, + rating: rating.present ? rating.value : this.rating, + projectionType: projectionType.present + ? projectionType.value + : this.projectionType, + ); RemoteExifEntityData copyWithCompanion(i1.RemoteExifEntityCompanion data) { return RemoteExifEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, @@ -1153,8 +1494,9 @@ class RemoteExifEntityData extends i0.DataClass dateTimeOriginal: data.dateTimeOriginal.present ? data.dateTimeOriginal.value : this.dateTimeOriginal, - description: - data.description.present ? data.description.value : this.description, + description: data.description.present + ? data.description.value + : this.description, height: data.height.present ? data.height.value : this.height, width: data.width.present ? data.width.value : this.width, exposureTime: data.exposureTime.present @@ -1162,16 +1504,18 @@ class RemoteExifEntityData extends i0.DataClass : this.exposureTime, fNumber: data.fNumber.present ? data.fNumber.value : this.fNumber, fileSize: data.fileSize.present ? data.fileSize.value : this.fileSize, - focalLength: - data.focalLength.present ? data.focalLength.value : this.focalLength, + focalLength: data.focalLength.present + ? data.focalLength.value + : this.focalLength, latitude: data.latitude.present ? data.latitude.value : this.latitude, longitude: data.longitude.present ? data.longitude.value : this.longitude, iso: data.iso.present ? data.iso.value : this.iso, make: data.make.present ? data.make.value : this.make, model: data.model.present ? data.model.value : this.model, lens: data.lens.present ? data.lens.value : this.lens, - orientation: - data.orientation.present ? data.orientation.value : this.orientation, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, rating: data.rating.present ? data.rating.value : this.rating, projectionType: data.projectionType.present @@ -1211,29 +1555,29 @@ class RemoteExifEntityData extends i0.DataClass @override int get hashCode => Object.hashAll([ - assetId, - city, - state, - country, - dateTimeOriginal, - description, - height, - width, - exposureTime, - fNumber, - fileSize, - focalLength, - latitude, - longitude, - iso, - make, - model, - lens, - orientation, - timeZone, - rating, - projectionType - ]); + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]); @override bool operator ==(Object other) => identical(this, other) || @@ -1384,29 +1728,30 @@ class RemoteExifEntityCompanion }); } - i1.RemoteExifEntityCompanion copyWith( - {i0.Value? assetId, - i0.Value? city, - i0.Value? state, - i0.Value? country, - i0.Value? dateTimeOriginal, - i0.Value? description, - i0.Value? height, - i0.Value? width, - i0.Value? exposureTime, - i0.Value? fNumber, - i0.Value? fileSize, - i0.Value? focalLength, - i0.Value? latitude, - i0.Value? longitude, - i0.Value? iso, - i0.Value? make, - i0.Value? model, - i0.Value? lens, - i0.Value? orientation, - i0.Value? timeZone, - i0.Value? rating, - i0.Value? projectionType}) { + i1.RemoteExifEntityCompanion copyWith({ + i0.Value? assetId, + i0.Value? city, + i0.Value? state, + i0.Value? country, + i0.Value? dateTimeOriginal, + i0.Value? description, + i0.Value? height, + i0.Value? width, + i0.Value? exposureTime, + i0.Value? fNumber, + i0.Value? fileSize, + i0.Value? focalLength, + i0.Value? latitude, + i0.Value? longitude, + i0.Value? iso, + i0.Value? make, + i0.Value? model, + i0.Value? lens, + i0.Value? orientation, + i0.Value? timeZone, + i0.Value? rating, + i0.Value? projectionType, + }) { return i1.RemoteExifEntityCompanion( assetId: assetId ?? this.assetId, city: city ?? this.city, diff --git a/mobile/lib/infrastructure/entities/exif.entity.g.dart b/mobile/lib/infrastructure/entities/exif.entity.g.dart index 989338abff..d2f9ebda27 100644 --- a/mobile/lib/infrastructure/entities/exif.entity.g.dart +++ b/mobile/lib/infrastructure/entities/exif.entity.g.dart @@ -17,16 +17,8 @@ const ExifInfoSchema = CollectionSchema( name: r'ExifInfo', id: -2409260054350835217, properties: { - r'city': PropertySchema( - id: 0, - name: r'city', - type: IsarType.string, - ), - r'country': PropertySchema( - id: 1, - name: r'country', - type: IsarType.string, - ), + r'city': PropertySchema(id: 0, name: r'city', type: IsarType.string), + r'country': PropertySchema(id: 1, name: r'country', type: IsarType.string), r'dateTimeOriginal': PropertySchema( id: 2, name: r'dateTimeOriginal', @@ -42,67 +34,28 @@ const ExifInfoSchema = CollectionSchema( name: r'exposureSeconds', type: IsarType.float, ), - r'f': PropertySchema( - id: 5, - name: r'f', - type: IsarType.float, - ), - r'fileSize': PropertySchema( - id: 6, - name: r'fileSize', - type: IsarType.long, - ), - r'iso': PropertySchema( - id: 7, - name: r'iso', - type: IsarType.int, - ), - r'lat': PropertySchema( - id: 8, - name: r'lat', - type: IsarType.float, - ), - r'lens': PropertySchema( - id: 9, - name: r'lens', - type: IsarType.string, - ), - r'long': PropertySchema( - id: 10, - name: r'long', - type: IsarType.float, - ), - r'make': PropertySchema( - id: 11, - name: r'make', - type: IsarType.string, - ), - r'mm': PropertySchema( - id: 12, - name: r'mm', - type: IsarType.float, - ), - r'model': PropertySchema( - id: 13, - name: r'model', - type: IsarType.string, - ), + r'f': PropertySchema(id: 5, name: r'f', type: IsarType.float), + r'fileSize': PropertySchema(id: 6, name: r'fileSize', type: IsarType.long), + r'iso': PropertySchema(id: 7, name: r'iso', type: IsarType.int), + r'lat': PropertySchema(id: 8, name: r'lat', type: IsarType.float), + r'lens': PropertySchema(id: 9, name: r'lens', type: IsarType.string), + r'long': PropertySchema(id: 10, name: r'long', type: IsarType.float), + r'make': PropertySchema(id: 11, name: r'make', type: IsarType.string), + r'mm': PropertySchema(id: 12, name: r'mm', type: IsarType.float), + r'model': PropertySchema(id: 13, name: r'model', type: IsarType.string), r'orientation': PropertySchema( id: 14, name: r'orientation', type: IsarType.string, ), - r'state': PropertySchema( - id: 15, - name: r'state', - type: IsarType.string, - ), + r'state': PropertySchema(id: 15, name: r'state', type: IsarType.string), r'timeZone': PropertySchema( id: 16, name: r'timeZone', type: IsarType.string, - ) + ), }, + estimateSize: _exifInfoEstimateSize, serialize: _exifInfoSerialize, deserialize: _exifInfoDeserialize, @@ -111,6 +64,7 @@ const ExifInfoSchema = CollectionSchema( indexes: {}, links: {}, embeddedSchemas: {}, + getId: _exifInfoGetId, getLinks: _exifInfoGetLinks, attach: _exifInfoAttach, @@ -301,10 +255,7 @@ extension ExifInfoQueryWhereSort on QueryBuilder { extension ExifInfoQueryWhere on QueryBuilder { QueryBuilder idEqualTo(Id id) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: id, - upper: id, - )); + return query.addWhereClause(IdWhereClause.between(lower: id, upper: id)); }); } @@ -330,8 +281,10 @@ extension ExifInfoQueryWhere on QueryBuilder { }); } - QueryBuilder idGreaterThan(Id id, - {bool include = false}) { + QueryBuilder idGreaterThan( + Id id, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: id, includeLower: include), @@ -339,8 +292,10 @@ extension ExifInfoQueryWhere on QueryBuilder { }); } - QueryBuilder idLessThan(Id id, - {bool include = false}) { + QueryBuilder idLessThan( + Id id, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: id, includeUpper: include), @@ -355,12 +310,14 @@ extension ExifInfoQueryWhere on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerId, - includeLower: includeLower, - upper: upperId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + ), + ); }); } } @@ -369,17 +326,17 @@ extension ExifInfoQueryFilter on QueryBuilder { QueryBuilder cityIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'city', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'city'), + ); }); } QueryBuilder cityIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'city', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'city'), + ); }); } @@ -388,11 +345,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'city', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'city', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -402,12 +361,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'city', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'city', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -417,12 +378,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'city', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'city', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -434,14 +397,16 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'city', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'city', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -450,11 +415,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'city', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'city', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -463,69 +430,75 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'city', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'city', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder cityContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'city', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'city', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder cityMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'city', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'city', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder cityIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'city', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'city', value: ''), + ); }); } QueryBuilder cityIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'city', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'city', value: ''), + ); }); } QueryBuilder countryIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'country', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'country'), + ); }); } QueryBuilder countryIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'country', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'country'), + ); }); } @@ -534,11 +507,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'country', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'country', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -548,12 +523,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'country', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'country', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -563,12 +540,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'country', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'country', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -580,14 +559,16 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'country', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'country', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -596,11 +577,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'country', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'country', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -609,144 +592,149 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'country', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'country', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder countryContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'country', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'country', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder countryMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'country', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'country', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder countryIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'country', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'country', value: ''), + ); }); } QueryBuilder countryIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'country', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'country', value: ''), + ); }); } QueryBuilder - dateTimeOriginalIsNull() { + dateTimeOriginalIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'dateTimeOriginal', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'dateTimeOriginal'), + ); }); } QueryBuilder - dateTimeOriginalIsNotNull() { + dateTimeOriginalIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'dateTimeOriginal', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'dateTimeOriginal'), + ); }); } QueryBuilder - dateTimeOriginalEqualTo(DateTime? value) { + dateTimeOriginalEqualTo(DateTime? value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'dateTimeOriginal', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'dateTimeOriginal', value: value), + ); }); } QueryBuilder - dateTimeOriginalGreaterThan( - DateTime? value, { - bool include = false, - }) { + dateTimeOriginalGreaterThan(DateTime? value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'dateTimeOriginal', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'dateTimeOriginal', + value: value, + ), + ); }); } QueryBuilder - dateTimeOriginalLessThan( - DateTime? value, { - bool include = false, - }) { + dateTimeOriginalLessThan(DateTime? value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'dateTimeOriginal', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'dateTimeOriginal', + value: value, + ), + ); }); } QueryBuilder - dateTimeOriginalBetween( + dateTimeOriginalBetween( DateTime? lower, DateTime? upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'dateTimeOriginal', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'dateTimeOriginal', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder descriptionIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'description', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'description'), + ); }); } QueryBuilder - descriptionIsNotNull() { + descriptionIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'description', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'description'), + ); }); } @@ -755,27 +743,31 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - descriptionGreaterThan( + descriptionGreaterThan( String? value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -785,12 +777,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -802,14 +796,16 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'description', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'description', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -818,11 +814,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -831,123 +829,135 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder descriptionContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'description', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'description', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder descriptionMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'description', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'description', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder descriptionIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'description', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'description', value: ''), + ); }); } QueryBuilder - descriptionIsNotEmpty() { + descriptionIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'description', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'description', value: ''), + ); }); } QueryBuilder - exposureSecondsIsNull() { + exposureSecondsIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'exposureSeconds', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'exposureSeconds'), + ); }); } QueryBuilder - exposureSecondsIsNotNull() { + exposureSecondsIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'exposureSeconds', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'exposureSeconds'), + ); }); } QueryBuilder - exposureSecondsEqualTo( - double? value, { - double epsilon = Query.epsilon, - }) { + exposureSecondsEqualTo(double? value, {double epsilon = Query.epsilon}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'exposureSeconds', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'exposureSeconds', + value: value, + + epsilon: epsilon, + ), + ); }); } QueryBuilder - exposureSecondsGreaterThan( + exposureSecondsGreaterThan( double? value, { bool include = false, double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'exposureSeconds', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'exposureSeconds', + value: value, + + epsilon: epsilon, + ), + ); }); } QueryBuilder - exposureSecondsLessThan( + exposureSecondsLessThan( double? value, { bool include = false, double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'exposureSeconds', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'exposureSeconds', + value: value, + + epsilon: epsilon, + ), + ); }); } QueryBuilder - exposureSecondsBetween( + exposureSecondsBetween( double? lower, double? upper, { bool includeLower = true, @@ -955,30 +965,33 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'exposureSeconds', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'exposureSeconds', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + + epsilon: epsilon, + ), + ); }); } QueryBuilder fIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'f', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'f'), + ); }); } QueryBuilder fIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'f', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'f'), + ); }); } @@ -987,11 +1000,9 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'f', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'f', value: value, epsilon: epsilon), + ); }); } @@ -1001,12 +1012,15 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'f', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'f', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1016,12 +1030,15 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'f', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'f', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1033,40 +1050,43 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'f', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'f', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + + epsilon: epsilon, + ), + ); }); } QueryBuilder fileSizeIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'fileSize', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'fileSize'), + ); }); } QueryBuilder fileSizeIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'fileSize', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'fileSize'), + ); }); } QueryBuilder fileSizeEqualTo( - int? value) { + int? value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'fileSize', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'fileSize', value: value), + ); }); } @@ -1075,11 +1095,13 @@ extension ExifInfoQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'fileSize', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'fileSize', + value: value, + ), + ); }); } @@ -1088,11 +1110,13 @@ extension ExifInfoQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'fileSize', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'fileSize', + value: value, + ), + ); }); } @@ -1103,38 +1127,39 @@ extension ExifInfoQueryFilter bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'fileSize', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'fileSize', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder idIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'id', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'id'), + ); }); } QueryBuilder idIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'id', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'id'), + ); }); } QueryBuilder idEqualTo(Id? value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: value), + ); }); } @@ -1143,11 +1168,13 @@ extension ExifInfoQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + ), + ); }); } @@ -1156,11 +1183,13 @@ extension ExifInfoQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + ), + ); }); } @@ -1171,39 +1200,41 @@ extension ExifInfoQueryFilter bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder isoIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'iso', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'iso'), + ); }); } QueryBuilder isoIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'iso', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'iso'), + ); }); } QueryBuilder isoEqualTo( - int? value) { + int? value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'iso', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'iso', value: value), + ); }); } @@ -1212,11 +1243,13 @@ extension ExifInfoQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'iso', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'iso', + value: value, + ), + ); }); } @@ -1225,11 +1258,13 @@ extension ExifInfoQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'iso', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'iso', + value: value, + ), + ); }); } @@ -1240,29 +1275,31 @@ extension ExifInfoQueryFilter bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'iso', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'iso', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder latIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'lat', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'lat'), + ); }); } QueryBuilder latIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'lat', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'lat'), + ); }); } @@ -1271,11 +1308,14 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'lat', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'lat', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1285,12 +1325,15 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'lat', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'lat', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1300,12 +1343,15 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'lat', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'lat', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1317,30 +1363,33 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'lat', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'lat', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + + epsilon: epsilon, + ), + ); }); } QueryBuilder lensIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'lens', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'lens'), + ); }); } QueryBuilder lensIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'lens', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'lens'), + ); }); } @@ -1349,11 +1398,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'lens', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'lens', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1363,12 +1414,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'lens', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'lens', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1378,12 +1431,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'lens', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'lens', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1395,14 +1450,16 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'lens', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'lens', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1411,11 +1468,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'lens', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'lens', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1424,69 +1483,75 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'lens', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'lens', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder lensContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'lens', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'lens', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder lensMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'lens', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'lens', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder lensIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'lens', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'lens', value: ''), + ); }); } QueryBuilder lensIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'lens', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'lens', value: ''), + ); }); } QueryBuilder longIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'long', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'long'), + ); }); } QueryBuilder longIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'long', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'long'), + ); }); } @@ -1495,11 +1560,14 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'long', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'long', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1509,12 +1577,15 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'long', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'long', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1524,12 +1595,15 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'long', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'long', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1541,30 +1615,33 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'long', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'long', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + + epsilon: epsilon, + ), + ); }); } QueryBuilder makeIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'make', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'make'), + ); }); } QueryBuilder makeIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'make', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'make'), + ); }); } @@ -1573,11 +1650,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'make', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'make', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1587,12 +1666,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'make', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'make', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1602,12 +1683,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'make', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'make', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1619,14 +1702,16 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'make', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'make', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1635,11 +1720,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'make', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'make', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1648,69 +1735,75 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'make', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'make', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder makeContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'make', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'make', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder makeMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'make', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'make', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder makeIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'make', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'make', value: ''), + ); }); } QueryBuilder makeIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'make', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'make', value: ''), + ); }); } QueryBuilder mmIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'mm', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'mm'), + ); }); } QueryBuilder mmIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'mm', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'mm'), + ); }); } @@ -1719,11 +1812,14 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'mm', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'mm', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1733,12 +1829,15 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'mm', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'mm', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1748,12 +1847,15 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'mm', - value: value, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'mm', + value: value, + + epsilon: epsilon, + ), + ); }); } @@ -1765,30 +1867,33 @@ extension ExifInfoQueryFilter double epsilon = Query.epsilon, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'mm', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - epsilon: epsilon, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'mm', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + + epsilon: epsilon, + ), + ); }); } QueryBuilder modelIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'model', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'model'), + ); }); } QueryBuilder modelIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'model', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'model'), + ); }); } @@ -1797,11 +1902,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'model', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'model', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1811,12 +1918,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'model', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'model', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1826,12 +1935,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'model', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'model', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1843,14 +1954,16 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'model', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'model', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1859,11 +1972,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'model', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'model', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1872,70 +1987,76 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'model', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'model', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder modelContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'model', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'model', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder modelMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'model', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'model', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder modelIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'model', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'model', value: ''), + ); }); } QueryBuilder modelIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'model', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'model', value: ''), + ); }); } QueryBuilder orientationIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'orientation', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'orientation'), + ); }); } QueryBuilder - orientationIsNotNull() { + orientationIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'orientation', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'orientation'), + ); }); } @@ -1944,27 +2065,31 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'orientation', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'orientation', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - orientationGreaterThan( + orientationGreaterThan( String? value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'orientation', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'orientation', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1974,12 +2099,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'orientation', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'orientation', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1991,14 +2118,16 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'orientation', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'orientation', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2007,11 +2136,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'orientation', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'orientation', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2020,70 +2151,76 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'orientation', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'orientation', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder orientationContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'orientation', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'orientation', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder orientationMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'orientation', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'orientation', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder orientationIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'orientation', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'orientation', value: ''), + ); }); } QueryBuilder - orientationIsNotEmpty() { + orientationIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'orientation', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'orientation', value: ''), + ); }); } QueryBuilder stateIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'state', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'state'), + ); }); } QueryBuilder stateIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'state', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'state'), + ); }); } @@ -2092,11 +2229,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'state', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'state', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2106,12 +2245,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'state', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'state', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2121,12 +2262,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'state', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'state', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2138,14 +2281,16 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'state', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'state', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2154,11 +2299,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'state', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'state', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2167,69 +2314,75 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'state', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'state', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder stateContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'state', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'state', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder stateMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'state', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'state', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder stateIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'state', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'state', value: ''), + ); }); } QueryBuilder stateIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'state', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'state', value: ''), + ); }); } QueryBuilder timeZoneIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'timeZone', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'timeZone'), + ); }); } QueryBuilder timeZoneIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'timeZone', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'timeZone'), + ); }); } @@ -2238,11 +2391,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'timeZone', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'timeZone', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2252,12 +2407,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'timeZone', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'timeZone', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2267,12 +2424,14 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'timeZone', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'timeZone', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2284,14 +2443,16 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'timeZone', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'timeZone', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2300,11 +2461,13 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'timeZone', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'timeZone', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -2313,53 +2476,59 @@ extension ExifInfoQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'timeZone', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'timeZone', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder timeZoneContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'timeZone', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'timeZone', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder timeZoneMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'timeZone', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'timeZone', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder timeZoneIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'timeZone', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'timeZone', value: ''), + ); }); } QueryBuilder timeZoneIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'timeZone', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'timeZone', value: ''), + ); }); } } @@ -2797,15 +2966,17 @@ extension ExifInfoQuerySortThenBy extension ExifInfoQueryWhereDistinct on QueryBuilder { - QueryBuilder distinctByCity( - {bool caseSensitive = true}) { + QueryBuilder distinctByCity({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'city', caseSensitive: caseSensitive); }); } - QueryBuilder distinctByCountry( - {bool caseSensitive = true}) { + QueryBuilder distinctByCountry({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'country', caseSensitive: caseSensitive); }); @@ -2817,8 +2988,9 @@ extension ExifInfoQueryWhereDistinct }); } - QueryBuilder distinctByDescription( - {bool caseSensitive = true}) { + QueryBuilder distinctByDescription({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'description', caseSensitive: caseSensitive); }); @@ -2854,8 +3026,9 @@ extension ExifInfoQueryWhereDistinct }); } - QueryBuilder distinctByLens( - {bool caseSensitive = true}) { + QueryBuilder distinctByLens({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'lens', caseSensitive: caseSensitive); }); @@ -2867,8 +3040,9 @@ extension ExifInfoQueryWhereDistinct }); } - QueryBuilder distinctByMake( - {bool caseSensitive = true}) { + QueryBuilder distinctByMake({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'make', caseSensitive: caseSensitive); }); @@ -2880,29 +3054,33 @@ extension ExifInfoQueryWhereDistinct }); } - QueryBuilder distinctByModel( - {bool caseSensitive = true}) { + QueryBuilder distinctByModel({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'model', caseSensitive: caseSensitive); }); } - QueryBuilder distinctByOrientation( - {bool caseSensitive = true}) { + QueryBuilder distinctByOrientation({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'orientation', caseSensitive: caseSensitive); }); } - QueryBuilder distinctByState( - {bool caseSensitive = true}) { + QueryBuilder distinctByState({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'state', caseSensitive: caseSensitive); }); } - QueryBuilder distinctByTimeZone( - {bool caseSensitive = true}) { + QueryBuilder distinctByTimeZone({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'timeZone', caseSensitive: caseSensitive); }); @@ -2930,7 +3108,7 @@ extension ExifInfoQueryProperty } QueryBuilder - dateTimeOriginalProperty() { + dateTimeOriginalProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'dateTimeOriginal'); }); diff --git a/mobile/lib/infrastructure/entities/local_album.entity.dart b/mobile/lib/infrastructure/entities/local_album.entity.dart index 398c5d4e44..c796a12956 100644 --- a/mobile/lib/infrastructure/entities/local_album.entity.dart +++ b/mobile/lib/infrastructure/entities/local_album.entity.dart @@ -9,8 +9,7 @@ class LocalAlbumEntity extends Table with DriftDefaultsMixin { TextColumn get name => text()(); DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)(); IntColumn get backupSelection => intEnum()(); - BoolColumn get isIosSharedAlbum => - boolean().withDefault(const Constant(false))(); + BoolColumn get isIosSharedAlbum => boolean().withDefault(const Constant(false))(); // Used for mark & sweep BoolColumn get marker_ => boolean().nullable()(); diff --git a/mobile/lib/infrastructure/entities/local_album.entity.drift.dart b/mobile/lib/infrastructure/entities/local_album.entity.drift.dart index 06f65e25d8..5be349c8e0 100644 --- a/mobile/lib/infrastructure/entities/local_album.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/local_album.entity.drift.dart @@ -8,24 +8,24 @@ import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart' as i3; import 'package:drift/src/runtime/query_builder/query_builder.dart' as i4; -typedef $$LocalAlbumEntityTableCreateCompanionBuilder - = i1.LocalAlbumEntityCompanion Function({ - required String id, - required String name, - i0.Value updatedAt, - required i2.BackupSelection backupSelection, - i0.Value isIosSharedAlbum, - i0.Value marker_, -}); -typedef $$LocalAlbumEntityTableUpdateCompanionBuilder - = i1.LocalAlbumEntityCompanion Function({ - i0.Value id, - i0.Value name, - i0.Value updatedAt, - i0.Value backupSelection, - i0.Value isIosSharedAlbum, - i0.Value marker_, -}); +typedef $$LocalAlbumEntityTableCreateCompanionBuilder = + i1.LocalAlbumEntityCompanion Function({ + required String id, + required String name, + i0.Value updatedAt, + required i2.BackupSelection backupSelection, + i0.Value isIosSharedAlbum, + i0.Value marker_, + }); +typedef $$LocalAlbumEntityTableUpdateCompanionBuilder = + i1.LocalAlbumEntityCompanion Function({ + i0.Value id, + i0.Value name, + i0.Value updatedAt, + i0.Value backupSelection, + i0.Value isIosSharedAlbum, + i0.Value marker_, + }); class $$LocalAlbumEntityTableFilterComposer extends i0.Composer { @@ -37,25 +37,35 @@ class $$LocalAlbumEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnFilters(column)); + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnFilters(column)); + column: $table.name, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnWithTypeConverterFilters - get backupSelection => $composableBuilder( - column: $table.backupSelection, - builder: (column) => i0.ColumnWithTypeConverterFilters(column)); + get backupSelection => $composableBuilder( + column: $table.backupSelection, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); i0.ColumnFilters get isIosSharedAlbum => $composableBuilder( - column: $table.isIosSharedAlbum, - builder: (column) => i0.ColumnFilters(column)); + column: $table.isIosSharedAlbum, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get marker_ => $composableBuilder( - column: $table.marker_, builder: (column) => i0.ColumnFilters(column)); + column: $table.marker_, + builder: (column) => i0.ColumnFilters(column), + ); } class $$LocalAlbumEntityTableOrderingComposer @@ -68,25 +78,34 @@ class $$LocalAlbumEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnOrderings(column)); + column: $table.name, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get updatedAt => $composableBuilder( - column: $table.updatedAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get backupSelection => $composableBuilder( - column: $table.backupSelection, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.backupSelection, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get isIosSharedAlbum => $composableBuilder( - column: $table.isIosSharedAlbum, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.isIosSharedAlbum, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get marker_ => $composableBuilder( - column: $table.marker_, builder: (column) => i0.ColumnOrderings(column)); + column: $table.marker_, + builder: (column) => i0.ColumnOrderings(column), + ); } class $$LocalAlbumEntityTableAnnotationComposer @@ -108,35 +127,47 @@ class $$LocalAlbumEntityTableAnnotationComposer $composableBuilder(column: $table.updatedAt, builder: (column) => column); i0.GeneratedColumnWithTypeConverter - get backupSelection => $composableBuilder( - column: $table.backupSelection, builder: (column) => column); + get backupSelection => $composableBuilder( + column: $table.backupSelection, + builder: (column) => column, + ); i0.GeneratedColumn get isIosSharedAlbum => $composableBuilder( - column: $table.isIosSharedAlbum, builder: (column) => column); + column: $table.isIosSharedAlbum, + builder: (column) => column, + ); i0.GeneratedColumn get marker_ => $composableBuilder(column: $table.marker_, builder: (column) => column); } -class $$LocalAlbumEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$LocalAlbumEntityTable, - i1.LocalAlbumEntityData, - i1.$$LocalAlbumEntityTableFilterComposer, - i1.$$LocalAlbumEntityTableOrderingComposer, - i1.$$LocalAlbumEntityTableAnnotationComposer, - $$LocalAlbumEntityTableCreateCompanionBuilder, - $$LocalAlbumEntityTableUpdateCompanionBuilder, - ( - i1.LocalAlbumEntityData, - i0.BaseReferences - ), - i1.LocalAlbumEntityData, - i0.PrefetchHooks Function()> { +class $$LocalAlbumEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$LocalAlbumEntityTable, + i1.LocalAlbumEntityData, + i1.$$LocalAlbumEntityTableFilterComposer, + i1.$$LocalAlbumEntityTableOrderingComposer, + i1.$$LocalAlbumEntityTableAnnotationComposer, + $$LocalAlbumEntityTableCreateCompanionBuilder, + $$LocalAlbumEntityTableUpdateCompanionBuilder, + ( + i1.LocalAlbumEntityData, + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$LocalAlbumEntityTable, + i1.LocalAlbumEntityData + >, + ), + i1.LocalAlbumEntityData, + i0.PrefetchHooks Function() + > { $$LocalAlbumEntityTableTableManager( - i0.GeneratedDatabase db, i1.$LocalAlbumEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$LocalAlbumEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -145,63 +176,71 @@ class $$LocalAlbumEntityTableTableManager extends i0.RootTableManager< .$$LocalAlbumEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$LocalAlbumEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value id = const i0.Value.absent(), - i0.Value name = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value backupSelection = - const i0.Value.absent(), - i0.Value isIosSharedAlbum = const i0.Value.absent(), - i0.Value marker_ = const i0.Value.absent(), - }) => - i1.LocalAlbumEntityCompanion( - id: id, - name: name, - updatedAt: updatedAt, - backupSelection: backupSelection, - isIosSharedAlbum: isIosSharedAlbum, - marker_: marker_, - ), - createCompanionCallback: ({ - required String id, - required String name, - i0.Value updatedAt = const i0.Value.absent(), - required i2.BackupSelection backupSelection, - i0.Value isIosSharedAlbum = const i0.Value.absent(), - i0.Value marker_ = const i0.Value.absent(), - }) => - i1.LocalAlbumEntityCompanion.insert( - id: id, - name: name, - updatedAt: updatedAt, - backupSelection: backupSelection, - isIosSharedAlbum: isIosSharedAlbum, - marker_: marker_, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value id = const i0.Value.absent(), + i0.Value name = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value backupSelection = + const i0.Value.absent(), + i0.Value isIosSharedAlbum = const i0.Value.absent(), + i0.Value marker_ = const i0.Value.absent(), + }) => i1.LocalAlbumEntityCompanion( + id: id, + name: name, + updatedAt: updatedAt, + backupSelection: backupSelection, + isIosSharedAlbum: isIosSharedAlbum, + marker_: marker_, + ), + createCompanionCallback: + ({ + required String id, + required String name, + i0.Value updatedAt = const i0.Value.absent(), + required i2.BackupSelection backupSelection, + i0.Value isIosSharedAlbum = const i0.Value.absent(), + i0.Value marker_ = const i0.Value.absent(), + }) => i1.LocalAlbumEntityCompanion.insert( + id: id, + name: name, + updatedAt: updatedAt, + backupSelection: backupSelection, + isIosSharedAlbum: isIosSharedAlbum, + marker_: marker_, + ), withReferenceMapper: (p0) => p0 .map((e) => (e.readTable(table), i0.BaseReferences(db, table, e))) .toList(), prefetchHooksCallback: null, - )); + ), + ); } -typedef $$LocalAlbumEntityTableProcessedTableManager = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$LocalAlbumEntityTable, - i1.LocalAlbumEntityData, - i1.$$LocalAlbumEntityTableFilterComposer, - i1.$$LocalAlbumEntityTableOrderingComposer, - i1.$$LocalAlbumEntityTableAnnotationComposer, - $$LocalAlbumEntityTableCreateCompanionBuilder, - $$LocalAlbumEntityTableUpdateCompanionBuilder, - ( +typedef $$LocalAlbumEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$LocalAlbumEntityTable, i1.LocalAlbumEntityData, - i0.BaseReferences - ), - i1.LocalAlbumEntityData, - i0.PrefetchHooks Function()>; + i1.$$LocalAlbumEntityTableFilterComposer, + i1.$$LocalAlbumEntityTableOrderingComposer, + i1.$$LocalAlbumEntityTableAnnotationComposer, + $$LocalAlbumEntityTableCreateCompanionBuilder, + $$LocalAlbumEntityTableUpdateCompanionBuilder, + ( + i1.LocalAlbumEntityData, + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$LocalAlbumEntityTable, + i1.LocalAlbumEntityData + >, + ), + i1.LocalAlbumEntityData, + i0.PrefetchHooks Function() + >; class $LocalAlbumEntityTable extends i3.LocalAlbumEntity with i0.TableInfo<$LocalAlbumEntityTable, i1.LocalAlbumEntityData> { @@ -212,51 +251,86 @@ class $LocalAlbumEntityTable extends i3.LocalAlbumEntity static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); @override late final i0.GeneratedColumn id = i0.GeneratedColumn( - 'id', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _nameMeta = - const i0.VerificationMeta('name'); + 'id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _nameMeta = const i0.VerificationMeta( + 'name', + ); @override late final i0.GeneratedColumn name = i0.GeneratedColumn( - 'name', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _updatedAtMeta = - const i0.VerificationMeta('updatedAt'); + 'name', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _updatedAtMeta = const i0.VerificationMeta( + 'updatedAt', + ); @override late final i0.GeneratedColumn updatedAt = - i0.GeneratedColumn('updated_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i4.currentDateAndTime); + i0.GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); @override late final i0.GeneratedColumnWithTypeConverter - backupSelection = i0.GeneratedColumn( - 'backup_selection', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true) - .withConverter( - i1.$LocalAlbumEntityTable.$converterbackupSelection); + backupSelection = + i0.GeneratedColumn( + 'backup_selection', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ).withConverter( + i1.$LocalAlbumEntityTable.$converterbackupSelection, + ); static const i0.VerificationMeta _isIosSharedAlbumMeta = const i0.VerificationMeta('isIosSharedAlbum'); @override late final i0.GeneratedColumn isIosSharedAlbum = - i0.GeneratedColumn('is_ios_shared_album', aliasedName, false, - type: i0.DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'CHECK ("is_ios_shared_album" IN (0, 1))'), - defaultValue: const i4.Constant(false)); - static const i0.VerificationMeta _marker_Meta = - const i0.VerificationMeta('marker_'); + i0.GeneratedColumn( + 'is_ios_shared_album', + aliasedName, + false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const i4.Constant(false), + ); + static const i0.VerificationMeta _marker_Meta = const i0.VerificationMeta( + 'marker_', + ); @override late final i0.GeneratedColumn marker_ = i0.GeneratedColumn( - 'marker', aliasedName, true, - type: i0.DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - i0.GeneratedColumn.constraintIsAlways('CHECK ("marker" IN (0, 1))')); + 'marker', + aliasedName, + true, + type: i0.DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); @override - List get $columns => - [id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_]; + List get $columns => [ + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -264,8 +338,9 @@ class $LocalAlbumEntityTable extends i3.LocalAlbumEntity static const String $name = 'local_album_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('id')) { @@ -275,23 +350,32 @@ class $LocalAlbumEntityTable extends i3.LocalAlbumEntity } if (data.containsKey('name')) { context.handle( - _nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta)); + _nameMeta, + name.isAcceptableOrUnknown(data['name']!, _nameMeta), + ); } else if (isInserting) { context.missing(_nameMeta); } if (data.containsKey('updated_at')) { - context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); + context.handle( + _updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), + ); } if (data.containsKey('is_ios_shared_album')) { context.handle( + _isIosSharedAlbumMeta, + isIosSharedAlbum.isAcceptableOrUnknown( + data['is_ios_shared_album']!, _isIosSharedAlbumMeta, - isIosSharedAlbum.isAcceptableOrUnknown( - data['is_ios_shared_album']!, _isIosSharedAlbumMeta)); + ), + ); } if (data.containsKey('marker')) { - context.handle(_marker_Meta, - marker_.isAcceptableOrUnknown(data['marker']!, _marker_Meta)); + context.handle( + _marker_Meta, + marker_.isAcceptableOrUnknown(data['marker']!, _marker_Meta), + ); } return context; } @@ -299,23 +383,39 @@ class $LocalAlbumEntityTable extends i3.LocalAlbumEntity @override Set get $primaryKey => {id}; @override - i1.LocalAlbumEntityData map(Map data, - {String? tablePrefix}) { + i1.LocalAlbumEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.LocalAlbumEntityData( - id: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!, + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}name'], + )!, updatedAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, backupSelection: i1.$LocalAlbumEntityTable.$converterbackupSelection - .fromSql(attachedDatabase.typeMapping.read(i0.DriftSqlType.int, - data['${effectivePrefix}backup_selection'])!), + .fromSql( + attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}backup_selection'], + )!, + ), isIosSharedAlbum: attachedDatabase.typeMapping.read( - i0.DriftSqlType.bool, data['${effectivePrefix}is_ios_shared_album'])!, - marker_: attachedDatabase.typeMapping - .read(i0.DriftSqlType.bool, data['${effectivePrefix}marker']), + i0.DriftSqlType.bool, + data['${effectivePrefix}is_ios_shared_album'], + )!, + marker_: attachedDatabase.typeMapping.read( + i0.DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), ); } @@ -325,9 +425,9 @@ class $LocalAlbumEntityTable extends i3.LocalAlbumEntity } static i0.JsonTypeConverter2 - $converterbackupSelection = - const i0.EnumIndexConverter( - i2.BackupSelection.values); + $converterbackupSelection = const i0.EnumIndexConverter( + i2.BackupSelection.values, + ); @override bool get withoutRowId => true; @override @@ -342,13 +442,14 @@ class LocalAlbumEntityData extends i0.DataClass final i2.BackupSelection backupSelection; final bool isIosSharedAlbum; final bool? marker_; - const LocalAlbumEntityData( - {required this.id, - required this.name, - required this.updatedAt, - required this.backupSelection, - required this.isIosSharedAlbum, - this.marker_}); + const LocalAlbumEntityData({ + required this.id, + required this.name, + required this.updatedAt, + required this.backupSelection, + required this.isIosSharedAlbum, + this.marker_, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -356,9 +457,11 @@ class LocalAlbumEntityData extends i0.DataClass map['name'] = i0.Variable(name); map['updated_at'] = i0.Variable(updatedAt); { - map['backup_selection'] = i0.Variable(i1 - .$LocalAlbumEntityTable.$converterbackupSelection - .toSql(backupSelection)); + map['backup_selection'] = i0.Variable( + i1.$LocalAlbumEntityTable.$converterbackupSelection.toSql( + backupSelection, + ), + ); } map['is_ios_shared_album'] = i0.Variable(isIosSharedAlbum); if (!nullToAbsent || marker_ != null) { @@ -367,8 +470,10 @@ class LocalAlbumEntityData extends i0.DataClass return map; } - factory LocalAlbumEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory LocalAlbumEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return LocalAlbumEntityData( id: serializer.fromJson(json['id']), @@ -387,29 +492,31 @@ class LocalAlbumEntityData extends i0.DataClass 'id': serializer.toJson(id), 'name': serializer.toJson(name), 'updatedAt': serializer.toJson(updatedAt), - 'backupSelection': serializer.toJson(i1 - .$LocalAlbumEntityTable.$converterbackupSelection - .toJson(backupSelection)), + 'backupSelection': serializer.toJson( + i1.$LocalAlbumEntityTable.$converterbackupSelection.toJson( + backupSelection, + ), + ), 'isIosSharedAlbum': serializer.toJson(isIosSharedAlbum), 'marker_': serializer.toJson(marker_), }; } - i1.LocalAlbumEntityData copyWith( - {String? id, - String? name, - DateTime? updatedAt, - i2.BackupSelection? backupSelection, - bool? isIosSharedAlbum, - i0.Value marker_ = const i0.Value.absent()}) => - i1.LocalAlbumEntityData( - id: id ?? this.id, - name: name ?? this.name, - updatedAt: updatedAt ?? this.updatedAt, - backupSelection: backupSelection ?? this.backupSelection, - isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, - marker_: marker_.present ? marker_.value : this.marker_, - ); + i1.LocalAlbumEntityData copyWith({ + String? id, + String? name, + DateTime? updatedAt, + i2.BackupSelection? backupSelection, + bool? isIosSharedAlbum, + i0.Value marker_ = const i0.Value.absent(), + }) => i1.LocalAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_.present ? marker_.value : this.marker_, + ); LocalAlbumEntityData copyWithCompanion(i1.LocalAlbumEntityCompanion data) { return LocalAlbumEntityData( id: data.id.present ? data.id.value : this.id, @@ -440,7 +547,13 @@ class LocalAlbumEntityData extends i0.DataClass @override int get hashCode => Object.hash( - id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_); + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -476,9 +589,9 @@ class LocalAlbumEntityCompanion required i2.BackupSelection backupSelection, this.isIosSharedAlbum = const i0.Value.absent(), this.marker_ = const i0.Value.absent(), - }) : id = i0.Value(id), - name = i0.Value(name), - backupSelection = i0.Value(backupSelection); + }) : id = i0.Value(id), + name = i0.Value(name), + backupSelection = i0.Value(backupSelection); static i0.Insertable custom({ i0.Expression? id, i0.Expression? name, @@ -497,13 +610,14 @@ class LocalAlbumEntityCompanion }); } - i1.LocalAlbumEntityCompanion copyWith( - {i0.Value? id, - i0.Value? name, - i0.Value? updatedAt, - i0.Value? backupSelection, - i0.Value? isIosSharedAlbum, - i0.Value? marker_}) { + i1.LocalAlbumEntityCompanion copyWith({ + i0.Value? id, + i0.Value? name, + i0.Value? updatedAt, + i0.Value? backupSelection, + i0.Value? isIosSharedAlbum, + i0.Value? marker_, + }) { return i1.LocalAlbumEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -527,9 +641,11 @@ class LocalAlbumEntityCompanion map['updated_at'] = i0.Variable(updatedAt.value); } if (backupSelection.present) { - map['backup_selection'] = i0.Variable(i1 - .$LocalAlbumEntityTable.$converterbackupSelection - .toSql(backupSelection.value)); + map['backup_selection'] = i0.Variable( + i1.$LocalAlbumEntityTable.$converterbackupSelection.toSql( + backupSelection.value, + ), + ); } if (isIosSharedAlbum.present) { map['is_ios_shared_album'] = i0.Variable(isIosSharedAlbum.value); diff --git a/mobile/lib/infrastructure/entities/local_album_asset.entity.dart b/mobile/lib/infrastructure/entities/local_album_asset.entity.dart index b64b9ec2fb..8de879a09d 100644 --- a/mobile/lib/infrastructure/entities/local_album_asset.entity.dart +++ b/mobile/lib/infrastructure/entities/local_album_asset.entity.dart @@ -6,11 +6,9 @@ import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; class LocalAlbumAssetEntity extends Table with DriftDefaultsMixin { const LocalAlbumAssetEntity(); - TextColumn get assetId => - text().references(LocalAssetEntity, #id, onDelete: KeyAction.cascade)(); + TextColumn get assetId => text().references(LocalAssetEntity, #id, onDelete: KeyAction.cascade)(); - TextColumn get albumId => - text().references(LocalAlbumEntity, #id, onDelete: KeyAction.cascade)(); + TextColumn get albumId => text().references(LocalAlbumEntity, #id, onDelete: KeyAction.cascade)(); @override Set get primaryKey => {assetId, albumId}; diff --git a/mobile/lib/infrastructure/entities/local_album_asset.entity.drift.dart b/mobile/lib/infrastructure/entities/local_album_asset.entity.drift.dart index e8f94fa74b..78da361f62 100644 --- a/mobile/lib/infrastructure/entities/local_album_asset.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/local_album_asset.entity.drift.dart @@ -11,76 +11,96 @@ import 'package:drift/internal/modular.dart' as i4; import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart' as i5; -typedef $$LocalAlbumAssetEntityTableCreateCompanionBuilder - = i1.LocalAlbumAssetEntityCompanion Function({ - required String assetId, - required String albumId, -}); -typedef $$LocalAlbumAssetEntityTableUpdateCompanionBuilder - = i1.LocalAlbumAssetEntityCompanion Function({ - i0.Value assetId, - i0.Value albumId, -}); +typedef $$LocalAlbumAssetEntityTableCreateCompanionBuilder = + i1.LocalAlbumAssetEntityCompanion Function({ + required String assetId, + required String albumId, + }); +typedef $$LocalAlbumAssetEntityTableUpdateCompanionBuilder = + i1.LocalAlbumAssetEntityCompanion Function({ + i0.Value assetId, + i0.Value albumId, + }); -final class $$LocalAlbumAssetEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, - i1.$LocalAlbumAssetEntityTable, - i1.LocalAlbumAssetEntityData> { +final class $$LocalAlbumAssetEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$LocalAlbumAssetEntityTable, + i1.LocalAlbumAssetEntityData + > { $$LocalAlbumAssetEntityTableReferences( - super.$_db, super.$_table, super.$_typedResult); + super.$_db, + super.$_table, + super.$_typedResult, + ); static i3.$LocalAssetEntityTable _assetIdTable(i0.GeneratedDatabase db) => i4.ReadDatabaseContainer(db) .resultSet('local_asset_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i4.ReadDatabaseContainer(db) .resultSet( - 'local_album_asset_entity') + 'local_album_asset_entity', + ) .assetId, - i4.ReadDatabaseContainer(db) - .resultSet('local_asset_entity') - .id)); + i4.ReadDatabaseContainer( + db, + ).resultSet('local_asset_entity').id, + ), + ); i3.$$LocalAssetEntityTableProcessedTableManager get assetId { final $_column = $_itemColumn('asset_id')!; final manager = i3 .$$LocalAssetEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer( $_db, - i4.ReadDatabaseContainer($_db) - .resultSet('local_asset_entity')) + ).resultSet('local_asset_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_assetIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } static i5.$LocalAlbumEntityTable _albumIdTable(i0.GeneratedDatabase db) => i4.ReadDatabaseContainer(db) .resultSet('local_album_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i4.ReadDatabaseContainer(db) .resultSet( - 'local_album_asset_entity') + 'local_album_asset_entity', + ) .albumId, - i4.ReadDatabaseContainer(db) - .resultSet('local_album_entity') - .id)); + i4.ReadDatabaseContainer( + db, + ).resultSet('local_album_entity').id, + ), + ); i5.$$LocalAlbumEntityTableProcessedTableManager get albumId { final $_column = $_itemColumn('album_id')!; final manager = i5 .$$LocalAlbumEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer( $_db, - i4.ReadDatabaseContainer($_db) - .resultSet('local_album_entity')) + ).resultSet('local_album_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_albumIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -95,45 +115,55 @@ class $$LocalAlbumAssetEntityTableFilterComposer }); i3.$$LocalAssetEntityTableFilterComposer get assetId { final i3.$$LocalAssetEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('local_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$LocalAssetEntityTableFilterComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('local_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$LocalAssetEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$LocalAlbumEntityTableFilterComposer get albumId { final i5.$$LocalAlbumEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.albumId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('local_album_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$LocalAlbumEntityTableFilterComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('local_album_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.albumId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_album_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$LocalAlbumEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_album_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -150,48 +180,56 @@ class $$LocalAlbumAssetEntityTableOrderingComposer i3.$$LocalAssetEntityTableOrderingComposer get assetId { final i3.$$LocalAssetEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('local_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$LocalAssetEntityTableOrderingComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'local_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$LocalAssetEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$LocalAlbumEntityTableOrderingComposer get albumId { final i5.$$LocalAlbumEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.albumId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('local_album_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$LocalAlbumEntityTableOrderingComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'local_album_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.albumId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_album_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$LocalAlbumEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_album_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -208,106 +246,129 @@ class $$LocalAlbumAssetEntityTableAnnotationComposer i3.$$LocalAssetEntityTableAnnotationComposer get assetId { final i3.$$LocalAssetEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('local_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$LocalAssetEntityTableAnnotationComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'local_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$LocalAssetEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$LocalAlbumEntityTableAnnotationComposer get albumId { final i5.$$LocalAlbumEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.albumId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('local_album_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$LocalAlbumEntityTableAnnotationComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'local_album_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.albumId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_album_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$LocalAlbumEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('local_album_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$LocalAlbumAssetEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$LocalAlbumAssetEntityTable, - i1.LocalAlbumAssetEntityData, - i1.$$LocalAlbumAssetEntityTableFilterComposer, - i1.$$LocalAlbumAssetEntityTableOrderingComposer, - i1.$$LocalAlbumAssetEntityTableAnnotationComposer, - $$LocalAlbumAssetEntityTableCreateCompanionBuilder, - $$LocalAlbumAssetEntityTableUpdateCompanionBuilder, - (i1.LocalAlbumAssetEntityData, i1.$$LocalAlbumAssetEntityTableReferences), - i1.LocalAlbumAssetEntityData, - i0.PrefetchHooks Function({bool assetId, bool albumId})> { +class $$LocalAlbumAssetEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$LocalAlbumAssetEntityTable, + i1.LocalAlbumAssetEntityData, + i1.$$LocalAlbumAssetEntityTableFilterComposer, + i1.$$LocalAlbumAssetEntityTableOrderingComposer, + i1.$$LocalAlbumAssetEntityTableAnnotationComposer, + $$LocalAlbumAssetEntityTableCreateCompanionBuilder, + $$LocalAlbumAssetEntityTableUpdateCompanionBuilder, + ( + i1.LocalAlbumAssetEntityData, + i1.$$LocalAlbumAssetEntityTableReferences, + ), + i1.LocalAlbumAssetEntityData, + i0.PrefetchHooks Function({bool assetId, bool albumId}) + > { $$LocalAlbumAssetEntityTableTableManager( - i0.GeneratedDatabase db, i1.$LocalAlbumAssetEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$LocalAlbumAssetEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => i1.$$LocalAlbumAssetEntityTableFilterComposer( - $db: db, $table: table), + $db: db, + $table: table, + ), createOrderingComposer: () => i1.$$LocalAlbumAssetEntityTableOrderingComposer( - $db: db, $table: table), + $db: db, + $table: table, + ), createComputedFieldComposer: () => i1.$$LocalAlbumAssetEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value assetId = const i0.Value.absent(), - i0.Value albumId = const i0.Value.absent(), - }) => - i1.LocalAlbumAssetEntityCompanion( - assetId: assetId, - albumId: albumId, - ), - createCompanionCallback: ({ - required String assetId, - required String albumId, - }) => - i1.LocalAlbumAssetEntityCompanion.insert( - assetId: assetId, - albumId: albumId, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value assetId = const i0.Value.absent(), + i0.Value albumId = const i0.Value.absent(), + }) => i1.LocalAlbumAssetEntityCompanion( + assetId: assetId, + albumId: albumId, + ), + createCompanionCallback: + ({required String assetId, required String albumId}) => + i1.LocalAlbumAssetEntityCompanion.insert( + assetId: assetId, + albumId: albumId, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$LocalAlbumAssetEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$LocalAlbumAssetEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({assetId = false, albumId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -318,83 +379,104 @@ class $$LocalAlbumAssetEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (assetId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.assetId, - referencedTable: i1.$$LocalAlbumAssetEntityTableReferences - ._assetIdTable(db), - referencedColumn: i1.$$LocalAlbumAssetEntityTableReferences - ._assetIdTable(db) - .id, - ) as T; - } - if (albumId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.albumId, - referencedTable: i1.$$LocalAlbumAssetEntityTableReferences - ._albumIdTable(db), - referencedColumn: i1.$$LocalAlbumAssetEntityTableReferences - ._albumIdTable(db) - .id, - ) as T; - } + dynamic + > + >(state) { + if (assetId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.assetId, + referencedTable: i1 + .$$LocalAlbumAssetEntityTableReferences + ._assetIdTable(db), + referencedColumn: i1 + .$$LocalAlbumAssetEntityTableReferences + ._assetIdTable(db) + .id, + ) + as T; + } + if (albumId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.albumId, + referencedTable: i1 + .$$LocalAlbumAssetEntityTableReferences + ._albumIdTable(db), + referencedColumn: i1 + .$$LocalAlbumAssetEntityTableReferences + ._albumIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$LocalAlbumAssetEntityTableProcessedTableManager - = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$LocalAlbumAssetEntityTable, - i1.LocalAlbumAssetEntityData, - i1.$$LocalAlbumAssetEntityTableFilterComposer, - i1.$$LocalAlbumAssetEntityTableOrderingComposer, - i1.$$LocalAlbumAssetEntityTableAnnotationComposer, - $$LocalAlbumAssetEntityTableCreateCompanionBuilder, - $$LocalAlbumAssetEntityTableUpdateCompanionBuilder, - ( - i1.LocalAlbumAssetEntityData, - i1.$$LocalAlbumAssetEntityTableReferences - ), - i1.LocalAlbumAssetEntityData, - i0.PrefetchHooks Function({bool assetId, bool albumId})>; +typedef $$LocalAlbumAssetEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$LocalAlbumAssetEntityTable, + i1.LocalAlbumAssetEntityData, + i1.$$LocalAlbumAssetEntityTableFilterComposer, + i1.$$LocalAlbumAssetEntityTableOrderingComposer, + i1.$$LocalAlbumAssetEntityTableAnnotationComposer, + $$LocalAlbumAssetEntityTableCreateCompanionBuilder, + $$LocalAlbumAssetEntityTableUpdateCompanionBuilder, + (i1.LocalAlbumAssetEntityData, i1.$$LocalAlbumAssetEntityTableReferences), + i1.LocalAlbumAssetEntityData, + i0.PrefetchHooks Function({bool assetId, bool albumId}) + >; class $LocalAlbumAssetEntityTable extends i2.LocalAlbumAssetEntity with - i0 - .TableInfo<$LocalAlbumAssetEntityTable, i1.LocalAlbumAssetEntityData> { + i0.TableInfo< + $LocalAlbumAssetEntityTable, + i1.LocalAlbumAssetEntityData + > { @override final i0.GeneratedDatabase attachedDatabase; final String? _alias; $LocalAlbumAssetEntityTable(this.attachedDatabase, [this._alias]); - static const i0.VerificationMeta _assetIdMeta = - const i0.VerificationMeta('assetId'); + static const i0.VerificationMeta _assetIdMeta = const i0.VerificationMeta( + 'assetId', + ); @override late final i0.GeneratedColumn assetId = i0.GeneratedColumn( - 'asset_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES local_asset_entity (id) ON DELETE CASCADE')); - static const i0.VerificationMeta _albumIdMeta = - const i0.VerificationMeta('albumId'); + 'asset_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); + static const i0.VerificationMeta _albumIdMeta = const i0.VerificationMeta( + 'albumId', + ); @override late final i0.GeneratedColumn albumId = i0.GeneratedColumn( - 'album_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES local_album_entity (id) ON DELETE CASCADE')); + 'album_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, albumId]; @override @@ -404,19 +486,24 @@ class $LocalAlbumAssetEntityTable extends i2.LocalAlbumAssetEntity static const String $name = 'local_album_asset_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('asset_id')) { - context.handle(_assetIdMeta, - assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta)); + context.handle( + _assetIdMeta, + assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta), + ); } else if (isInserting) { context.missing(_assetIdMeta); } if (data.containsKey('album_id')) { - context.handle(_albumIdMeta, - albumId.isAcceptableOrUnknown(data['album_id']!, _albumIdMeta)); + context.handle( + _albumIdMeta, + albumId.isAcceptableOrUnknown(data['album_id']!, _albumIdMeta), + ); } else if (isInserting) { context.missing(_albumIdMeta); } @@ -426,14 +513,20 @@ class $LocalAlbumAssetEntityTable extends i2.LocalAlbumAssetEntity @override Set get $primaryKey => {assetId, albumId}; @override - i1.LocalAlbumAssetEntityData map(Map data, - {String? tablePrefix}) { + i1.LocalAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.LocalAlbumAssetEntityData( - assetId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - albumId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}album_id'])!, + assetId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, ); } @@ -452,8 +545,10 @@ class LocalAlbumAssetEntityData extends i0.DataClass implements i0.Insertable { final String assetId; final String albumId; - const LocalAlbumAssetEntityData( - {required this.assetId, required this.albumId}); + const LocalAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -462,8 +557,10 @@ class LocalAlbumAssetEntityData extends i0.DataClass return map; } - factory LocalAlbumAssetEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory LocalAlbumAssetEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return LocalAlbumAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -485,7 +582,8 @@ class LocalAlbumAssetEntityData extends i0.DataClass albumId: albumId ?? this.albumId, ); LocalAlbumAssetEntityData copyWithCompanion( - i1.LocalAlbumAssetEntityCompanion data) { + i1.LocalAlbumAssetEntityCompanion data, + ) { return LocalAlbumAssetEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, albumId: data.albumId.present ? data.albumId.value : this.albumId, @@ -522,8 +620,8 @@ class LocalAlbumAssetEntityCompanion LocalAlbumAssetEntityCompanion.insert({ required String assetId, required String albumId, - }) : assetId = i0.Value(assetId), - albumId = i0.Value(albumId); + }) : assetId = i0.Value(assetId), + albumId = i0.Value(albumId); static i0.Insertable custom({ i0.Expression? assetId, i0.Expression? albumId, @@ -534,8 +632,10 @@ class LocalAlbumAssetEntityCompanion }); } - i1.LocalAlbumAssetEntityCompanion copyWith( - {i0.Value? assetId, i0.Value? albumId}) { + i1.LocalAlbumAssetEntityCompanion copyWith({ + i0.Value? assetId, + i0.Value? albumId, + }) { return i1.LocalAlbumAssetEntityCompanion( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, diff --git a/mobile/lib/infrastructure/entities/local_asset.entity.dart b/mobile/lib/infrastructure/entities/local_asset.entity.dart index 204d5d6a80..e5519cfacf 100644 --- a/mobile/lib/infrastructure/entities/local_asset.entity.dart +++ b/mobile/lib/infrastructure/entities/local_asset.entity.dart @@ -22,17 +22,17 @@ class LocalAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin { extension LocalAssetEntityDataDomainEx on LocalAssetEntityData { LocalAsset toDto() => LocalAsset( - id: id, - name: name, - checksum: checksum, - type: type, - createdAt: createdAt, - updatedAt: updatedAt, - durationInSeconds: durationInSeconds, - isFavorite: isFavorite, - height: height, - width: width, - remoteId: null, - orientation: orientation, - ); + id: id, + name: name, + checksum: checksum, + type: type, + createdAt: createdAt, + updatedAt: updatedAt, + durationInSeconds: durationInSeconds, + isFavorite: isFavorite, + height: height, + width: width, + remoteId: null, + orientation: orientation, + ); } diff --git a/mobile/lib/infrastructure/entities/local_asset.entity.drift.dart b/mobile/lib/infrastructure/entities/local_asset.entity.drift.dart index e9c5961aa5..329401d5db 100644 --- a/mobile/lib/infrastructure/entities/local_asset.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/local_asset.entity.drift.dart @@ -8,34 +8,34 @@ import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart' as i3; import 'package:drift/src/runtime/query_builder/query_builder.dart' as i4; -typedef $$LocalAssetEntityTableCreateCompanionBuilder - = i1.LocalAssetEntityCompanion Function({ - required String name, - required i2.AssetType type, - i0.Value createdAt, - i0.Value updatedAt, - i0.Value width, - i0.Value height, - i0.Value durationInSeconds, - required String id, - i0.Value checksum, - i0.Value isFavorite, - i0.Value orientation, -}); -typedef $$LocalAssetEntityTableUpdateCompanionBuilder - = i1.LocalAssetEntityCompanion Function({ - i0.Value name, - i0.Value type, - i0.Value createdAt, - i0.Value updatedAt, - i0.Value width, - i0.Value height, - i0.Value durationInSeconds, - i0.Value id, - i0.Value checksum, - i0.Value isFavorite, - i0.Value orientation, -}); +typedef $$LocalAssetEntityTableCreateCompanionBuilder = + i1.LocalAssetEntityCompanion Function({ + required String name, + required i2.AssetType type, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value width, + i0.Value height, + i0.Value durationInSeconds, + required String id, + i0.Value checksum, + i0.Value isFavorite, + i0.Value orientation, + }); +typedef $$LocalAssetEntityTableUpdateCompanionBuilder = + i1.LocalAssetEntityCompanion Function({ + i0.Value name, + i0.Value type, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value width, + i0.Value height, + i0.Value durationInSeconds, + i0.Value id, + i0.Value checksum, + i0.Value isFavorite, + i0.Value orientation, + }); class $$LocalAssetEntityTableFilterComposer extends i0.Composer { @@ -47,41 +47,60 @@ class $$LocalAssetEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnFilters(column)); + column: $table.name, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnWithTypeConverterFilters get type => $composableBuilder( - column: $table.type, - builder: (column) => i0.ColumnWithTypeConverterFilters(column)); + column: $table.type, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); i0.ColumnFilters get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get width => $composableBuilder( - column: $table.width, builder: (column) => i0.ColumnFilters(column)); + column: $table.width, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get height => $composableBuilder( - column: $table.height, builder: (column) => i0.ColumnFilters(column)); + column: $table.height, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get durationInSeconds => $composableBuilder( - column: $table.durationInSeconds, - builder: (column) => i0.ColumnFilters(column)); + column: $table.durationInSeconds, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnFilters(column)); + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get checksum => $composableBuilder( - column: $table.checksum, builder: (column) => i0.ColumnFilters(column)); + column: $table.checksum, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get isFavorite => $composableBuilder( - column: $table.isFavorite, builder: (column) => i0.ColumnFilters(column)); + column: $table.isFavorite, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get orientation => $composableBuilder( - column: $table.orientation, - builder: (column) => i0.ColumnFilters(column)); + column: $table.orientation, + builder: (column) => i0.ColumnFilters(column), + ); } class $$LocalAssetEntityTableOrderingComposer @@ -94,42 +113,59 @@ class $$LocalAssetEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnOrderings(column)); + column: $table.name, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get type => $composableBuilder( - column: $table.type, builder: (column) => i0.ColumnOrderings(column)); + column: $table.type, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get createdAt => $composableBuilder( - column: $table.createdAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get updatedAt => $composableBuilder( - column: $table.updatedAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get width => $composableBuilder( - column: $table.width, builder: (column) => i0.ColumnOrderings(column)); + column: $table.width, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get height => $composableBuilder( - column: $table.height, builder: (column) => i0.ColumnOrderings(column)); + column: $table.height, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get durationInSeconds => $composableBuilder( - column: $table.durationInSeconds, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.durationInSeconds, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get checksum => $composableBuilder( - column: $table.checksum, builder: (column) => i0.ColumnOrderings(column)); + column: $table.checksum, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get isFavorite => $composableBuilder( - column: $table.isFavorite, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.isFavorite, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get orientation => $composableBuilder( - column: $table.orientation, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.orientation, + builder: (column) => i0.ColumnOrderings(column), + ); } class $$LocalAssetEntityTableAnnotationComposer @@ -160,7 +196,9 @@ class $$LocalAssetEntityTableAnnotationComposer $composableBuilder(column: $table.height, builder: (column) => column); i0.GeneratedColumn get durationInSeconds => $composableBuilder( - column: $table.durationInSeconds, builder: (column) => column); + column: $table.durationInSeconds, + builder: (column) => column, + ); i0.GeneratedColumn get id => $composableBuilder(column: $table.id, builder: (column) => column); @@ -169,31 +207,43 @@ class $$LocalAssetEntityTableAnnotationComposer $composableBuilder(column: $table.checksum, builder: (column) => column); i0.GeneratedColumn get isFavorite => $composableBuilder( - column: $table.isFavorite, builder: (column) => column); + column: $table.isFavorite, + builder: (column) => column, + ); i0.GeneratedColumn get orientation => $composableBuilder( - column: $table.orientation, builder: (column) => column); + column: $table.orientation, + builder: (column) => column, + ); } -class $$LocalAssetEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$LocalAssetEntityTable, - i1.LocalAssetEntityData, - i1.$$LocalAssetEntityTableFilterComposer, - i1.$$LocalAssetEntityTableOrderingComposer, - i1.$$LocalAssetEntityTableAnnotationComposer, - $$LocalAssetEntityTableCreateCompanionBuilder, - $$LocalAssetEntityTableUpdateCompanionBuilder, - ( - i1.LocalAssetEntityData, - i0.BaseReferences - ), - i1.LocalAssetEntityData, - i0.PrefetchHooks Function()> { +class $$LocalAssetEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$LocalAssetEntityTable, + i1.LocalAssetEntityData, + i1.$$LocalAssetEntityTableFilterComposer, + i1.$$LocalAssetEntityTableOrderingComposer, + i1.$$LocalAssetEntityTableAnnotationComposer, + $$LocalAssetEntityTableCreateCompanionBuilder, + $$LocalAssetEntityTableUpdateCompanionBuilder, + ( + i1.LocalAssetEntityData, + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$LocalAssetEntityTable, + i1.LocalAssetEntityData + >, + ), + i1.LocalAssetEntityData, + i0.PrefetchHooks Function() + > { $$LocalAssetEntityTableTableManager( - i0.GeneratedDatabase db, i1.$LocalAssetEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$LocalAssetEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -202,84 +252,94 @@ class $$LocalAssetEntityTableTableManager extends i0.RootTableManager< .$$LocalAssetEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$LocalAssetEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value name = const i0.Value.absent(), - i0.Value type = const i0.Value.absent(), - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value width = const i0.Value.absent(), - i0.Value height = const i0.Value.absent(), - i0.Value durationInSeconds = const i0.Value.absent(), - i0.Value id = const i0.Value.absent(), - i0.Value checksum = const i0.Value.absent(), - i0.Value isFavorite = const i0.Value.absent(), - i0.Value orientation = const i0.Value.absent(), - }) => - i1.LocalAssetEntityCompanion( - name: name, - type: type, - createdAt: createdAt, - updatedAt: updatedAt, - width: width, - height: height, - durationInSeconds: durationInSeconds, - id: id, - checksum: checksum, - isFavorite: isFavorite, - orientation: orientation, - ), - createCompanionCallback: ({ - required String name, - required i2.AssetType type, - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value width = const i0.Value.absent(), - i0.Value height = const i0.Value.absent(), - i0.Value durationInSeconds = const i0.Value.absent(), - required String id, - i0.Value checksum = const i0.Value.absent(), - i0.Value isFavorite = const i0.Value.absent(), - i0.Value orientation = const i0.Value.absent(), - }) => - i1.LocalAssetEntityCompanion.insert( - name: name, - type: type, - createdAt: createdAt, - updatedAt: updatedAt, - width: width, - height: height, - durationInSeconds: durationInSeconds, - id: id, - checksum: checksum, - isFavorite: isFavorite, - orientation: orientation, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value name = const i0.Value.absent(), + i0.Value type = const i0.Value.absent(), + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value width = const i0.Value.absent(), + i0.Value height = const i0.Value.absent(), + i0.Value durationInSeconds = const i0.Value.absent(), + i0.Value id = const i0.Value.absent(), + i0.Value checksum = const i0.Value.absent(), + i0.Value isFavorite = const i0.Value.absent(), + i0.Value orientation = const i0.Value.absent(), + }) => i1.LocalAssetEntityCompanion( + name: name, + type: type, + createdAt: createdAt, + updatedAt: updatedAt, + width: width, + height: height, + durationInSeconds: durationInSeconds, + id: id, + checksum: checksum, + isFavorite: isFavorite, + orientation: orientation, + ), + createCompanionCallback: + ({ + required String name, + required i2.AssetType type, + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value width = const i0.Value.absent(), + i0.Value height = const i0.Value.absent(), + i0.Value durationInSeconds = const i0.Value.absent(), + required String id, + i0.Value checksum = const i0.Value.absent(), + i0.Value isFavorite = const i0.Value.absent(), + i0.Value orientation = const i0.Value.absent(), + }) => i1.LocalAssetEntityCompanion.insert( + name: name, + type: type, + createdAt: createdAt, + updatedAt: updatedAt, + width: width, + height: height, + durationInSeconds: durationInSeconds, + id: id, + checksum: checksum, + isFavorite: isFavorite, + orientation: orientation, + ), withReferenceMapper: (p0) => p0 .map((e) => (e.readTable(table), i0.BaseReferences(db, table, e))) .toList(), prefetchHooksCallback: null, - )); + ), + ); } -typedef $$LocalAssetEntityTableProcessedTableManager = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$LocalAssetEntityTable, - i1.LocalAssetEntityData, - i1.$$LocalAssetEntityTableFilterComposer, - i1.$$LocalAssetEntityTableOrderingComposer, - i1.$$LocalAssetEntityTableAnnotationComposer, - $$LocalAssetEntityTableCreateCompanionBuilder, - $$LocalAssetEntityTableUpdateCompanionBuilder, - ( +typedef $$LocalAssetEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$LocalAssetEntityTable, i1.LocalAssetEntityData, - i0.BaseReferences - ), - i1.LocalAssetEntityData, - i0.PrefetchHooks Function()>; -i0.Index get idxLocalAssetChecksum => i0.Index('idx_local_asset_checksum', - 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)'); + i1.$$LocalAssetEntityTableFilterComposer, + i1.$$LocalAssetEntityTableOrderingComposer, + i1.$$LocalAssetEntityTableAnnotationComposer, + $$LocalAssetEntityTableCreateCompanionBuilder, + $$LocalAssetEntityTableUpdateCompanionBuilder, + ( + i1.LocalAssetEntityData, + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$LocalAssetEntityTable, + i1.LocalAssetEntityData + >, + ), + i1.LocalAssetEntityData, + i0.PrefetchHooks Function() + >; +i0.Index get idxLocalAssetChecksum => i0.Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', +); class $LocalAssetEntityTable extends i3.LocalAssetEntity with i0.TableInfo<$LocalAssetEntityTable, i1.LocalAssetEntityData> { @@ -287,95 +347,146 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity final i0.GeneratedDatabase attachedDatabase; final String? _alias; $LocalAssetEntityTable(this.attachedDatabase, [this._alias]); - static const i0.VerificationMeta _nameMeta = - const i0.VerificationMeta('name'); + static const i0.VerificationMeta _nameMeta = const i0.VerificationMeta( + 'name', + ); @override late final i0.GeneratedColumn name = i0.GeneratedColumn( - 'name', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); + 'name', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); @override late final i0.GeneratedColumnWithTypeConverter type = - i0.GeneratedColumn('type', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true) - .withConverter( - i1.$LocalAssetEntityTable.$convertertype); - static const i0.VerificationMeta _createdAtMeta = - const i0.VerificationMeta('createdAt'); + i0.GeneratedColumn( + 'type', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ).withConverter(i1.$LocalAssetEntityTable.$convertertype); + static const i0.VerificationMeta _createdAtMeta = const i0.VerificationMeta( + 'createdAt', + ); @override late final i0.GeneratedColumn createdAt = - i0.GeneratedColumn('created_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i4.currentDateAndTime); - static const i0.VerificationMeta _updatedAtMeta = - const i0.VerificationMeta('updatedAt'); + i0.GeneratedColumn( + 'created_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); + static const i0.VerificationMeta _updatedAtMeta = const i0.VerificationMeta( + 'updatedAt', + ); @override late final i0.GeneratedColumn updatedAt = - i0.GeneratedColumn('updated_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i4.currentDateAndTime); - static const i0.VerificationMeta _widthMeta = - const i0.VerificationMeta('width'); + i0.GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); + static const i0.VerificationMeta _widthMeta = const i0.VerificationMeta( + 'width', + ); @override late final i0.GeneratedColumn width = i0.GeneratedColumn( - 'width', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); - static const i0.VerificationMeta _heightMeta = - const i0.VerificationMeta('height'); + 'width', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _heightMeta = const i0.VerificationMeta( + 'height', + ); @override late final i0.GeneratedColumn height = i0.GeneratedColumn( - 'height', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); + 'height', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); static const i0.VerificationMeta _durationInSecondsMeta = const i0.VerificationMeta('durationInSeconds'); @override late final i0.GeneratedColumn durationInSeconds = - i0.GeneratedColumn('duration_in_seconds', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); + i0.GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); @override late final i0.GeneratedColumn id = i0.GeneratedColumn( - 'id', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _checksumMeta = - const i0.VerificationMeta('checksum'); + 'id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _checksumMeta = const i0.VerificationMeta( + 'checksum', + ); @override late final i0.GeneratedColumn checksum = i0.GeneratedColumn( - 'checksum', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _isFavoriteMeta = - const i0.VerificationMeta('isFavorite'); + 'checksum', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _isFavoriteMeta = const i0.VerificationMeta( + 'isFavorite', + ); @override late final i0.GeneratedColumn isFavorite = i0.GeneratedColumn( - 'is_favorite', aliasedName, false, - type: i0.DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'CHECK ("is_favorite" IN (0, 1))'), - defaultValue: const i4.Constant(false)); - static const i0.VerificationMeta _orientationMeta = - const i0.VerificationMeta('orientation'); + 'is_favorite', + aliasedName, + false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const i4.Constant(false), + ); + static const i0.VerificationMeta _orientationMeta = const i0.VerificationMeta( + 'orientation', + ); @override late final i0.GeneratedColumn orientation = i0.GeneratedColumn( - 'orientation', aliasedName, false, - type: i0.DriftSqlType.int, - requiredDuringInsert: false, - defaultValue: const i4.Constant(0)); + 'orientation', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const i4.Constant(0), + ); @override List get $columns => [ - name, - type, - createdAt, - updatedAt, - width, - height, - durationInSeconds, - id, - checksum, - isFavorite, - orientation - ]; + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -383,37 +494,51 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity static const String $name = 'local_asset_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('name')) { context.handle( - _nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta)); + _nameMeta, + name.isAcceptableOrUnknown(data['name']!, _nameMeta), + ); } else if (isInserting) { context.missing(_nameMeta); } if (data.containsKey('created_at')) { - context.handle(_createdAtMeta, - createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); + context.handle( + _createdAtMeta, + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta), + ); } if (data.containsKey('updated_at')) { - context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); + context.handle( + _updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), + ); } if (data.containsKey('width')) { context.handle( - _widthMeta, width.isAcceptableOrUnknown(data['width']!, _widthMeta)); + _widthMeta, + width.isAcceptableOrUnknown(data['width']!, _widthMeta), + ); } if (data.containsKey('height')) { - context.handle(_heightMeta, - height.isAcceptableOrUnknown(data['height']!, _heightMeta)); + context.handle( + _heightMeta, + height.isAcceptableOrUnknown(data['height']!, _heightMeta), + ); } if (data.containsKey('duration_in_seconds')) { context.handle( + _durationInSecondsMeta, + durationInSeconds.isAcceptableOrUnknown( + data['duration_in_seconds']!, _durationInSecondsMeta, - durationInSeconds.isAcceptableOrUnknown( - data['duration_in_seconds']!, _durationInSecondsMeta)); + ), + ); } if (data.containsKey('id')) { context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); @@ -421,20 +546,25 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity context.missing(_idMeta); } if (data.containsKey('checksum')) { - context.handle(_checksumMeta, - checksum.isAcceptableOrUnknown(data['checksum']!, _checksumMeta)); + context.handle( + _checksumMeta, + checksum.isAcceptableOrUnknown(data['checksum']!, _checksumMeta), + ); } if (data.containsKey('is_favorite')) { context.handle( - _isFavoriteMeta, - isFavorite.isAcceptableOrUnknown( - data['is_favorite']!, _isFavoriteMeta)); + _isFavoriteMeta, + isFavorite.isAcceptableOrUnknown(data['is_favorite']!, _isFavoriteMeta), + ); } if (data.containsKey('orientation')) { context.handle( + _orientationMeta, + orientation.isAcceptableOrUnknown( + data['orientation']!, _orientationMeta, - orientation.isAcceptableOrUnknown( - data['orientation']!, _orientationMeta)); + ), + ); } return context; } @@ -442,33 +572,58 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity @override Set get $primaryKey => {id}; @override - i1.LocalAssetEntityData map(Map data, - {String? tablePrefix}) { + i1.LocalAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.LocalAssetEntityData( - name: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!, - type: i1.$LocalAssetEntityTable.$convertertype.fromSql(attachedDatabase - .typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}type'])!), + name: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: i1.$LocalAssetEntityTable.$convertertype.fromSql( + attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + ), createdAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, updatedAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - width: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}width']), - height: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}height']), + i0.DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}height'], + ), durationInSeconds: attachedDatabase.typeMapping.read( - i0.DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), - id: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!, - checksum: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}checksum']), - isFavorite: attachedDatabase.typeMapping - .read(i0.DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - orientation: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}orientation'])!, + i0.DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + isFavorite: attachedDatabase.typeMapping.read( + i0.DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + orientation: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}orientation'], + )!, ); } @@ -498,25 +653,27 @@ class LocalAssetEntityData extends i0.DataClass final String? checksum; final bool isFavorite; final int orientation; - const LocalAssetEntityData( - {required this.name, - required this.type, - required this.createdAt, - required this.updatedAt, - this.width, - this.height, - this.durationInSeconds, - required this.id, - this.checksum, - required this.isFavorite, - required this.orientation}); + const LocalAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + this.checksum, + required this.isFavorite, + required this.orientation, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; map['name'] = i0.Variable(name); { map['type'] = i0.Variable( - i1.$LocalAssetEntityTable.$convertertype.toSql(type)); + i1.$LocalAssetEntityTable.$convertertype.toSql(type), + ); } map['created_at'] = i0.Variable(createdAt); map['updated_at'] = i0.Variable(updatedAt); @@ -538,13 +695,16 @@ class LocalAssetEntityData extends i0.DataClass return map; } - factory LocalAssetEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory LocalAssetEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return LocalAssetEntityData( name: serializer.fromJson(json['name']), - type: i1.$LocalAssetEntityTable.$convertertype - .fromJson(serializer.fromJson(json['type'])), + type: i1.$LocalAssetEntityTable.$convertertype.fromJson( + serializer.fromJson(json['type']), + ), createdAt: serializer.fromJson(json['createdAt']), updatedAt: serializer.fromJson(json['updatedAt']), width: serializer.fromJson(json['width']), @@ -561,8 +721,9 @@ class LocalAssetEntityData extends i0.DataClass serializer ??= i0.driftRuntimeOptions.defaultSerializer; return { 'name': serializer.toJson(name), - 'type': serializer - .toJson(i1.$LocalAssetEntityTable.$convertertype.toJson(type)), + 'type': serializer.toJson( + i1.$LocalAssetEntityTable.$convertertype.toJson(type), + ), 'createdAt': serializer.toJson(createdAt), 'updatedAt': serializer.toJson(updatedAt), 'width': serializer.toJson(width), @@ -575,33 +736,33 @@ class LocalAssetEntityData extends i0.DataClass }; } - i1.LocalAssetEntityData copyWith( - {String? name, - i2.AssetType? type, - DateTime? createdAt, - DateTime? updatedAt, - i0.Value width = const i0.Value.absent(), - i0.Value height = const i0.Value.absent(), - i0.Value durationInSeconds = const i0.Value.absent(), - String? id, - i0.Value checksum = const i0.Value.absent(), - bool? isFavorite, - int? orientation}) => - i1.LocalAssetEntityData( - name: name ?? this.name, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - width: width.present ? width.value : this.width, - height: height.present ? height.value : this.height, - durationInSeconds: durationInSeconds.present - ? durationInSeconds.value - : this.durationInSeconds, - id: id ?? this.id, - checksum: checksum.present ? checksum.value : this.checksum, - isFavorite: isFavorite ?? this.isFavorite, - orientation: orientation ?? this.orientation, - ); + i1.LocalAssetEntityData copyWith({ + String? name, + i2.AssetType? type, + DateTime? createdAt, + DateTime? updatedAt, + i0.Value width = const i0.Value.absent(), + i0.Value height = const i0.Value.absent(), + i0.Value durationInSeconds = const i0.Value.absent(), + String? id, + i0.Value checksum = const i0.Value.absent(), + bool? isFavorite, + int? orientation, + }) => i1.LocalAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); LocalAssetEntityData copyWithCompanion(i1.LocalAssetEntityCompanion data) { return LocalAssetEntityData( name: data.name.present ? data.name.value : this.name, @@ -615,10 +776,12 @@ class LocalAssetEntityData extends i0.DataClass : this.durationInSeconds, id: data.id.present ? data.id.value : this.id, checksum: data.checksum.present ? data.checksum.value : this.checksum, - isFavorite: - data.isFavorite.present ? data.isFavorite.value : this.isFavorite, - orientation: - data.orientation.present ? data.orientation.value : this.orientation, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, ); } @@ -641,8 +804,19 @@ class LocalAssetEntityData extends i0.DataClass } @override - int get hashCode => Object.hash(name, type, createdAt, updatedAt, width, - height, durationInSeconds, id, checksum, isFavorite, orientation); + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -698,9 +872,9 @@ class LocalAssetEntityCompanion this.checksum = const i0.Value.absent(), this.isFavorite = const i0.Value.absent(), this.orientation = const i0.Value.absent(), - }) : name = i0.Value(name), - type = i0.Value(type), - id = i0.Value(id); + }) : name = i0.Value(name), + type = i0.Value(type), + id = i0.Value(id); static i0.Insertable custom({ i0.Expression? name, i0.Expression? type, @@ -729,18 +903,19 @@ class LocalAssetEntityCompanion }); } - i1.LocalAssetEntityCompanion copyWith( - {i0.Value? name, - i0.Value? type, - i0.Value? createdAt, - i0.Value? updatedAt, - i0.Value? width, - i0.Value? height, - i0.Value? durationInSeconds, - i0.Value? id, - i0.Value? checksum, - i0.Value? isFavorite, - i0.Value? orientation}) { + i1.LocalAssetEntityCompanion copyWith({ + i0.Value? name, + i0.Value? type, + i0.Value? createdAt, + i0.Value? updatedAt, + i0.Value? width, + i0.Value? height, + i0.Value? durationInSeconds, + i0.Value? id, + i0.Value? checksum, + i0.Value? isFavorite, + i0.Value? orientation, + }) { return i1.LocalAssetEntityCompanion( name: name ?? this.name, type: type ?? this.type, @@ -764,7 +939,8 @@ class LocalAssetEntityCompanion } if (type.present) { map['type'] = i0.Variable( - i1.$LocalAssetEntityTable.$convertertype.toSql(type.value)); + i1.$LocalAssetEntityTable.$convertertype.toSql(type.value), + ); } if (createdAt.present) { map['created_at'] = i0.Variable(createdAt.value); diff --git a/mobile/lib/infrastructure/entities/log.entity.g.dart b/mobile/lib/infrastructure/entities/log.entity.g.dart index 9300cf15c5..02fa817a08 100644 --- a/mobile/lib/infrastructure/entities/log.entity.g.dart +++ b/mobile/lib/infrastructure/entities/log.entity.g.dart @@ -32,23 +32,16 @@ const LoggerMessageSchema = CollectionSchema( name: r'createdAt', type: IsarType.dateTime, ), - r'details': PropertySchema( - id: 3, - name: r'details', - type: IsarType.string, - ), + r'details': PropertySchema(id: 3, name: r'details', type: IsarType.string), r'level': PropertySchema( id: 4, name: r'level', type: IsarType.byte, enumMap: _LoggerMessagelevelEnumValueMap, ), - r'message': PropertySchema( - id: 5, - name: r'message', - type: IsarType.string, - ) + r'message': PropertySchema(id: 5, name: r'message', type: IsarType.string), }, + estimateSize: _loggerMessageEstimateSize, serialize: _loggerMessageSerialize, deserialize: _loggerMessageDeserialize, @@ -57,6 +50,7 @@ const LoggerMessageSchema = CollectionSchema( indexes: {}, links: {}, embeddedSchemas: {}, + getId: _loggerMessageGetId, getLinks: _loggerMessageGetLinks, attach: _loggerMessageAttach, @@ -116,7 +110,8 @@ LoggerMessage _loggerMessageDeserialize( context2: reader.readStringOrNull(offsets[1]), createdAt: reader.readDateTime(offsets[2]), details: reader.readStringOrNull(offsets[3]), - level: _LoggerMessagelevelValueEnumMap[reader.readByteOrNull(offsets[4])] ?? + level: + _LoggerMessagelevelValueEnumMap[reader.readByteOrNull(offsets[4])] ?? LogLevel.info, message: reader.readString(offsets[5]), ); @@ -140,7 +135,8 @@ P _loggerMessageDeserializeProp

( return (reader.readStringOrNull(offset)) as P; case 4: return (_LoggerMessagelevelValueEnumMap[reader.readByteOrNull(offset)] ?? - LogLevel.info) as P; + LogLevel.info) + as P; case 5: return (reader.readString(offset)) as P; default: @@ -182,7 +178,10 @@ List> _loggerMessageGetLinks(LoggerMessage object) { } void _loggerMessageAttach( - IsarCollection col, Id id, LoggerMessage object) {} + IsarCollection col, + Id id, + LoggerMessage object, +) {} extension LoggerMessageQueryWhereSort on QueryBuilder { @@ -196,17 +195,16 @@ extension LoggerMessageQueryWhereSort extension LoggerMessageQueryWhere on QueryBuilder { QueryBuilder idEqualTo( - Id id) { + Id id, + ) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: id, - upper: id, - )); + return query.addWhereClause(IdWhereClause.between(lower: id, upper: id)); }); } QueryBuilder idNotEqualTo( - Id id) { + Id id, + ) { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query @@ -229,8 +227,9 @@ extension LoggerMessageQueryWhere } QueryBuilder idGreaterThan( - Id id, - {bool include = false}) { + Id id, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: id, includeLower: include), @@ -239,8 +238,9 @@ extension LoggerMessageQueryWhere } QueryBuilder idLessThan( - Id id, - {bool include = false}) { + Id id, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: id, includeUpper: include), @@ -255,12 +255,14 @@ extension LoggerMessageQueryWhere bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerId, - includeLower: includeLower, - upper: upperId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + ), + ); }); } } @@ -268,71 +270,74 @@ extension LoggerMessageQueryWhere extension LoggerMessageQueryFilter on QueryBuilder { QueryBuilder - context1IsNull() { + context1IsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'context1', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'context1'), + ); }); } QueryBuilder - context1IsNotNull() { + context1IsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'context1', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'context1'), + ); }); } QueryBuilder - context1EqualTo( - String? value, { - bool caseSensitive = true, - }) { + context1EqualTo(String? value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'context1', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'context1', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context1GreaterThan( + context1GreaterThan( String? value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'context1', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'context1', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context1LessThan( + context1LessThan( String? value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'context1', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'context1', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context1Between( + context1Between( String? lower, String? upper, { bool includeLower = true, @@ -340,153 +345,158 @@ extension LoggerMessageQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'context1', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'context1', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context1StartsWith( - String value, { - bool caseSensitive = true, - }) { + context1StartsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'context1', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'context1', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context1EndsWith( - String value, { - bool caseSensitive = true, - }) { + context1EndsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'context1', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'context1', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context1Contains(String value, {bool caseSensitive = true}) { + context1Contains(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'context1', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'context1', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context1Matches(String pattern, {bool caseSensitive = true}) { + context1Matches(String pattern, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'context1', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'context1', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context1IsEmpty() { + context1IsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'context1', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'context1', value: ''), + ); }); } QueryBuilder - context1IsNotEmpty() { + context1IsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'context1', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'context1', value: ''), + ); }); } QueryBuilder - context2IsNull() { + context2IsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'context2', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'context2'), + ); }); } QueryBuilder - context2IsNotNull() { + context2IsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'context2', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'context2'), + ); }); } QueryBuilder - context2EqualTo( - String? value, { - bool caseSensitive = true, - }) { + context2EqualTo(String? value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'context2', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'context2', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context2GreaterThan( + context2GreaterThan( String? value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'context2', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'context2', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context2LessThan( + context2LessThan( String? value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'context2', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'context2', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context2Between( + context2Between( String? lower, String? upper, { bool includeLower = true, @@ -494,209 +504,213 @@ extension LoggerMessageQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'context2', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'context2', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context2StartsWith( - String value, { - bool caseSensitive = true, - }) { + context2StartsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'context2', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'context2', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context2EndsWith( - String value, { - bool caseSensitive = true, - }) { + context2EndsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'context2', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'context2', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context2Contains(String value, {bool caseSensitive = true}) { + context2Contains(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'context2', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'context2', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context2Matches(String pattern, {bool caseSensitive = true}) { + context2Matches(String pattern, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'context2', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'context2', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - context2IsEmpty() { + context2IsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'context2', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'context2', value: ''), + ); }); } QueryBuilder - context2IsNotEmpty() { + context2IsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'context2', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'context2', value: ''), + ); }); } QueryBuilder - createdAtEqualTo(DateTime value) { + createdAtEqualTo(DateTime value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'createdAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'createdAt', value: value), + ); }); } QueryBuilder - createdAtGreaterThan( - DateTime value, { - bool include = false, - }) { + createdAtGreaterThan(DateTime value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'createdAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'createdAt', + value: value, + ), + ); }); } QueryBuilder - createdAtLessThan( - DateTime value, { - bool include = false, - }) { + createdAtLessThan(DateTime value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'createdAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'createdAt', + value: value, + ), + ); }); } QueryBuilder - createdAtBetween( + createdAtBetween( DateTime lower, DateTime upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'createdAt', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'createdAt', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder - detailsIsNull() { + detailsIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'details', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'details'), + ); }); } QueryBuilder - detailsIsNotNull() { + detailsIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'details', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'details'), + ); }); } QueryBuilder - detailsEqualTo( - String? value, { - bool caseSensitive = true, - }) { + detailsEqualTo(String? value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'details', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'details', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - detailsGreaterThan( + detailsGreaterThan( String? value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'details', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'details', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - detailsLessThan( + detailsLessThan( String? value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'details', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'details', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - detailsBetween( + detailsBetween( String? lower, String? upper, { bool includeLower = true, @@ -704,108 +718,109 @@ extension LoggerMessageQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'details', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'details', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - detailsStartsWith( - String value, { - bool caseSensitive = true, - }) { + detailsStartsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'details', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'details', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - detailsEndsWith( - String value, { - bool caseSensitive = true, - }) { + detailsEndsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'details', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'details', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - detailsContains(String value, {bool caseSensitive = true}) { + detailsContains(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'details', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'details', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - detailsMatches(String pattern, {bool caseSensitive = true}) { + detailsMatches(String pattern, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'details', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'details', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - detailsIsEmpty() { + detailsIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'details', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'details', value: ''), + ); }); } QueryBuilder - detailsIsNotEmpty() { + detailsIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'details', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'details', value: ''), + ); }); } QueryBuilder idEqualTo( - Id value) { + Id value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: value), + ); }); } QueryBuilder - idGreaterThan( - Id value, { - bool include = false, - }) { + idGreaterThan(Id value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + ), + ); }); } @@ -814,11 +829,13 @@ extension LoggerMessageQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + ), + ); }); } @@ -829,120 +846,124 @@ extension LoggerMessageQueryFilter bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder - levelEqualTo(LogLevel value) { + levelEqualTo(LogLevel value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'level', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'level', value: value), + ); }); } QueryBuilder - levelGreaterThan( - LogLevel value, { - bool include = false, - }) { + levelGreaterThan(LogLevel value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'level', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'level', + value: value, + ), + ); }); } QueryBuilder - levelLessThan( - LogLevel value, { - bool include = false, - }) { + levelLessThan(LogLevel value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'level', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'level', + value: value, + ), + ); }); } QueryBuilder - levelBetween( + levelBetween( LogLevel lower, LogLevel upper, { bool includeLower = true, bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'level', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'level', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder - messageEqualTo( - String value, { - bool caseSensitive = true, - }) { + messageEqualTo(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'message', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'message', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - messageGreaterThan( + messageGreaterThan( String value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'message', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'message', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - messageLessThan( + messageLessThan( String value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'message', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'message', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - messageBetween( + messageBetween( String lower, String upper, { bool includeLower = true, @@ -950,84 +971,86 @@ extension LoggerMessageQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'message', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'message', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - messageStartsWith( - String value, { - bool caseSensitive = true, - }) { + messageStartsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'message', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'message', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - messageEndsWith( - String value, { - bool caseSensitive = true, - }) { + messageEndsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'message', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'message', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - messageContains(String value, {bool caseSensitive = true}) { + messageContains(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'message', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'message', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - messageMatches(String pattern, {bool caseSensitive = true}) { + messageMatches(String pattern, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'message', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'message', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - messageIsEmpty() { + messageIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'message', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'message', value: ''), + ); }); } QueryBuilder - messageIsNotEmpty() { + messageIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'message', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'message', value: ''), + ); }); } } @@ -1047,7 +1070,7 @@ extension LoggerMessageQuerySortBy } QueryBuilder - sortByContext1Desc() { + sortByContext1Desc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'context1', Sort.desc); }); @@ -1060,7 +1083,7 @@ extension LoggerMessageQuerySortBy } QueryBuilder - sortByContext2Desc() { + sortByContext2Desc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'context2', Sort.desc); }); @@ -1073,7 +1096,7 @@ extension LoggerMessageQuerySortBy } QueryBuilder - sortByCreatedAtDesc() { + sortByCreatedAtDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'createdAt', Sort.desc); }); @@ -1125,7 +1148,7 @@ extension LoggerMessageQuerySortThenBy } QueryBuilder - thenByContext1Desc() { + thenByContext1Desc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'context1', Sort.desc); }); @@ -1138,7 +1161,7 @@ extension LoggerMessageQuerySortThenBy } QueryBuilder - thenByContext2Desc() { + thenByContext2Desc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'context2', Sort.desc); }); @@ -1151,7 +1174,7 @@ extension LoggerMessageQuerySortThenBy } QueryBuilder - thenByCreatedAtDesc() { + thenByCreatedAtDesc() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'createdAt', Sort.desc); }); @@ -1208,15 +1231,17 @@ extension LoggerMessageQuerySortThenBy extension LoggerMessageQueryWhereDistinct on QueryBuilder { - QueryBuilder distinctByContext1( - {bool caseSensitive = true}) { + QueryBuilder distinctByContext1({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'context1', caseSensitive: caseSensitive); }); } - QueryBuilder distinctByContext2( - {bool caseSensitive = true}) { + QueryBuilder distinctByContext2({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'context2', caseSensitive: caseSensitive); }); @@ -1228,8 +1253,9 @@ extension LoggerMessageQueryWhereDistinct }); } - QueryBuilder distinctByDetails( - {bool caseSensitive = true}) { + QueryBuilder distinctByDetails({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'details', caseSensitive: caseSensitive); }); @@ -1241,8 +1267,9 @@ extension LoggerMessageQueryWhereDistinct }); } - QueryBuilder distinctByMessage( - {bool caseSensitive = true}) { + QueryBuilder distinctByMessage({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'message', caseSensitive: caseSensitive); }); diff --git a/mobile/lib/infrastructure/entities/memory.entity.dart b/mobile/lib/infrastructure/entities/memory.entity.dart index 0e19802103..63dcfef5cc 100644 --- a/mobile/lib/infrastructure/entities/memory.entity.dart +++ b/mobile/lib/infrastructure/entities/memory.entity.dart @@ -14,8 +14,7 @@ class MemoryEntity extends Table with DriftDefaultsMixin { DateTimeColumn get deletedAt => dateTime().nullable()(); - TextColumn get ownerId => - text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); + TextColumn get ownerId => text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); IntColumn get type => intEnum()(); diff --git a/mobile/lib/infrastructure/entities/memory.entity.drift.dart b/mobile/lib/infrastructure/entities/memory.entity.drift.dart index cb88651ba4..f5f18695a5 100644 --- a/mobile/lib/infrastructure/entities/memory.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/memory.entity.drift.dart @@ -10,65 +10,76 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' as i5; import 'package:drift/internal/modular.dart' as i6; -typedef $$MemoryEntityTableCreateCompanionBuilder = i1.MemoryEntityCompanion - Function({ - required String id, - i0.Value createdAt, - i0.Value updatedAt, - i0.Value deletedAt, - required String ownerId, - required i2.MemoryTypeEnum type, - required String data, - i0.Value isSaved, - required DateTime memoryAt, - i0.Value seenAt, - i0.Value showAt, - i0.Value hideAt, -}); -typedef $$MemoryEntityTableUpdateCompanionBuilder = i1.MemoryEntityCompanion - Function({ - i0.Value id, - i0.Value createdAt, - i0.Value updatedAt, - i0.Value deletedAt, - i0.Value ownerId, - i0.Value type, - i0.Value data, - i0.Value isSaved, - i0.Value memoryAt, - i0.Value seenAt, - i0.Value showAt, - i0.Value hideAt, -}); +typedef $$MemoryEntityTableCreateCompanionBuilder = + i1.MemoryEntityCompanion Function({ + required String id, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value deletedAt, + required String ownerId, + required i2.MemoryTypeEnum type, + required String data, + i0.Value isSaved, + required DateTime memoryAt, + i0.Value seenAt, + i0.Value showAt, + i0.Value hideAt, + }); +typedef $$MemoryEntityTableUpdateCompanionBuilder = + i1.MemoryEntityCompanion Function({ + i0.Value id, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value deletedAt, + i0.Value ownerId, + i0.Value type, + i0.Value data, + i0.Value isSaved, + i0.Value memoryAt, + i0.Value seenAt, + i0.Value showAt, + i0.Value hideAt, + }); -final class $$MemoryEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, i1.$MemoryEntityTable, i1.MemoryEntityData> { +final class $$MemoryEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$MemoryEntityTable, + i1.MemoryEntityData + > { $$MemoryEntityTableReferences(super.$_db, super.$_table, super.$_typedResult); static i5.$UserEntityTable _ownerIdTable(i0.GeneratedDatabase db) => i6.ReadDatabaseContainer(db) .resultSet('user_entity') - .createAlias(i0.$_aliasNameGenerator( - i6.ReadDatabaseContainer(db) - .resultSet('memory_entity') - .ownerId, - i6.ReadDatabaseContainer(db) - .resultSet('user_entity') - .id)); + .createAlias( + i0.$_aliasNameGenerator( + i6.ReadDatabaseContainer( + db, + ).resultSet('memory_entity').ownerId, + i6.ReadDatabaseContainer( + db, + ).resultSet('user_entity').id, + ), + ); i5.$$UserEntityTableProcessedTableManager get ownerId { final $_column = $_itemColumn('owner_id')!; final manager = i5 .$$UserEntityTableTableManager( + $_db, + i6.ReadDatabaseContainer( $_db, - i6.ReadDatabaseContainer($_db) - .resultSet('user_entity')) + ).resultSet('user_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_ownerIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -82,59 +93,85 @@ class $$MemoryEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnFilters(column)); + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get deletedAt => $composableBuilder( - column: $table.deletedAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.deletedAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnWithTypeConverterFilters - get type => $composableBuilder( - column: $table.type, - builder: (column) => i0.ColumnWithTypeConverterFilters(column)); + get type => $composableBuilder( + column: $table.type, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); i0.ColumnFilters get data => $composableBuilder( - column: $table.data, builder: (column) => i0.ColumnFilters(column)); + column: $table.data, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get isSaved => $composableBuilder( - column: $table.isSaved, builder: (column) => i0.ColumnFilters(column)); + column: $table.isSaved, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get memoryAt => $composableBuilder( - column: $table.memoryAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.memoryAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get seenAt => $composableBuilder( - column: $table.seenAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.seenAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get showAt => $composableBuilder( - column: $table.showAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.showAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get hideAt => $composableBuilder( - column: $table.hideAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.hideAt, + builder: (column) => i0.ColumnFilters(column), + ); i5.$$UserEntityTableFilterComposer get ownerId { final i5.$$UserEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableFilterComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableFilterComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -149,60 +186,84 @@ class $$MemoryEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get createdAt => $composableBuilder( - column: $table.createdAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get updatedAt => $composableBuilder( - column: $table.updatedAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get deletedAt => $composableBuilder( - column: $table.deletedAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.deletedAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get type => $composableBuilder( - column: $table.type, builder: (column) => i0.ColumnOrderings(column)); + column: $table.type, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get data => $composableBuilder( - column: $table.data, builder: (column) => i0.ColumnOrderings(column)); + column: $table.data, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get isSaved => $composableBuilder( - column: $table.isSaved, builder: (column) => i0.ColumnOrderings(column)); + column: $table.isSaved, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get memoryAt => $composableBuilder( - column: $table.memoryAt, builder: (column) => i0.ColumnOrderings(column)); + column: $table.memoryAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get seenAt => $composableBuilder( - column: $table.seenAt, builder: (column) => i0.ColumnOrderings(column)); + column: $table.seenAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get showAt => $composableBuilder( - column: $table.showAt, builder: (column) => i0.ColumnOrderings(column)); + column: $table.showAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get hideAt => $composableBuilder( - column: $table.hideAt, builder: (column) => i0.ColumnOrderings(column)); + column: $table.hideAt, + builder: (column) => i0.ColumnOrderings(column), + ); i5.$$UserEntityTableOrderingComposer get ownerId { final i5.$$UserEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableOrderingComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -251,42 +312,52 @@ class $$MemoryEntityTableAnnotationComposer i5.$$UserEntityTableAnnotationComposer get ownerId { final i5.$$UserEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableAnnotationComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$MemoryEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$MemoryEntityTable, - i1.MemoryEntityData, - i1.$$MemoryEntityTableFilterComposer, - i1.$$MemoryEntityTableOrderingComposer, - i1.$$MemoryEntityTableAnnotationComposer, - $$MemoryEntityTableCreateCompanionBuilder, - $$MemoryEntityTableUpdateCompanionBuilder, - (i1.MemoryEntityData, i1.$$MemoryEntityTableReferences), - i1.MemoryEntityData, - i0.PrefetchHooks Function({bool ownerId})> { +class $$MemoryEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$MemoryEntityTable, + i1.MemoryEntityData, + i1.$$MemoryEntityTableFilterComposer, + i1.$$MemoryEntityTableOrderingComposer, + i1.$$MemoryEntityTableAnnotationComposer, + $$MemoryEntityTableCreateCompanionBuilder, + $$MemoryEntityTableUpdateCompanionBuilder, + (i1.MemoryEntityData, i1.$$MemoryEntityTableReferences), + i1.MemoryEntityData, + i0.PrefetchHooks Function({bool ownerId}) + > { $$MemoryEntityTableTableManager( - i0.GeneratedDatabase db, i1.$MemoryEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$MemoryEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -295,74 +366,77 @@ class $$MemoryEntityTableTableManager extends i0.RootTableManager< i1.$$MemoryEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$MemoryEntityTableAnnotationComposer($db: db, $table: table), - updateCompanionCallback: ({ - i0.Value id = const i0.Value.absent(), - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value deletedAt = const i0.Value.absent(), - i0.Value ownerId = const i0.Value.absent(), - i0.Value type = const i0.Value.absent(), - i0.Value data = const i0.Value.absent(), - i0.Value isSaved = const i0.Value.absent(), - i0.Value memoryAt = const i0.Value.absent(), - i0.Value seenAt = const i0.Value.absent(), - i0.Value showAt = const i0.Value.absent(), - i0.Value hideAt = const i0.Value.absent(), - }) => - i1.MemoryEntityCompanion( - id: id, - createdAt: createdAt, - updatedAt: updatedAt, - deletedAt: deletedAt, - ownerId: ownerId, - type: type, - data: data, - isSaved: isSaved, - memoryAt: memoryAt, - seenAt: seenAt, - showAt: showAt, - hideAt: hideAt, - ), - createCompanionCallback: ({ - required String id, - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value deletedAt = const i0.Value.absent(), - required String ownerId, - required i2.MemoryTypeEnum type, - required String data, - i0.Value isSaved = const i0.Value.absent(), - required DateTime memoryAt, - i0.Value seenAt = const i0.Value.absent(), - i0.Value showAt = const i0.Value.absent(), - i0.Value hideAt = const i0.Value.absent(), - }) => - i1.MemoryEntityCompanion.insert( - id: id, - createdAt: createdAt, - updatedAt: updatedAt, - deletedAt: deletedAt, - ownerId: ownerId, - type: type, - data: data, - isSaved: isSaved, - memoryAt: memoryAt, - seenAt: seenAt, - showAt: showAt, - hideAt: hideAt, - ), + updateCompanionCallback: + ({ + i0.Value id = const i0.Value.absent(), + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value deletedAt = const i0.Value.absent(), + i0.Value ownerId = const i0.Value.absent(), + i0.Value type = const i0.Value.absent(), + i0.Value data = const i0.Value.absent(), + i0.Value isSaved = const i0.Value.absent(), + i0.Value memoryAt = const i0.Value.absent(), + i0.Value seenAt = const i0.Value.absent(), + i0.Value showAt = const i0.Value.absent(), + i0.Value hideAt = const i0.Value.absent(), + }) => i1.MemoryEntityCompanion( + id: id, + createdAt: createdAt, + updatedAt: updatedAt, + deletedAt: deletedAt, + ownerId: ownerId, + type: type, + data: data, + isSaved: isSaved, + memoryAt: memoryAt, + seenAt: seenAt, + showAt: showAt, + hideAt: hideAt, + ), + createCompanionCallback: + ({ + required String id, + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value deletedAt = const i0.Value.absent(), + required String ownerId, + required i2.MemoryTypeEnum type, + required String data, + i0.Value isSaved = const i0.Value.absent(), + required DateTime memoryAt, + i0.Value seenAt = const i0.Value.absent(), + i0.Value showAt = const i0.Value.absent(), + i0.Value hideAt = const i0.Value.absent(), + }) => i1.MemoryEntityCompanion.insert( + id: id, + createdAt: createdAt, + updatedAt: updatedAt, + deletedAt: deletedAt, + ownerId: ownerId, + type: type, + data: data, + isSaved: isSaved, + memoryAt: memoryAt, + seenAt: seenAt, + showAt: showAt, + hideAt: hideAt, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$MemoryEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$MemoryEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({ownerId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -373,40 +447,50 @@ class $$MemoryEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (ownerId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.ownerId, - referencedTable: - i1.$$MemoryEntityTableReferences._ownerIdTable(db), - referencedColumn: - i1.$$MemoryEntityTableReferences._ownerIdTable(db).id, - ) as T; - } + dynamic + > + >(state) { + if (ownerId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.ownerId, + referencedTable: i1 + .$$MemoryEntityTableReferences + ._ownerIdTable(db), + referencedColumn: i1 + .$$MemoryEntityTableReferences + ._ownerIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$MemoryEntityTableProcessedTableManager = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$MemoryEntityTable, - i1.MemoryEntityData, - i1.$$MemoryEntityTableFilterComposer, - i1.$$MemoryEntityTableOrderingComposer, - i1.$$MemoryEntityTableAnnotationComposer, - $$MemoryEntityTableCreateCompanionBuilder, - $$MemoryEntityTableUpdateCompanionBuilder, - (i1.MemoryEntityData, i1.$$MemoryEntityTableReferences), - i1.MemoryEntityData, - i0.PrefetchHooks Function({bool ownerId})>; +typedef $$MemoryEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$MemoryEntityTable, + i1.MemoryEntityData, + i1.$$MemoryEntityTableFilterComposer, + i1.$$MemoryEntityTableOrderingComposer, + i1.$$MemoryEntityTableAnnotationComposer, + $$MemoryEntityTableCreateCompanionBuilder, + $$MemoryEntityTableUpdateCompanionBuilder, + (i1.MemoryEntityData, i1.$$MemoryEntityTableReferences), + i1.MemoryEntityData, + i0.PrefetchHooks Function({bool ownerId}) + >; class $MemoryEntityTable extends i3.MemoryEntity with i0.TableInfo<$MemoryEntityTable, i1.MemoryEntityData> { @@ -417,100 +501,159 @@ class $MemoryEntityTable extends i3.MemoryEntity static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); @override late final i0.GeneratedColumn id = i0.GeneratedColumn( - 'id', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _createdAtMeta = - const i0.VerificationMeta('createdAt'); + 'id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _createdAtMeta = const i0.VerificationMeta( + 'createdAt', + ); @override late final i0.GeneratedColumn createdAt = - i0.GeneratedColumn('created_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i4.currentDateAndTime); - static const i0.VerificationMeta _updatedAtMeta = - const i0.VerificationMeta('updatedAt'); + i0.GeneratedColumn( + 'created_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); + static const i0.VerificationMeta _updatedAtMeta = const i0.VerificationMeta( + 'updatedAt', + ); @override late final i0.GeneratedColumn updatedAt = - i0.GeneratedColumn('updated_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i4.currentDateAndTime); - static const i0.VerificationMeta _deletedAtMeta = - const i0.VerificationMeta('deletedAt'); + i0.GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); + static const i0.VerificationMeta _deletedAtMeta = const i0.VerificationMeta( + 'deletedAt', + ); @override late final i0.GeneratedColumn deletedAt = - i0.GeneratedColumn('deleted_at', aliasedName, true, - type: i0.DriftSqlType.dateTime, requiredDuringInsert: false); - static const i0.VerificationMeta _ownerIdMeta = - const i0.VerificationMeta('ownerId'); + i0.GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _ownerIdMeta = const i0.VerificationMeta( + 'ownerId', + ); @override late final i0.GeneratedColumn ownerId = i0.GeneratedColumn( - 'owner_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'owner_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); @override late final i0.GeneratedColumnWithTypeConverter type = - i0.GeneratedColumn('type', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true) - .withConverter( - i1.$MemoryEntityTable.$convertertype); - static const i0.VerificationMeta _dataMeta = - const i0.VerificationMeta('data'); + i0.GeneratedColumn( + 'type', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ).withConverter(i1.$MemoryEntityTable.$convertertype); + static const i0.VerificationMeta _dataMeta = const i0.VerificationMeta( + 'data', + ); @override late final i0.GeneratedColumn data = i0.GeneratedColumn( - 'data', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _isSavedMeta = - const i0.VerificationMeta('isSaved'); + 'data', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _isSavedMeta = const i0.VerificationMeta( + 'isSaved', + ); @override late final i0.GeneratedColumn isSaved = i0.GeneratedColumn( - 'is_saved', aliasedName, false, - type: i0.DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - i0.GeneratedColumn.constraintIsAlways('CHECK ("is_saved" IN (0, 1))'), - defaultValue: const i4.Constant(false)); - static const i0.VerificationMeta _memoryAtMeta = - const i0.VerificationMeta('memoryAt'); + 'is_saved', + aliasedName, + false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const i4.Constant(false), + ); + static const i0.VerificationMeta _memoryAtMeta = const i0.VerificationMeta( + 'memoryAt', + ); @override late final i0.GeneratedColumn memoryAt = - i0.GeneratedColumn('memory_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, requiredDuringInsert: true); - static const i0.VerificationMeta _seenAtMeta = - const i0.VerificationMeta('seenAt'); + i0.GeneratedColumn( + 'memory_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _seenAtMeta = const i0.VerificationMeta( + 'seenAt', + ); @override late final i0.GeneratedColumn seenAt = i0.GeneratedColumn( - 'seen_at', aliasedName, true, - type: i0.DriftSqlType.dateTime, requiredDuringInsert: false); - static const i0.VerificationMeta _showAtMeta = - const i0.VerificationMeta('showAt'); + 'seen_at', + aliasedName, + true, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _showAtMeta = const i0.VerificationMeta( + 'showAt', + ); @override late final i0.GeneratedColumn showAt = i0.GeneratedColumn( - 'show_at', aliasedName, true, - type: i0.DriftSqlType.dateTime, requiredDuringInsert: false); - static const i0.VerificationMeta _hideAtMeta = - const i0.VerificationMeta('hideAt'); + 'show_at', + aliasedName, + true, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _hideAtMeta = const i0.VerificationMeta( + 'hideAt', + ); @override late final i0.GeneratedColumn hideAt = i0.GeneratedColumn( - 'hide_at', aliasedName, true, - type: i0.DriftSqlType.dateTime, requiredDuringInsert: false); + 'hide_at', + aliasedName, + true, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + ); @override List get $columns => [ - id, - createdAt, - updatedAt, - deletedAt, - ownerId, - type, - data, - isSaved, - memoryAt, - seenAt, - showAt, - hideAt - ]; + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -518,8 +661,9 @@ class $MemoryEntityTable extends i3.MemoryEntity static const String $name = 'memory_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('id')) { @@ -528,50 +672,70 @@ class $MemoryEntityTable extends i3.MemoryEntity context.missing(_idMeta); } if (data.containsKey('created_at')) { - context.handle(_createdAtMeta, - createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); + context.handle( + _createdAtMeta, + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta), + ); } if (data.containsKey('updated_at')) { - context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); + context.handle( + _updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), + ); } if (data.containsKey('deleted_at')) { - context.handle(_deletedAtMeta, - deletedAt.isAcceptableOrUnknown(data['deleted_at']!, _deletedAtMeta)); + context.handle( + _deletedAtMeta, + deletedAt.isAcceptableOrUnknown(data['deleted_at']!, _deletedAtMeta), + ); } if (data.containsKey('owner_id')) { - context.handle(_ownerIdMeta, - ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta)); + context.handle( + _ownerIdMeta, + ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta), + ); } else if (isInserting) { context.missing(_ownerIdMeta); } if (data.containsKey('data')) { context.handle( - _dataMeta, this.data.isAcceptableOrUnknown(data['data']!, _dataMeta)); + _dataMeta, + this.data.isAcceptableOrUnknown(data['data']!, _dataMeta), + ); } else if (isInserting) { context.missing(_dataMeta); } if (data.containsKey('is_saved')) { - context.handle(_isSavedMeta, - isSaved.isAcceptableOrUnknown(data['is_saved']!, _isSavedMeta)); + context.handle( + _isSavedMeta, + isSaved.isAcceptableOrUnknown(data['is_saved']!, _isSavedMeta), + ); } if (data.containsKey('memory_at')) { - context.handle(_memoryAtMeta, - memoryAt.isAcceptableOrUnknown(data['memory_at']!, _memoryAtMeta)); + context.handle( + _memoryAtMeta, + memoryAt.isAcceptableOrUnknown(data['memory_at']!, _memoryAtMeta), + ); } else if (isInserting) { context.missing(_memoryAtMeta); } if (data.containsKey('seen_at')) { - context.handle(_seenAtMeta, - seenAt.isAcceptableOrUnknown(data['seen_at']!, _seenAtMeta)); + context.handle( + _seenAtMeta, + seenAt.isAcceptableOrUnknown(data['seen_at']!, _seenAtMeta), + ); } if (data.containsKey('show_at')) { - context.handle(_showAtMeta, - showAt.isAcceptableOrUnknown(data['show_at']!, _showAtMeta)); + context.handle( + _showAtMeta, + showAt.isAcceptableOrUnknown(data['show_at']!, _showAtMeta), + ); } if (data.containsKey('hide_at')) { - context.handle(_hideAtMeta, - hideAt.isAcceptableOrUnknown(data['hide_at']!, _hideAtMeta)); + context.handle( + _hideAtMeta, + hideAt.isAcceptableOrUnknown(data['hide_at']!, _hideAtMeta), + ); } return context; } @@ -582,31 +746,56 @@ class $MemoryEntityTable extends i3.MemoryEntity i1.MemoryEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.MemoryEntityData( - id: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!, + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}id'], + )!, createdAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, updatedAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - deletedAt: attachedDatabase.typeMapping - .read(i0.DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), - ownerId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - type: i1.$MemoryEntityTable.$convertertype.fromSql(attachedDatabase - .typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}type'])!), - data: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}data'])!, - isSaved: attachedDatabase.typeMapping - .read(i0.DriftSqlType.bool, data['${effectivePrefix}is_saved'])!, - memoryAt: attachedDatabase.typeMapping - .read(i0.DriftSqlType.dateTime, data['${effectivePrefix}memory_at'])!, - seenAt: attachedDatabase.typeMapping - .read(i0.DriftSqlType.dateTime, data['${effectivePrefix}seen_at']), - showAt: attachedDatabase.typeMapping - .read(i0.DriftSqlType.dateTime, data['${effectivePrefix}show_at']), - hideAt: attachedDatabase.typeMapping - .read(i0.DriftSqlType.dateTime, data['${effectivePrefix}hide_at']), + i0.DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + deletedAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + ownerId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + type: i1.$MemoryEntityTable.$convertertype.fromSql( + attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + ), + data: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}data'], + )!, + isSaved: attachedDatabase.typeMapping.read( + i0.DriftSqlType.bool, + data['${effectivePrefix}is_saved'], + )!, + memoryAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, + data['${effectivePrefix}memory_at'], + )!, + seenAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, + data['${effectivePrefix}seen_at'], + ), + showAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, + data['${effectivePrefix}show_at'], + ), + hideAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, + data['${effectivePrefix}hide_at'], + ), ); } @@ -637,19 +826,20 @@ class MemoryEntityData extends i0.DataClass final DateTime? seenAt; final DateTime? showAt; final DateTime? hideAt; - const MemoryEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - this.deletedAt, - required this.ownerId, - required this.type, - required this.data, - required this.isSaved, - required this.memoryAt, - this.seenAt, - this.showAt, - this.hideAt}); + const MemoryEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -661,8 +851,9 @@ class MemoryEntityData extends i0.DataClass } map['owner_id'] = i0.Variable(ownerId); { - map['type'] = - i0.Variable(i1.$MemoryEntityTable.$convertertype.toSql(type)); + map['type'] = i0.Variable( + i1.$MemoryEntityTable.$convertertype.toSql(type), + ); } map['data'] = i0.Variable(data); map['is_saved'] = i0.Variable(isSaved); @@ -679,8 +870,10 @@ class MemoryEntityData extends i0.DataClass return map; } - factory MemoryEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory MemoryEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return MemoryEntityData( id: serializer.fromJson(json['id']), @@ -688,8 +881,9 @@ class MemoryEntityData extends i0.DataClass updatedAt: serializer.fromJson(json['updatedAt']), deletedAt: serializer.fromJson(json['deletedAt']), ownerId: serializer.fromJson(json['ownerId']), - type: i1.$MemoryEntityTable.$convertertype - .fromJson(serializer.fromJson(json['type'])), + type: i1.$MemoryEntityTable.$convertertype.fromJson( + serializer.fromJson(json['type']), + ), data: serializer.fromJson(json['data']), isSaved: serializer.fromJson(json['isSaved']), memoryAt: serializer.fromJson(json['memoryAt']), @@ -707,8 +901,9 @@ class MemoryEntityData extends i0.DataClass 'updatedAt': serializer.toJson(updatedAt), 'deletedAt': serializer.toJson(deletedAt), 'ownerId': serializer.toJson(ownerId), - 'type': serializer - .toJson(i1.$MemoryEntityTable.$convertertype.toJson(type)), + 'type': serializer.toJson( + i1.$MemoryEntityTable.$convertertype.toJson(type), + ), 'data': serializer.toJson(data), 'isSaved': serializer.toJson(isSaved), 'memoryAt': serializer.toJson(memoryAt), @@ -718,33 +913,33 @@ class MemoryEntityData extends i0.DataClass }; } - i1.MemoryEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - i0.Value deletedAt = const i0.Value.absent(), - String? ownerId, - i2.MemoryTypeEnum? type, - String? data, - bool? isSaved, - DateTime? memoryAt, - i0.Value seenAt = const i0.Value.absent(), - i0.Value showAt = const i0.Value.absent(), - i0.Value hideAt = const i0.Value.absent()}) => - i1.MemoryEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, - ownerId: ownerId ?? this.ownerId, - type: type ?? this.type, - data: data ?? this.data, - isSaved: isSaved ?? this.isSaved, - memoryAt: memoryAt ?? this.memoryAt, - seenAt: seenAt.present ? seenAt.value : this.seenAt, - showAt: showAt.present ? showAt.value : this.showAt, - hideAt: hideAt.present ? hideAt.value : this.hideAt, - ); + i1.MemoryEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + i0.Value deletedAt = const i0.Value.absent(), + String? ownerId, + i2.MemoryTypeEnum? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + i0.Value seenAt = const i0.Value.absent(), + i0.Value showAt = const i0.Value.absent(), + i0.Value hideAt = const i0.Value.absent(), + }) => i1.MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); MemoryEntityData copyWithCompanion(i1.MemoryEntityCompanion data) { return MemoryEntityData( id: data.id.present ? data.id.value : this.id, @@ -782,8 +977,20 @@ class MemoryEntityData extends i0.DataClass } @override - int get hashCode => Object.hash(id, createdAt, updatedAt, deletedAt, ownerId, - type, data, isSaved, memoryAt, seenAt, showAt, hideAt); + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -842,11 +1049,11 @@ class MemoryEntityCompanion extends i0.UpdateCompanion { this.seenAt = const i0.Value.absent(), this.showAt = const i0.Value.absent(), this.hideAt = const i0.Value.absent(), - }) : id = i0.Value(id), - ownerId = i0.Value(ownerId), - type = i0.Value(type), - data = i0.Value(data), - memoryAt = i0.Value(memoryAt); + }) : id = i0.Value(id), + ownerId = i0.Value(ownerId), + type = i0.Value(type), + data = i0.Value(data), + memoryAt = i0.Value(memoryAt); static i0.Insertable custom({ i0.Expression? id, i0.Expression? createdAt, @@ -877,19 +1084,20 @@ class MemoryEntityCompanion extends i0.UpdateCompanion { }); } - i1.MemoryEntityCompanion copyWith( - {i0.Value? id, - i0.Value? createdAt, - i0.Value? updatedAt, - i0.Value? deletedAt, - i0.Value? ownerId, - i0.Value? type, - i0.Value? data, - i0.Value? isSaved, - i0.Value? memoryAt, - i0.Value? seenAt, - i0.Value? showAt, - i0.Value? hideAt}) { + i1.MemoryEntityCompanion copyWith({ + i0.Value? id, + i0.Value? createdAt, + i0.Value? updatedAt, + i0.Value? deletedAt, + i0.Value? ownerId, + i0.Value? type, + i0.Value? data, + i0.Value? isSaved, + i0.Value? memoryAt, + i0.Value? seenAt, + i0.Value? showAt, + i0.Value? hideAt, + }) { return i1.MemoryEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -926,7 +1134,8 @@ class MemoryEntityCompanion extends i0.UpdateCompanion { } if (type.present) { map['type'] = i0.Variable( - i1.$MemoryEntityTable.$convertertype.toSql(type.value)); + i1.$MemoryEntityTable.$convertertype.toSql(type.value), + ); } if (data.present) { map['data'] = i0.Variable(data.value); diff --git a/mobile/lib/infrastructure/entities/memory_asset.entity.dart b/mobile/lib/infrastructure/entities/memory_asset.entity.dart index c304b03724..5053afdfb3 100644 --- a/mobile/lib/infrastructure/entities/memory_asset.entity.dart +++ b/mobile/lib/infrastructure/entities/memory_asset.entity.dart @@ -6,11 +6,9 @@ import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; class MemoryAssetEntity extends Table with DriftDefaultsMixin { const MemoryAssetEntity(); - TextColumn get assetId => - text().references(RemoteAssetEntity, #id, onDelete: KeyAction.cascade)(); + TextColumn get assetId => text().references(RemoteAssetEntity, #id, onDelete: KeyAction.cascade)(); - TextColumn get memoryId => - text().references(MemoryEntity, #id, onDelete: KeyAction.cascade)(); + TextColumn get memoryId => text().references(MemoryEntity, #id, onDelete: KeyAction.cascade)(); @override Set get primaryKey => {assetId, memoryId}; diff --git a/mobile/lib/infrastructure/entities/memory_asset.entity.drift.dart b/mobile/lib/infrastructure/entities/memory_asset.entity.drift.dart index 9253e8bc05..eedeb85e5c 100644 --- a/mobile/lib/infrastructure/entities/memory_asset.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/memory_asset.entity.drift.dart @@ -11,74 +11,92 @@ import 'package:drift/internal/modular.dart' as i4; import 'package:immich_mobile/infrastructure/entities/memory.entity.drift.dart' as i5; -typedef $$MemoryAssetEntityTableCreateCompanionBuilder - = i1.MemoryAssetEntityCompanion Function({ - required String assetId, - required String memoryId, -}); -typedef $$MemoryAssetEntityTableUpdateCompanionBuilder - = i1.MemoryAssetEntityCompanion Function({ - i0.Value assetId, - i0.Value memoryId, -}); +typedef $$MemoryAssetEntityTableCreateCompanionBuilder = + i1.MemoryAssetEntityCompanion Function({ + required String assetId, + required String memoryId, + }); +typedef $$MemoryAssetEntityTableUpdateCompanionBuilder = + i1.MemoryAssetEntityCompanion Function({ + i0.Value assetId, + i0.Value memoryId, + }); -final class $$MemoryAssetEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, - i1.$MemoryAssetEntityTable, - i1.MemoryAssetEntityData> { +final class $$MemoryAssetEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$MemoryAssetEntityTable, + i1.MemoryAssetEntityData + > { $$MemoryAssetEntityTableReferences( - super.$_db, super.$_table, super.$_typedResult); + super.$_db, + super.$_table, + super.$_typedResult, + ); static i3.$RemoteAssetEntityTable _assetIdTable(i0.GeneratedDatabase db) => i4.ReadDatabaseContainer(db) .resultSet('remote_asset_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i4.ReadDatabaseContainer(db) .resultSet('memory_asset_entity') .assetId, - i4.ReadDatabaseContainer(db) - .resultSet('remote_asset_entity') - .id)); + i4.ReadDatabaseContainer( + db, + ).resultSet('remote_asset_entity').id, + ), + ); i3.$$RemoteAssetEntityTableProcessedTableManager get assetId { final $_column = $_itemColumn('asset_id')!; final manager = i3 .$$RemoteAssetEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer( $_db, - i4.ReadDatabaseContainer($_db) - .resultSet('remote_asset_entity')) + ).resultSet('remote_asset_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_assetIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } static i5.$MemoryEntityTable _memoryIdTable(i0.GeneratedDatabase db) => i4.ReadDatabaseContainer(db) .resultSet('memory_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i4.ReadDatabaseContainer(db) .resultSet('memory_asset_entity') .memoryId, - i4.ReadDatabaseContainer(db) - .resultSet('memory_entity') - .id)); + i4.ReadDatabaseContainer( + db, + ).resultSet('memory_entity').id, + ), + ); i5.$$MemoryEntityTableProcessedTableManager get memoryId { final $_column = $_itemColumn('memory_id')!; final manager = i5 .$$MemoryEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer( $_db, - i4.ReadDatabaseContainer($_db) - .resultSet('memory_entity')) + ).resultSet('memory_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_memoryIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -93,45 +111,55 @@ class $$MemoryAssetEntityTableFilterComposer }); i3.$$RemoteAssetEntityTableFilterComposer get assetId { final i3.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableFilterComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$MemoryEntityTableFilterComposer get memoryId { final i5.$$MemoryEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.memoryId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('memory_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$MemoryEntityTableFilterComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('memory_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.memoryId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('memory_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$MemoryEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('memory_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -148,46 +176,55 @@ class $$MemoryAssetEntityTableOrderingComposer i3.$$RemoteAssetEntityTableOrderingComposer get assetId { final i3.$$RemoteAssetEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableOrderingComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$MemoryEntityTableOrderingComposer get memoryId { final i5.$$MemoryEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.memoryId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('memory_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$MemoryEntityTableOrderingComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('memory_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.memoryId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('memory_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$MemoryEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('memory_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -204,65 +241,79 @@ class $$MemoryAssetEntityTableAnnotationComposer i3.$$RemoteAssetEntityTableAnnotationComposer get assetId { final i3.$$RemoteAssetEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableAnnotationComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$MemoryEntityTableAnnotationComposer get memoryId { final i5.$$MemoryEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.memoryId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('memory_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$MemoryEntityTableAnnotationComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('memory_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.memoryId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('memory_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$MemoryEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('memory_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$MemoryAssetEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$MemoryAssetEntityTable, - i1.MemoryAssetEntityData, - i1.$$MemoryAssetEntityTableFilterComposer, - i1.$$MemoryAssetEntityTableOrderingComposer, - i1.$$MemoryAssetEntityTableAnnotationComposer, - $$MemoryAssetEntityTableCreateCompanionBuilder, - $$MemoryAssetEntityTableUpdateCompanionBuilder, - (i1.MemoryAssetEntityData, i1.$$MemoryAssetEntityTableReferences), - i1.MemoryAssetEntityData, - i0.PrefetchHooks Function({bool assetId, bool memoryId})> { +class $$MemoryAssetEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$MemoryAssetEntityTable, + i1.MemoryAssetEntityData, + i1.$$MemoryAssetEntityTableFilterComposer, + i1.$$MemoryAssetEntityTableOrderingComposer, + i1.$$MemoryAssetEntityTableAnnotationComposer, + $$MemoryAssetEntityTableCreateCompanionBuilder, + $$MemoryAssetEntityTableUpdateCompanionBuilder, + (i1.MemoryAssetEntityData, i1.$$MemoryAssetEntityTableReferences), + i1.MemoryAssetEntityData, + i0.PrefetchHooks Function({bool assetId, bool memoryId}) + > { $$MemoryAssetEntityTableTableManager( - i0.GeneratedDatabase db, i1.$MemoryAssetEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$MemoryAssetEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -271,35 +322,38 @@ class $$MemoryAssetEntityTableTableManager extends i0.RootTableManager< .$$MemoryAssetEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$MemoryAssetEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value assetId = const i0.Value.absent(), - i0.Value memoryId = const i0.Value.absent(), - }) => - i1.MemoryAssetEntityCompanion( - assetId: assetId, - memoryId: memoryId, - ), - createCompanionCallback: ({ - required String assetId, - required String memoryId, - }) => - i1.MemoryAssetEntityCompanion.insert( - assetId: assetId, - memoryId: memoryId, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value assetId = const i0.Value.absent(), + i0.Value memoryId = const i0.Value.absent(), + }) => i1.MemoryAssetEntityCompanion( + assetId: assetId, + memoryId: memoryId, + ), + createCompanionCallback: + ({required String assetId, required String memoryId}) => + i1.MemoryAssetEntityCompanion.insert( + assetId: assetId, + memoryId: memoryId, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$MemoryAssetEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$MemoryAssetEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({assetId = false, memoryId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -310,53 +364,65 @@ class $$MemoryAssetEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (assetId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.assetId, - referencedTable: - i1.$$MemoryAssetEntityTableReferences._assetIdTable(db), - referencedColumn: i1.$$MemoryAssetEntityTableReferences - ._assetIdTable(db) - .id, - ) as T; - } - if (memoryId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.memoryId, - referencedTable: i1.$$MemoryAssetEntityTableReferences - ._memoryIdTable(db), - referencedColumn: i1.$$MemoryAssetEntityTableReferences - ._memoryIdTable(db) - .id, - ) as T; - } + dynamic + > + >(state) { + if (assetId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.assetId, + referencedTable: i1 + .$$MemoryAssetEntityTableReferences + ._assetIdTable(db), + referencedColumn: i1 + .$$MemoryAssetEntityTableReferences + ._assetIdTable(db) + .id, + ) + as T; + } + if (memoryId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.memoryId, + referencedTable: i1 + .$$MemoryAssetEntityTableReferences + ._memoryIdTable(db), + referencedColumn: i1 + .$$MemoryAssetEntityTableReferences + ._memoryIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$MemoryAssetEntityTableProcessedTableManager - = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$MemoryAssetEntityTable, - i1.MemoryAssetEntityData, - i1.$$MemoryAssetEntityTableFilterComposer, - i1.$$MemoryAssetEntityTableOrderingComposer, - i1.$$MemoryAssetEntityTableAnnotationComposer, - $$MemoryAssetEntityTableCreateCompanionBuilder, - $$MemoryAssetEntityTableUpdateCompanionBuilder, - (i1.MemoryAssetEntityData, i1.$$MemoryAssetEntityTableReferences), - i1.MemoryAssetEntityData, - i0.PrefetchHooks Function({bool assetId, bool memoryId})>; +typedef $$MemoryAssetEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$MemoryAssetEntityTable, + i1.MemoryAssetEntityData, + i1.$$MemoryAssetEntityTableFilterComposer, + i1.$$MemoryAssetEntityTableOrderingComposer, + i1.$$MemoryAssetEntityTableAnnotationComposer, + $$MemoryAssetEntityTableCreateCompanionBuilder, + $$MemoryAssetEntityTableUpdateCompanionBuilder, + (i1.MemoryAssetEntityData, i1.$$MemoryAssetEntityTableReferences), + i1.MemoryAssetEntityData, + i0.PrefetchHooks Function({bool assetId, bool memoryId}) + >; class $MemoryAssetEntityTable extends i2.MemoryAssetEntity with i0.TableInfo<$MemoryAssetEntityTable, i1.MemoryAssetEntityData> { @@ -364,24 +430,34 @@ class $MemoryAssetEntityTable extends i2.MemoryAssetEntity final i0.GeneratedDatabase attachedDatabase; final String? _alias; $MemoryAssetEntityTable(this.attachedDatabase, [this._alias]); - static const i0.VerificationMeta _assetIdMeta = - const i0.VerificationMeta('assetId'); + static const i0.VerificationMeta _assetIdMeta = const i0.VerificationMeta( + 'assetId', + ); @override late final i0.GeneratedColumn assetId = i0.GeneratedColumn( - 'asset_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - static const i0.VerificationMeta _memoryIdMeta = - const i0.VerificationMeta('memoryId'); + 'asset_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + static const i0.VerificationMeta _memoryIdMeta = const i0.VerificationMeta( + 'memoryId', + ); @override late final i0.GeneratedColumn memoryId = i0.GeneratedColumn( - 'memory_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES memory_entity (id) ON DELETE CASCADE')); + 'memory_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, memoryId]; @override @@ -391,19 +467,24 @@ class $MemoryAssetEntityTable extends i2.MemoryAssetEntity static const String $name = 'memory_asset_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('asset_id')) { - context.handle(_assetIdMeta, - assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta)); + context.handle( + _assetIdMeta, + assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta), + ); } else if (isInserting) { context.missing(_assetIdMeta); } if (data.containsKey('memory_id')) { - context.handle(_memoryIdMeta, - memoryId.isAcceptableOrUnknown(data['memory_id']!, _memoryIdMeta)); + context.handle( + _memoryIdMeta, + memoryId.isAcceptableOrUnknown(data['memory_id']!, _memoryIdMeta), + ); } else if (isInserting) { context.missing(_memoryIdMeta); } @@ -413,14 +494,20 @@ class $MemoryAssetEntityTable extends i2.MemoryAssetEntity @override Set get $primaryKey => {assetId, memoryId}; @override - i1.MemoryAssetEntityData map(Map data, - {String? tablePrefix}) { + i1.MemoryAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.MemoryAssetEntityData( - assetId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - memoryId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}memory_id'])!, + assetId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + memoryId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}memory_id'], + )!, ); } @@ -448,8 +535,10 @@ class MemoryAssetEntityData extends i0.DataClass return map; } - factory MemoryAssetEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory MemoryAssetEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return MemoryAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -507,8 +596,8 @@ class MemoryAssetEntityCompanion MemoryAssetEntityCompanion.insert({ required String assetId, required String memoryId, - }) : assetId = i0.Value(assetId), - memoryId = i0.Value(memoryId); + }) : assetId = i0.Value(assetId), + memoryId = i0.Value(memoryId); static i0.Insertable custom({ i0.Expression? assetId, i0.Expression? memoryId, @@ -519,8 +608,10 @@ class MemoryAssetEntityCompanion }); } - i1.MemoryAssetEntityCompanion copyWith( - {i0.Value? assetId, i0.Value? memoryId}) { + i1.MemoryAssetEntityCompanion copyWith({ + i0.Value? assetId, + i0.Value? memoryId, + }) { return i1.MemoryAssetEntityCompanion( assetId: assetId ?? this.assetId, memoryId: memoryId ?? this.memoryId, diff --git a/mobile/lib/infrastructure/entities/merged_asset.drift b/mobile/lib/infrastructure/entities/merged_asset.drift index 3dc7221c15..d1377f6685 100644 --- a/mobile/lib/infrastructure/entities/merged_asset.drift +++ b/mobile/lib/infrastructure/entities/merged_asset.drift @@ -1,76 +1,73 @@ import 'remote_asset.entity.dart'; -import 'local_asset.entity.dart'; import 'stack.entity.dart'; +import 'local_asset.entity.dart'; +import 'local_album.entity.dart'; +import 'local_album_asset.entity.dart'; -mergedAsset: SELECT * FROM -( - SELECT - rae.id as remote_id, - lae.id as local_id, - rae.name, - rae."type", - rae.created_at, - rae.updated_at, - rae.width, - rae.height, - rae.duration_in_seconds, - rae.is_favorite, - rae.thumb_hash, - rae.checksum, - rae.owner_id, - rae.live_photo_video_id, - 0 as orientation, - rae.stack_id, - COALESCE(stack_count.total_count, 0) AS stack_count - FROM - remote_asset_entity rae - LEFT JOIN - local_asset_entity lae ON rae.checksum = lae.checksum - LEFT JOIN - stack_entity se ON rae.stack_id = se.id - LEFT JOIN - (SELECT - stack_id, - COUNT(*) AS total_count - FROM remote_asset_entity - WHERE deleted_at IS NULL - AND visibility = 0 - AND stack_id IS NOT NULL - GROUP BY stack_id - ) AS stack_count ON rae.stack_id = stack_count.stack_id - WHERE - rae.deleted_at IS NULL - AND rae.visibility = 0 - AND rae.owner_id in ? - AND ( - rae.stack_id IS NULL - OR rae.id = se.primary_asset_id - ) - UNION ALL - SELECT - NULL as remote_id, - lae.id as local_id, - lae.name, - lae."type", - lae.created_at, - lae.updated_at, - lae.width, - lae.height, - lae.duration_in_seconds, - lae.is_favorite, - NULL as thumb_hash, - lae.checksum, - NULL as owner_id, - NULL as live_photo_video_id, - lae.orientation, - NULL as stack_id, - 0 AS stack_count - FROM - local_asset_entity lae - LEFT JOIN - remote_asset_entity rae ON rae.checksum = lae.checksum - WHERE - rae.id IS NULL +mergedAsset: +SELECT + rae.id as remote_id, + (SELECT lae.id FROM local_asset_entity lae WHERE lae.checksum = rae.checksum LIMIT 1) as local_id, + rae.name, + rae."type", + rae.created_at as created_at, + rae.updated_at, + rae.width, + rae.height, + rae.duration_in_seconds, + rae.is_favorite, + rae.thumb_hash, + rae.checksum, + rae.owner_id, + rae.live_photo_video_id, + 0 as orientation, + rae.stack_id +FROM + remote_asset_entity rae +LEFT JOIN + stack_entity se ON rae.stack_id = se.id +WHERE + rae.deleted_at IS NULL + AND rae.visibility = 0 -- timeline visibility + AND rae.owner_id IN :user_ids + AND ( + rae.stack_id IS NULL + OR rae.id = se.primary_asset_id + ) + +UNION ALL + +SELECT + NULL as remote_id, + lae.id as local_id, + lae.name, + lae."type", + lae.created_at as created_at, + lae.updated_at, + lae.width, + lae.height, + lae.duration_in_seconds, + lae.is_favorite, + NULL as thumb_hash, + lae.checksum, + NULL as owner_id, + NULL as live_photo_video_id, + lae.orientation, + NULL as stack_id +FROM + local_asset_entity lae +WHERE NOT EXISTS ( + SELECT 1 FROM remote_asset_entity rae WHERE rae.checksum = lae.checksum AND rae.owner_id IN :user_ids +) +AND EXISTS ( + SELECT 1 FROM local_album_asset_entity laa + INNER JOIN local_album_entity la on laa.album_id = la.id + WHERE laa.asset_id = lae.id AND la.backup_selection = 0 -- selected +) +AND NOT EXISTS ( + SELECT 1 FROM local_album_asset_entity laa + INNER JOIN local_album_entity la on laa.album_id = la.id + WHERE laa.asset_id = lae.id AND la.backup_selection = 2 -- excluded ) ORDER BY created_at DESC LIMIT $limit; @@ -85,32 +82,37 @@ SELECT FROM ( SELECT - rae.name, rae.created_at FROM remote_asset_entity rae - LEFT JOIN - local_asset_entity lae ON rae.checksum = lae.checksum LEFT JOIN stack_entity se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL - AND rae.visibility = 0 - AND rae.owner_id in ? + AND rae.visibility = 0 -- timeline visibility + AND rae.owner_id in :user_ids AND ( rae.stack_id IS NULL OR rae.id = se.primary_asset_id ) UNION ALL SELECT - lae.name, lae.created_at FROM local_asset_entity lae - LEFT JOIN - remote_asset_entity rae ON rae.checksum = lae.checksum - WHERE - rae.id IS NULL + WHERE NOT EXISTS ( + SELECT 1 FROM remote_asset_entity rae WHERE rae.checksum = lae.checksum AND rae.owner_id IN :user_ids + ) + AND EXISTS ( + SELECT 1 FROM local_album_asset_entity laa + INNER JOIN local_album_entity la on laa.album_id = la.id + WHERE laa.asset_id = lae.id AND la.backup_selection = 0 -- selected + ) + AND NOT EXISTS ( + SELECT 1 FROM local_album_asset_entity laa + INNER JOIN local_album_entity la on laa.album_id = la.id + WHERE laa.asset_id = lae.id AND la.backup_selection = 2 -- excluded + ) ) GROUP BY bucket_date ORDER BY bucket_date DESC; diff --git a/mobile/lib/infrastructure/entities/merged_asset.drift.dart b/mobile/lib/infrastructure/entities/merged_asset.drift.dart index ac3db868e1..5a091c349c 100644 --- a/mobile/lib/infrastructure/entities/merged_asset.drift.dart +++ b/mobile/lib/infrastructure/entities/merged_asset.drift.dart @@ -3,85 +3,113 @@ import 'package:drift/drift.dart' as i0; import 'package:drift/internal/modular.dart' as i1; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart' as i2; -import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart' - as i3; import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart' + as i3; +import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart' as i4; import 'package:immich_mobile/infrastructure/entities/stack.entity.drift.dart' as i5; +import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.drift.dart' + as i6; +import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart' + as i7; class MergedAssetDrift extends i1.ModularAccessor { MergedAssetDrift(i0.GeneratedDatabase db) : super(db); - i0.Selectable mergedAsset(List var1, - {required i0.Limit limit}) { + i0.Selectable mergedAsset({ + required List userIds, + required MergedAsset$limit limit, + }) { var $arrayStartIndex = 1; - final expandedvar1 = $expandVar($arrayStartIndex, var1.length); - $arrayStartIndex += var1.length; - final generatedlimit = $write(limit, startIndex: $arrayStartIndex); + final expandeduserIds = $expandVar($arrayStartIndex, userIds.length); + $arrayStartIndex += userIds.length; + final generatedlimit = $write( + limit(alias(this.localAssetEntity, 'lae')), + startIndex: $arrayStartIndex, + ); $arrayStartIndex += generatedlimit.amountOfVariables; return customSelect( - 'SELECT * FROM (SELECT rae.id AS remote_id, lae.id AS local_id, rae.name, rae.type, rae.created_at, rae.updated_at, rae.width, rae.height, rae.duration_in_seconds, rae.is_favorite, rae.thumb_hash, rae.checksum, rae.owner_id, rae.live_photo_video_id, 0 AS orientation, rae.stack_id, COALESCE(stack_count.total_count, 0) AS stack_count FROM remote_asset_entity AS rae LEFT JOIN local_asset_entity AS lae ON rae.checksum = lae.checksum LEFT JOIN stack_entity AS se ON rae.stack_id = se.id LEFT JOIN (SELECT stack_id, COUNT(*) AS total_count FROM remote_asset_entity WHERE deleted_at IS NULL AND visibility = 0 AND stack_id IS NOT NULL GROUP BY stack_id) AS stack_count ON rae.stack_id = stack_count.stack_id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandedvar1) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT NULL AS remote_id, lae.id AS local_id, lae.name, lae.type, lae.created_at, lae.updated_at, lae.width, lae.height, lae.duration_in_seconds, lae.is_favorite, NULL AS thumb_hash, lae.checksum, NULL AS owner_id, NULL AS live_photo_video_id, lae.orientation, NULL AS stack_id, 0 AS stack_count FROM local_asset_entity AS lae LEFT JOIN remote_asset_entity AS rae ON rae.checksum = lae.checksum WHERE rae.id IS NULL) ORDER BY created_at DESC ${generatedlimit.sql}', - variables: [ - for (var $ in var1) i0.Variable($), - ...generatedlimit.introducedVariables - ], - readsFrom: { - remoteAssetEntity, - localAssetEntity, - stackEntity, - ...generatedlimit.watchedTables, - }).map((i0.QueryRow row) => MergedAssetResult( - remoteId: row.readNullable('remote_id'), - localId: row.readNullable('local_id'), - name: row.read('name'), - type: i3.$RemoteAssetEntityTable.$convertertype - .fromSql(row.read('type')), - createdAt: row.read('created_at'), - updatedAt: row.read('updated_at'), - width: row.readNullable('width'), - height: row.readNullable('height'), - durationInSeconds: row.readNullable('duration_in_seconds'), - isFavorite: row.read('is_favorite'), - thumbHash: row.readNullable('thumb_hash'), - checksum: row.readNullable('checksum'), - ownerId: row.readNullable('owner_id'), - livePhotoVideoId: row.readNullable('live_photo_video_id'), - orientation: row.read('orientation'), - stackId: row.readNullable('stack_id'), - stackCount: row.read('stack_count'), - )); + 'SELECT rae.id AS remote_id, (SELECT lae.id FROM local_asset_entity AS lae WHERE lae.checksum = rae.checksum LIMIT 1) AS local_id, rae.name, rae.type, rae.created_at AS created_at, rae.updated_at, rae.width, rae.height, rae.duration_in_seconds, rae.is_favorite, rae.thumb_hash, rae.checksum, rae.owner_id, rae.live_photo_video_id, 0 AS orientation, rae.stack_id FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandeduserIds) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT NULL AS remote_id, lae.id AS local_id, lae.name, lae.type, lae.created_at AS created_at, lae.updated_at, lae.width, lae.height, lae.duration_in_seconds, lae.is_favorite, NULL AS thumb_hash, lae.checksum, NULL AS owner_id, NULL AS live_photo_video_id, lae.orientation, NULL AS stack_id FROM local_asset_entity AS lae WHERE NOT EXISTS (SELECT 1 FROM remote_asset_entity AS rae WHERE rae.checksum = lae.checksum AND rae.owner_id IN ($expandeduserIds)) AND EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 0) AND NOT EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 2) ORDER BY created_at DESC ${generatedlimit.sql}', + variables: [ + for (var $ in userIds) i0.Variable($), + ...generatedlimit.introducedVariables, + ], + readsFrom: { + remoteAssetEntity, + localAssetEntity, + stackEntity, + localAlbumAssetEntity, + localAlbumEntity, + ...generatedlimit.watchedTables, + }, + ).map( + (i0.QueryRow row) => MergedAssetResult( + remoteId: row.readNullable('remote_id'), + localId: row.readNullable('local_id'), + name: row.read('name'), + type: i4.$RemoteAssetEntityTable.$convertertype.fromSql( + row.read('type'), + ), + createdAt: row.read('created_at'), + updatedAt: row.read('updated_at'), + width: row.readNullable('width'), + height: row.readNullable('height'), + durationInSeconds: row.readNullable('duration_in_seconds'), + isFavorite: row.read('is_favorite'), + thumbHash: row.readNullable('thumb_hash'), + checksum: row.readNullable('checksum'), + ownerId: row.readNullable('owner_id'), + livePhotoVideoId: row.readNullable('live_photo_video_id'), + orientation: row.read('orientation'), + stackId: row.readNullable('stack_id'), + ), + ); } - i0.Selectable mergedBucket(List var2, - {required int groupBy}) { + i0.Selectable mergedBucket({ + required int groupBy, + required List userIds, + }) { var $arrayStartIndex = 2; - final expandedvar2 = $expandVar($arrayStartIndex, var2.length); - $arrayStartIndex += var2.length; + final expandeduserIds = $expandVar($arrayStartIndex, userIds.length); + $arrayStartIndex += userIds.length; return customSelect( - 'SELECT COUNT(*) AS asset_count, CASE WHEN ?1 = 0 THEN STRFTIME(\'%Y-%m-%d\', created_at, \'localtime\') WHEN ?1 = 1 THEN STRFTIME(\'%Y-%m\', created_at, \'localtime\') END AS bucket_date FROM (SELECT rae.name, rae.created_at FROM remote_asset_entity AS rae LEFT JOIN local_asset_entity AS lae ON rae.checksum = lae.checksum LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandedvar2) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT lae.name, lae.created_at FROM local_asset_entity AS lae LEFT JOIN remote_asset_entity AS rae ON rae.checksum = lae.checksum WHERE rae.id IS NULL) GROUP BY bucket_date ORDER BY bucket_date DESC', - variables: [ - i0.Variable(groupBy), - for (var $ in var2) i0.Variable($) - ], - readsFrom: { - remoteAssetEntity, - localAssetEntity, - stackEntity, - }).map((i0.QueryRow row) => MergedBucketResult( - assetCount: row.read('asset_count'), - bucketDate: row.read('bucket_date'), - )); + 'SELECT COUNT(*) AS asset_count, CASE WHEN ?1 = 0 THEN STRFTIME(\'%Y-%m-%d\', created_at, \'localtime\') WHEN ?1 = 1 THEN STRFTIME(\'%Y-%m\', created_at, \'localtime\') END AS bucket_date FROM (SELECT rae.created_at FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandeduserIds) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT lae.created_at FROM local_asset_entity AS lae WHERE NOT EXISTS (SELECT 1 FROM remote_asset_entity AS rae WHERE rae.checksum = lae.checksum AND rae.owner_id IN ($expandeduserIds)) AND EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 0) AND NOT EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 2)) GROUP BY bucket_date ORDER BY bucket_date DESC', + variables: [ + i0.Variable(groupBy), + for (var $ in userIds) i0.Variable($), + ], + readsFrom: { + remoteAssetEntity, + stackEntity, + localAssetEntity, + localAlbumAssetEntity, + localAlbumEntity, + }, + ).map( + (i0.QueryRow row) => MergedBucketResult( + assetCount: row.read('asset_count'), + bucketDate: row.read('bucket_date'), + ), + ); } - i3.$RemoteAssetEntityTable get remoteAssetEntity => - i1.ReadDatabaseContainer(attachedDatabase) - .resultSet('remote_asset_entity'); - i4.$LocalAssetEntityTable get localAssetEntity => - i1.ReadDatabaseContainer(attachedDatabase) - .resultSet('local_asset_entity'); - i5.$StackEntityTable get stackEntity => - i1.ReadDatabaseContainer(attachedDatabase) - .resultSet('stack_entity'); + i4.$RemoteAssetEntityTable get remoteAssetEntity => i1.ReadDatabaseContainer( + attachedDatabase, + ).resultSet('remote_asset_entity'); + i5.$StackEntityTable get stackEntity => i1.ReadDatabaseContainer( + attachedDatabase, + ).resultSet('stack_entity'); + i3.$LocalAssetEntityTable get localAssetEntity => i1.ReadDatabaseContainer( + attachedDatabase, + ).resultSet('local_asset_entity'); + i6.$LocalAlbumAssetEntityTable get localAlbumAssetEntity => + i1.ReadDatabaseContainer( + attachedDatabase, + ).resultSet('local_album_asset_entity'); + i7.$LocalAlbumEntityTable get localAlbumEntity => i1.ReadDatabaseContainer( + attachedDatabase, + ).resultSet('local_album_entity'); } class MergedAssetResult { @@ -101,7 +129,6 @@ class MergedAssetResult { final String? livePhotoVideoId; final int orientation; final String? stackId; - final int stackCount; MergedAssetResult({ this.remoteId, this.localId, @@ -119,15 +146,13 @@ class MergedAssetResult { this.livePhotoVideoId, required this.orientation, this.stackId, - required this.stackCount, }); } +typedef MergedAsset$limit = i0.Limit Function(i3.$LocalAssetEntityTable lae); + class MergedBucketResult { final int assetCount; final String bucketDate; - MergedBucketResult({ - required this.assetCount, - required this.bucketDate, - }); + MergedBucketResult({required this.assetCount, required this.bucketDate}); } diff --git a/mobile/lib/infrastructure/entities/partner.entity.dart b/mobile/lib/infrastructure/entities/partner.entity.dart index 8b51d93e6f..dbc675ee99 100644 --- a/mobile/lib/infrastructure/entities/partner.entity.dart +++ b/mobile/lib/infrastructure/entities/partner.entity.dart @@ -5,11 +5,9 @@ import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; class PartnerEntity extends Table with DriftDefaultsMixin { const PartnerEntity(); - TextColumn get sharedById => - text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); + TextColumn get sharedById => text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); - TextColumn get sharedWithId => - text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); + TextColumn get sharedWithId => text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); BoolColumn get inTimeline => boolean().withDefault(const Constant(false))(); diff --git a/mobile/lib/infrastructure/entities/partner.entity.drift.dart b/mobile/lib/infrastructure/entities/partner.entity.drift.dart index 26a5dd2fe0..01ec72fe23 100644 --- a/mobile/lib/infrastructure/entities/partner.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/partner.entity.drift.dart @@ -10,74 +10,94 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' as i4; import 'package:drift/internal/modular.dart' as i5; -typedef $$PartnerEntityTableCreateCompanionBuilder = i1.PartnerEntityCompanion - Function({ - required String sharedById, - required String sharedWithId, - i0.Value inTimeline, -}); -typedef $$PartnerEntityTableUpdateCompanionBuilder = i1.PartnerEntityCompanion - Function({ - i0.Value sharedById, - i0.Value sharedWithId, - i0.Value inTimeline, -}); +typedef $$PartnerEntityTableCreateCompanionBuilder = + i1.PartnerEntityCompanion Function({ + required String sharedById, + required String sharedWithId, + i0.Value inTimeline, + }); +typedef $$PartnerEntityTableUpdateCompanionBuilder = + i1.PartnerEntityCompanion Function({ + i0.Value sharedById, + i0.Value sharedWithId, + i0.Value inTimeline, + }); -final class $$PartnerEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, i1.$PartnerEntityTable, i1.PartnerEntityData> { +final class $$PartnerEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$PartnerEntityTable, + i1.PartnerEntityData + > { $$PartnerEntityTableReferences( - super.$_db, super.$_table, super.$_typedResult); + super.$_db, + super.$_table, + super.$_typedResult, + ); static i4.$UserEntityTable _sharedByIdTable(i0.GeneratedDatabase db) => i5.ReadDatabaseContainer(db) .resultSet('user_entity') - .createAlias(i0.$_aliasNameGenerator( - i5.ReadDatabaseContainer(db) - .resultSet('partner_entity') - .sharedById, - i5.ReadDatabaseContainer(db) - .resultSet('user_entity') - .id)); + .createAlias( + i0.$_aliasNameGenerator( + i5.ReadDatabaseContainer( + db, + ).resultSet('partner_entity').sharedById, + i5.ReadDatabaseContainer( + db, + ).resultSet('user_entity').id, + ), + ); i4.$$UserEntityTableProcessedTableManager get sharedById { final $_column = $_itemColumn('shared_by_id')!; final manager = i4 .$$UserEntityTableTableManager( + $_db, + i5.ReadDatabaseContainer( $_db, - i5.ReadDatabaseContainer($_db) - .resultSet('user_entity')) + ).resultSet('user_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_sharedByIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } static i4.$UserEntityTable _sharedWithIdTable(i0.GeneratedDatabase db) => i5.ReadDatabaseContainer(db) .resultSet('user_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i5.ReadDatabaseContainer(db) .resultSet('partner_entity') .sharedWithId, - i5.ReadDatabaseContainer(db) - .resultSet('user_entity') - .id)); + i5.ReadDatabaseContainer( + db, + ).resultSet('user_entity').id, + ), + ); i4.$$UserEntityTableProcessedTableManager get sharedWithId { final $_column = $_itemColumn('shared_with_id')!; final manager = i4 .$$UserEntityTableTableManager( + $_db, + i5.ReadDatabaseContainer( $_db, - i5.ReadDatabaseContainer($_db) - .resultSet('user_entity')) + ).resultSet('user_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_sharedWithIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -91,49 +111,61 @@ class $$PartnerEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get inTimeline => $composableBuilder( - column: $table.inTimeline, builder: (column) => i0.ColumnFilters(column)); + column: $table.inTimeline, + builder: (column) => i0.ColumnFilters(column), + ); i4.$$UserEntityTableFilterComposer get sharedById { final i4.$$UserEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.sharedById, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableFilterComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.sharedById, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableFilterComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i4.$$UserEntityTableFilterComposer get sharedWithId { final i4.$$UserEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.sharedWithId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableFilterComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.sharedWithId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableFilterComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -148,50 +180,61 @@ class $$PartnerEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get inTimeline => $composableBuilder( - column: $table.inTimeline, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.inTimeline, + builder: (column) => i0.ColumnOrderings(column), + ); i4.$$UserEntityTableOrderingComposer get sharedById { final i4.$$UserEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.sharedById, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableOrderingComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.sharedById, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i4.$$UserEntityTableOrderingComposer get sharedWithId { final i4.$$UserEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.sharedWithId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableOrderingComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.sharedWithId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -206,68 +249,85 @@ class $$PartnerEntityTableAnnotationComposer super.$removeJoinBuilderFromRootComposer, }); i0.GeneratedColumn get inTimeline => $composableBuilder( - column: $table.inTimeline, builder: (column) => column); + column: $table.inTimeline, + builder: (column) => column, + ); i4.$$UserEntityTableAnnotationComposer get sharedById { final i4.$$UserEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.sharedById, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableAnnotationComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.sharedById, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i4.$$UserEntityTableAnnotationComposer get sharedWithId { final i4.$$UserEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.sharedWithId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableAnnotationComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.sharedWithId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$PartnerEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$PartnerEntityTable, - i1.PartnerEntityData, - i1.$$PartnerEntityTableFilterComposer, - i1.$$PartnerEntityTableOrderingComposer, - i1.$$PartnerEntityTableAnnotationComposer, - $$PartnerEntityTableCreateCompanionBuilder, - $$PartnerEntityTableUpdateCompanionBuilder, - (i1.PartnerEntityData, i1.$$PartnerEntityTableReferences), - i1.PartnerEntityData, - i0.PrefetchHooks Function({bool sharedById, bool sharedWithId})> { +class $$PartnerEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$PartnerEntityTable, + i1.PartnerEntityData, + i1.$$PartnerEntityTableFilterComposer, + i1.$$PartnerEntityTableOrderingComposer, + i1.$$PartnerEntityTableAnnotationComposer, + $$PartnerEntityTableCreateCompanionBuilder, + $$PartnerEntityTableUpdateCompanionBuilder, + (i1.PartnerEntityData, i1.$$PartnerEntityTableReferences), + i1.PartnerEntityData, + i0.PrefetchHooks Function({bool sharedById, bool sharedWithId}) + > { $$PartnerEntityTableTableManager( - i0.GeneratedDatabase db, i1.$PartnerEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$PartnerEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -276,38 +336,41 @@ class $$PartnerEntityTableTableManager extends i0.RootTableManager< i1.$$PartnerEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$PartnerEntityTableAnnotationComposer($db: db, $table: table), - updateCompanionCallback: ({ - i0.Value sharedById = const i0.Value.absent(), - i0.Value sharedWithId = const i0.Value.absent(), - i0.Value inTimeline = const i0.Value.absent(), - }) => - i1.PartnerEntityCompanion( - sharedById: sharedById, - sharedWithId: sharedWithId, - inTimeline: inTimeline, - ), - createCompanionCallback: ({ - required String sharedById, - required String sharedWithId, - i0.Value inTimeline = const i0.Value.absent(), - }) => - i1.PartnerEntityCompanion.insert( - sharedById: sharedById, - sharedWithId: sharedWithId, - inTimeline: inTimeline, - ), + updateCompanionCallback: + ({ + i0.Value sharedById = const i0.Value.absent(), + i0.Value sharedWithId = const i0.Value.absent(), + i0.Value inTimeline = const i0.Value.absent(), + }) => i1.PartnerEntityCompanion( + sharedById: sharedById, + sharedWithId: sharedWithId, + inTimeline: inTimeline, + ), + createCompanionCallback: + ({ + required String sharedById, + required String sharedWithId, + i0.Value inTimeline = const i0.Value.absent(), + }) => i1.PartnerEntityCompanion.insert( + sharedById: sharedById, + sharedWithId: sharedWithId, + inTimeline: inTimeline, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$PartnerEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$PartnerEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({sharedById = false, sharedWithId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -318,52 +381,65 @@ class $$PartnerEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (sharedById) { - state = state.withJoin( - currentTable: table, - currentColumn: table.sharedById, - referencedTable: - i1.$$PartnerEntityTableReferences._sharedByIdTable(db), - referencedColumn: i1.$$PartnerEntityTableReferences - ._sharedByIdTable(db) - .id, - ) as T; - } - if (sharedWithId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.sharedWithId, - referencedTable: i1.$$PartnerEntityTableReferences - ._sharedWithIdTable(db), - referencedColumn: i1.$$PartnerEntityTableReferences - ._sharedWithIdTable(db) - .id, - ) as T; - } + dynamic + > + >(state) { + if (sharedById) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.sharedById, + referencedTable: i1 + .$$PartnerEntityTableReferences + ._sharedByIdTable(db), + referencedColumn: i1 + .$$PartnerEntityTableReferences + ._sharedByIdTable(db) + .id, + ) + as T; + } + if (sharedWithId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.sharedWithId, + referencedTable: i1 + .$$PartnerEntityTableReferences + ._sharedWithIdTable(db), + referencedColumn: i1 + .$$PartnerEntityTableReferences + ._sharedWithIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$PartnerEntityTableProcessedTableManager = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$PartnerEntityTable, - i1.PartnerEntityData, - i1.$$PartnerEntityTableFilterComposer, - i1.$$PartnerEntityTableOrderingComposer, - i1.$$PartnerEntityTableAnnotationComposer, - $$PartnerEntityTableCreateCompanionBuilder, - $$PartnerEntityTableUpdateCompanionBuilder, - (i1.PartnerEntityData, i1.$$PartnerEntityTableReferences), - i1.PartnerEntityData, - i0.PrefetchHooks Function({bool sharedById, bool sharedWithId})>; +typedef $$PartnerEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$PartnerEntityTable, + i1.PartnerEntityData, + i1.$$PartnerEntityTableFilterComposer, + i1.$$PartnerEntityTableOrderingComposer, + i1.$$PartnerEntityTableAnnotationComposer, + $$PartnerEntityTableCreateCompanionBuilder, + $$PartnerEntityTableUpdateCompanionBuilder, + (i1.PartnerEntityData, i1.$$PartnerEntityTableReferences), + i1.PartnerEntityData, + i0.PrefetchHooks Function({bool sharedById, bool sharedWithId}) + >; class $PartnerEntityTable extends i2.PartnerEntity with i0.TableInfo<$PartnerEntityTable, i1.PartnerEntityData> { @@ -371,37 +447,55 @@ class $PartnerEntityTable extends i2.PartnerEntity final i0.GeneratedDatabase attachedDatabase; final String? _alias; $PartnerEntityTable(this.attachedDatabase, [this._alias]); - static const i0.VerificationMeta _sharedByIdMeta = - const i0.VerificationMeta('sharedById'); + static const i0.VerificationMeta _sharedByIdMeta = const i0.VerificationMeta( + 'sharedById', + ); @override late final i0.GeneratedColumn sharedById = i0.GeneratedColumn( - 'shared_by_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'shared_by_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); static const i0.VerificationMeta _sharedWithIdMeta = const i0.VerificationMeta('sharedWithId'); @override late final i0.GeneratedColumn sharedWithId = - i0.GeneratedColumn('shared_with_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); - static const i0.VerificationMeta _inTimelineMeta = - const i0.VerificationMeta('inTimeline'); + i0.GeneratedColumn( + 'shared_with_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + static const i0.VerificationMeta _inTimelineMeta = const i0.VerificationMeta( + 'inTimeline', + ); @override late final i0.GeneratedColumn inTimeline = i0.GeneratedColumn( - 'in_timeline', aliasedName, false, - type: i0.DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'CHECK ("in_timeline" IN (0, 1))'), - defaultValue: const i3.Constant(false)); + 'in_timeline', + aliasedName, + false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const i3.Constant(false), + ); @override - List get $columns => - [sharedById, sharedWithId, inTimeline]; + List get $columns => [ + sharedById, + sharedWithId, + inTimeline, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -409,31 +503,38 @@ class $PartnerEntityTable extends i2.PartnerEntity static const String $name = 'partner_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('shared_by_id')) { context.handle( + _sharedByIdMeta, + sharedById.isAcceptableOrUnknown( + data['shared_by_id']!, _sharedByIdMeta, - sharedById.isAcceptableOrUnknown( - data['shared_by_id']!, _sharedByIdMeta)); + ), + ); } else if (isInserting) { context.missing(_sharedByIdMeta); } if (data.containsKey('shared_with_id')) { context.handle( + _sharedWithIdMeta, + sharedWithId.isAcceptableOrUnknown( + data['shared_with_id']!, _sharedWithIdMeta, - sharedWithId.isAcceptableOrUnknown( - data['shared_with_id']!, _sharedWithIdMeta)); + ), + ); } else if (isInserting) { context.missing(_sharedWithIdMeta); } if (data.containsKey('in_timeline')) { context.handle( - _inTimelineMeta, - inTimeline.isAcceptableOrUnknown( - data['in_timeline']!, _inTimelineMeta)); + _inTimelineMeta, + inTimeline.isAcceptableOrUnknown(data['in_timeline']!, _inTimelineMeta), + ); } return context; } @@ -445,11 +546,17 @@ class $PartnerEntityTable extends i2.PartnerEntity final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.PartnerEntityData( sharedById: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, data['${effectivePrefix}shared_by_id'])!, + i0.DriftSqlType.string, + data['${effectivePrefix}shared_by_id'], + )!, sharedWithId: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, data['${effectivePrefix}shared_with_id'])!, - inTimeline: attachedDatabase.typeMapping - .read(i0.DriftSqlType.bool, data['${effectivePrefix}in_timeline'])!, + i0.DriftSqlType.string, + data['${effectivePrefix}shared_with_id'], + )!, + inTimeline: attachedDatabase.typeMapping.read( + i0.DriftSqlType.bool, + data['${effectivePrefix}in_timeline'], + )!, ); } @@ -469,10 +576,11 @@ class PartnerEntityData extends i0.DataClass final String sharedById; final String sharedWithId; final bool inTimeline; - const PartnerEntityData( - {required this.sharedById, - required this.sharedWithId, - required this.inTimeline}); + const PartnerEntityData({ + required this.sharedById, + required this.sharedWithId, + required this.inTimeline, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -482,8 +590,10 @@ class PartnerEntityData extends i0.DataClass return map; } - factory PartnerEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory PartnerEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return PartnerEntityData( sharedById: serializer.fromJson(json['sharedById']), @@ -501,22 +611,26 @@ class PartnerEntityData extends i0.DataClass }; } - i1.PartnerEntityData copyWith( - {String? sharedById, String? sharedWithId, bool? inTimeline}) => - i1.PartnerEntityData( - sharedById: sharedById ?? this.sharedById, - sharedWithId: sharedWithId ?? this.sharedWithId, - inTimeline: inTimeline ?? this.inTimeline, - ); + i1.PartnerEntityData copyWith({ + String? sharedById, + String? sharedWithId, + bool? inTimeline, + }) => i1.PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); PartnerEntityData copyWithCompanion(i1.PartnerEntityCompanion data) { return PartnerEntityData( - sharedById: - data.sharedById.present ? data.sharedById.value : this.sharedById, + sharedById: data.sharedById.present + ? data.sharedById.value + : this.sharedById, sharedWithId: data.sharedWithId.present ? data.sharedWithId.value : this.sharedWithId, - inTimeline: - data.inTimeline.present ? data.inTimeline.value : this.inTimeline, + inTimeline: data.inTimeline.present + ? data.inTimeline.value + : this.inTimeline, ); } @@ -554,8 +668,8 @@ class PartnerEntityCompanion extends i0.UpdateCompanion { required String sharedById, required String sharedWithId, this.inTimeline = const i0.Value.absent(), - }) : sharedById = i0.Value(sharedById), - sharedWithId = i0.Value(sharedWithId); + }) : sharedById = i0.Value(sharedById), + sharedWithId = i0.Value(sharedWithId); static i0.Insertable custom({ i0.Expression? sharedById, i0.Expression? sharedWithId, @@ -568,10 +682,11 @@ class PartnerEntityCompanion extends i0.UpdateCompanion { }); } - i1.PartnerEntityCompanion copyWith( - {i0.Value? sharedById, - i0.Value? sharedWithId, - i0.Value? inTimeline}) { + i1.PartnerEntityCompanion copyWith({ + i0.Value? sharedById, + i0.Value? sharedWithId, + i0.Value? inTimeline, + }) { return i1.PartnerEntityCompanion( sharedById: sharedById ?? this.sharedById, sharedWithId: sharedWithId ?? this.sharedWithId, diff --git a/mobile/lib/infrastructure/entities/person.entity.dart b/mobile/lib/infrastructure/entities/person.entity.dart new file mode 100644 index 0000000000..f0878e00f8 --- /dev/null +++ b/mobile/lib/infrastructure/entities/person.entity.dart @@ -0,0 +1,30 @@ +import 'package:drift/drift.dart'; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; +import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; + +class PersonEntity extends Table with DriftDefaultsMixin { + const PersonEntity(); + + TextColumn get id => text()(); + + DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)(); + + DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)(); + + TextColumn get ownerId => text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); + + TextColumn get name => text()(); + + TextColumn get faceAssetId => text().nullable()(); + + BoolColumn get isFavorite => boolean()(); + + BoolColumn get isHidden => boolean()(); + + TextColumn get color => text().nullable()(); + + DateTimeColumn get birthDate => dateTime().nullable()(); + + @override + Set get primaryKey => {id}; +} diff --git a/mobile/lib/infrastructure/entities/person.entity.drift.dart b/mobile/lib/infrastructure/entities/person.entity.drift.dart new file mode 100644 index 0000000000..ffbd796f4b --- /dev/null +++ b/mobile/lib/infrastructure/entities/person.entity.drift.dart @@ -0,0 +1,1058 @@ +// dart format width=80 +// ignore_for_file: type=lint +import 'package:drift/drift.dart' as i0; +import 'package:immich_mobile/infrastructure/entities/person.entity.drift.dart' + as i1; +import 'package:immich_mobile/infrastructure/entities/person.entity.dart' as i2; +import 'package:drift/src/runtime/query_builder/query_builder.dart' as i3; +import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' + as i4; +import 'package:drift/internal/modular.dart' as i5; + +typedef $$PersonEntityTableCreateCompanionBuilder = + i1.PersonEntityCompanion Function({ + required String id, + i0.Value createdAt, + i0.Value updatedAt, + required String ownerId, + required String name, + i0.Value faceAssetId, + required bool isFavorite, + required bool isHidden, + i0.Value color, + i0.Value birthDate, + }); +typedef $$PersonEntityTableUpdateCompanionBuilder = + i1.PersonEntityCompanion Function({ + i0.Value id, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value ownerId, + i0.Value name, + i0.Value faceAssetId, + i0.Value isFavorite, + i0.Value isHidden, + i0.Value color, + i0.Value birthDate, + }); + +final class $$PersonEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$PersonEntityTable, + i1.PersonEntityData + > { + $$PersonEntityTableReferences(super.$_db, super.$_table, super.$_typedResult); + + static i4.$UserEntityTable _ownerIdTable(i0.GeneratedDatabase db) => + i5.ReadDatabaseContainer(db) + .resultSet('user_entity') + .createAlias( + i0.$_aliasNameGenerator( + i5.ReadDatabaseContainer( + db, + ).resultSet('person_entity').ownerId, + i5.ReadDatabaseContainer( + db, + ).resultSet('user_entity').id, + ), + ); + + i4.$$UserEntityTableProcessedTableManager get ownerId { + final $_column = $_itemColumn('owner_id')!; + + final manager = i4 + .$$UserEntityTableTableManager( + $_db, + i5.ReadDatabaseContainer( + $_db, + ).resultSet('user_entity'), + ) + .filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_ownerIdTable($_db)); + if (item == null) return manager; + return i0.ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item]), + ); + } +} + +class $$PersonEntityTableFilterComposer + extends i0.Composer { + $$PersonEntityTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.ColumnFilters get id => $composableBuilder( + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get createdAt => $composableBuilder( + column: $table.createdAt, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get updatedAt => $composableBuilder( + column: $table.updatedAt, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get name => $composableBuilder( + column: $table.name, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get faceAssetId => $composableBuilder( + column: $table.faceAssetId, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get isFavorite => $composableBuilder( + column: $table.isFavorite, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get isHidden => $composableBuilder( + column: $table.isHidden, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get color => $composableBuilder( + column: $table.color, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get birthDate => $composableBuilder( + column: $table.birthDate, + builder: (column) => i0.ColumnFilters(column), + ); + + i4.$$UserEntityTableFilterComposer get ownerId { + final i4.$$UserEntityTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableFilterComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$PersonEntityTableOrderingComposer + extends i0.Composer { + $$PersonEntityTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.ColumnOrderings get id => $composableBuilder( + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get createdAt => $composableBuilder( + column: $table.createdAt, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get updatedAt => $composableBuilder( + column: $table.updatedAt, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get name => $composableBuilder( + column: $table.name, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get faceAssetId => $composableBuilder( + column: $table.faceAssetId, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get isFavorite => $composableBuilder( + column: $table.isFavorite, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get isHidden => $composableBuilder( + column: $table.isHidden, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get color => $composableBuilder( + column: $table.color, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get birthDate => $composableBuilder( + column: $table.birthDate, + builder: (column) => i0.ColumnOrderings(column), + ); + + i4.$$UserEntityTableOrderingComposer get ownerId { + final i4.$$UserEntityTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$PersonEntityTableAnnotationComposer + extends i0.Composer { + $$PersonEntityTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + i0.GeneratedColumn get id => + $composableBuilder(column: $table.id, builder: (column) => column); + + i0.GeneratedColumn get createdAt => + $composableBuilder(column: $table.createdAt, builder: (column) => column); + + i0.GeneratedColumn get updatedAt => + $composableBuilder(column: $table.updatedAt, builder: (column) => column); + + i0.GeneratedColumn get name => + $composableBuilder(column: $table.name, builder: (column) => column); + + i0.GeneratedColumn get faceAssetId => $composableBuilder( + column: $table.faceAssetId, + builder: (column) => column, + ); + + i0.GeneratedColumn get isFavorite => $composableBuilder( + column: $table.isFavorite, + builder: (column) => column, + ); + + i0.GeneratedColumn get isHidden => + $composableBuilder(column: $table.isHidden, builder: (column) => column); + + i0.GeneratedColumn get color => + $composableBuilder(column: $table.color, builder: (column) => column); + + i0.GeneratedColumn get birthDate => + $composableBuilder(column: $table.birthDate, builder: (column) => column); + + i4.$$UserEntityTableAnnotationComposer get ownerId { + final i4.$$UserEntityTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$PersonEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$PersonEntityTable, + i1.PersonEntityData, + i1.$$PersonEntityTableFilterComposer, + i1.$$PersonEntityTableOrderingComposer, + i1.$$PersonEntityTableAnnotationComposer, + $$PersonEntityTableCreateCompanionBuilder, + $$PersonEntityTableUpdateCompanionBuilder, + (i1.PersonEntityData, i1.$$PersonEntityTableReferences), + i1.PersonEntityData, + i0.PrefetchHooks Function({bool ownerId}) + > { + $$PersonEntityTableTableManager( + i0.GeneratedDatabase db, + i1.$PersonEntityTable table, + ) : super( + i0.TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + i1.$$PersonEntityTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => + i1.$$PersonEntityTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => + i1.$$PersonEntityTableAnnotationComposer($db: db, $table: table), + updateCompanionCallback: + ({ + i0.Value id = const i0.Value.absent(), + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value ownerId = const i0.Value.absent(), + i0.Value name = const i0.Value.absent(), + i0.Value faceAssetId = const i0.Value.absent(), + i0.Value isFavorite = const i0.Value.absent(), + i0.Value isHidden = const i0.Value.absent(), + i0.Value color = const i0.Value.absent(), + i0.Value birthDate = const i0.Value.absent(), + }) => i1.PersonEntityCompanion( + id: id, + createdAt: createdAt, + updatedAt: updatedAt, + ownerId: ownerId, + name: name, + faceAssetId: faceAssetId, + isFavorite: isFavorite, + isHidden: isHidden, + color: color, + birthDate: birthDate, + ), + createCompanionCallback: + ({ + required String id, + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + required String ownerId, + required String name, + i0.Value faceAssetId = const i0.Value.absent(), + required bool isFavorite, + required bool isHidden, + i0.Value color = const i0.Value.absent(), + i0.Value birthDate = const i0.Value.absent(), + }) => i1.PersonEntityCompanion.insert( + id: id, + createdAt: createdAt, + updatedAt: updatedAt, + ownerId: ownerId, + name: name, + faceAssetId: faceAssetId, + isFavorite: isFavorite, + isHidden: isHidden, + color: color, + birthDate: birthDate, + ), + withReferenceMapper: (p0) => p0 + .map( + (e) => ( + e.readTable(table), + i1.$$PersonEntityTableReferences(db, table, e), + ), + ) + .toList(), + prefetchHooksCallback: ({ownerId = false}) { + return i0.PrefetchHooks( + db: db, + explicitlyWatchedTables: [], + addJoins: + < + T extends i0.TableManagerState< + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic + > + >(state) { + if (ownerId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.ownerId, + referencedTable: i1 + .$$PersonEntityTableReferences + ._ownerIdTable(db), + referencedColumn: i1 + .$$PersonEntityTableReferences + ._ownerIdTable(db) + .id, + ) + as T; + } + + return state; + }, + getPrefetchedDataCallback: (items) async { + return []; + }, + ); + }, + ), + ); +} + +typedef $$PersonEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$PersonEntityTable, + i1.PersonEntityData, + i1.$$PersonEntityTableFilterComposer, + i1.$$PersonEntityTableOrderingComposer, + i1.$$PersonEntityTableAnnotationComposer, + $$PersonEntityTableCreateCompanionBuilder, + $$PersonEntityTableUpdateCompanionBuilder, + (i1.PersonEntityData, i1.$$PersonEntityTableReferences), + i1.PersonEntityData, + i0.PrefetchHooks Function({bool ownerId}) + >; + +class $PersonEntityTable extends i2.PersonEntity + with i0.TableInfo<$PersonEntityTable, i1.PersonEntityData> { + @override + final i0.GeneratedDatabase attachedDatabase; + final String? _alias; + $PersonEntityTable(this.attachedDatabase, [this._alias]); + static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); + @override + late final i0.GeneratedColumn id = i0.GeneratedColumn( + 'id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _createdAtMeta = const i0.VerificationMeta( + 'createdAt', + ); + @override + late final i0.GeneratedColumn createdAt = + i0.GeneratedColumn( + 'created_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i3.currentDateAndTime, + ); + static const i0.VerificationMeta _updatedAtMeta = const i0.VerificationMeta( + 'updatedAt', + ); + @override + late final i0.GeneratedColumn updatedAt = + i0.GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i3.currentDateAndTime, + ); + static const i0.VerificationMeta _ownerIdMeta = const i0.VerificationMeta( + 'ownerId', + ); + @override + late final i0.GeneratedColumn ownerId = i0.GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + static const i0.VerificationMeta _nameMeta = const i0.VerificationMeta( + 'name', + ); + @override + late final i0.GeneratedColumn name = i0.GeneratedColumn( + 'name', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _faceAssetIdMeta = const i0.VerificationMeta( + 'faceAssetId', + ); + @override + late final i0.GeneratedColumn faceAssetId = + i0.GeneratedColumn( + 'face_asset_id', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _isFavoriteMeta = const i0.VerificationMeta( + 'isFavorite', + ); + @override + late final i0.GeneratedColumn isFavorite = i0.GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); + static const i0.VerificationMeta _isHiddenMeta = const i0.VerificationMeta( + 'isHidden', + ); + @override + late final i0.GeneratedColumn isHidden = i0.GeneratedColumn( + 'is_hidden', + aliasedName, + false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); + static const i0.VerificationMeta _colorMeta = const i0.VerificationMeta( + 'color', + ); + @override + late final i0.GeneratedColumn color = i0.GeneratedColumn( + 'color', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _birthDateMeta = const i0.VerificationMeta( + 'birthDate', + ); + @override + late final i0.GeneratedColumn birthDate = + i0.GeneratedColumn( + 'birth_date', + aliasedName, + true, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'person_entity'; + @override + i0.VerificationContext validateIntegrity( + i0.Insertable instance, { + bool isInserting = false, + }) { + final context = i0.VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('id')) { + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); + } else if (isInserting) { + context.missing(_idMeta); + } + if (data.containsKey('created_at')) { + context.handle( + _createdAtMeta, + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta), + ); + } + if (data.containsKey('updated_at')) { + context.handle( + _updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), + ); + } + if (data.containsKey('owner_id')) { + context.handle( + _ownerIdMeta, + ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta), + ); + } else if (isInserting) { + context.missing(_ownerIdMeta); + } + if (data.containsKey('name')) { + context.handle( + _nameMeta, + name.isAcceptableOrUnknown(data['name']!, _nameMeta), + ); + } else if (isInserting) { + context.missing(_nameMeta); + } + if (data.containsKey('face_asset_id')) { + context.handle( + _faceAssetIdMeta, + faceAssetId.isAcceptableOrUnknown( + data['face_asset_id']!, + _faceAssetIdMeta, + ), + ); + } + if (data.containsKey('is_favorite')) { + context.handle( + _isFavoriteMeta, + isFavorite.isAcceptableOrUnknown(data['is_favorite']!, _isFavoriteMeta), + ); + } else if (isInserting) { + context.missing(_isFavoriteMeta); + } + if (data.containsKey('is_hidden')) { + context.handle( + _isHiddenMeta, + isHidden.isAcceptableOrUnknown(data['is_hidden']!, _isHiddenMeta), + ); + } else if (isInserting) { + context.missing(_isHiddenMeta); + } + if (data.containsKey('color')) { + context.handle( + _colorMeta, + color.isAcceptableOrUnknown(data['color']!, _colorMeta), + ); + } + if (data.containsKey('birth_date')) { + context.handle( + _birthDateMeta, + birthDate.isAcceptableOrUnknown(data['birth_date']!, _birthDateMeta), + ); + } + return context; + } + + @override + Set get $primaryKey => {id}; + @override + i1.PersonEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return i1.PersonEntityData( + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + name: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + faceAssetId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}face_asset_id'], + ), + isFavorite: attachedDatabase.typeMapping.read( + i0.DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + isHidden: attachedDatabase.typeMapping.read( + i0.DriftSqlType.bool, + data['${effectivePrefix}is_hidden'], + )!, + color: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}color'], + ), + birthDate: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, + data['${effectivePrefix}birth_date'], + ), + ); + } + + @override + $PersonEntityTable createAlias(String alias) { + return $PersonEntityTable(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PersonEntityData extends i0.DataClass + implements i0.Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String name; + final String? faceAssetId; + final bool isFavorite; + final bool isHidden; + final String? color; + final DateTime? birthDate; + const PersonEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = i0.Variable(id); + map['created_at'] = i0.Variable(createdAt); + map['updated_at'] = i0.Variable(updatedAt); + map['owner_id'] = i0.Variable(ownerId); + map['name'] = i0.Variable(name); + if (!nullToAbsent || faceAssetId != null) { + map['face_asset_id'] = i0.Variable(faceAssetId); + } + map['is_favorite'] = i0.Variable(isFavorite); + map['is_hidden'] = i0.Variable(isHidden); + if (!nullToAbsent || color != null) { + map['color'] = i0.Variable(color); + } + if (!nullToAbsent || birthDate != null) { + map['birth_date'] = i0.Variable(birthDate); + } + return map; + } + + factory PersonEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { + serializer ??= i0.driftRuntimeOptions.defaultSerializer; + return PersonEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + name: serializer.fromJson(json['name']), + faceAssetId: serializer.fromJson(json['faceAssetId']), + isFavorite: serializer.fromJson(json['isFavorite']), + isHidden: serializer.fromJson(json['isHidden']), + color: serializer.fromJson(json['color']), + birthDate: serializer.fromJson(json['birthDate']), + ); + } + @override + Map toJson({i0.ValueSerializer? serializer}) { + serializer ??= i0.driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'name': serializer.toJson(name), + 'faceAssetId': serializer.toJson(faceAssetId), + 'isFavorite': serializer.toJson(isFavorite), + 'isHidden': serializer.toJson(isHidden), + 'color': serializer.toJson(color), + 'birthDate': serializer.toJson(birthDate), + }; + } + + i1.PersonEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + i0.Value faceAssetId = const i0.Value.absent(), + bool? isFavorite, + bool? isHidden, + i0.Value color = const i0.Value.absent(), + i0.Value birthDate = const i0.Value.absent(), + }) => i1.PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); + PersonEntityData copyWithCompanion(i1.PersonEntityCompanion data) { + return PersonEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + name: data.name.present ? data.name.value : this.name, + faceAssetId: data.faceAssetId.present + ? data.faceAssetId.value + : this.faceAssetId, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, + color: data.color.present ? data.color.value : this.color, + birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, + ); + } + + @override + String toString() { + return (StringBuffer('PersonEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is i1.PersonEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.name == this.name && + other.faceAssetId == this.faceAssetId && + other.isFavorite == this.isFavorite && + other.isHidden == this.isHidden && + other.color == this.color && + other.birthDate == this.birthDate); +} + +class PersonEntityCompanion extends i0.UpdateCompanion { + final i0.Value id; + final i0.Value createdAt; + final i0.Value updatedAt; + final i0.Value ownerId; + final i0.Value name; + final i0.Value faceAssetId; + final i0.Value isFavorite; + final i0.Value isHidden; + final i0.Value color; + final i0.Value birthDate; + const PersonEntityCompanion({ + this.id = const i0.Value.absent(), + this.createdAt = const i0.Value.absent(), + this.updatedAt = const i0.Value.absent(), + this.ownerId = const i0.Value.absent(), + this.name = const i0.Value.absent(), + this.faceAssetId = const i0.Value.absent(), + this.isFavorite = const i0.Value.absent(), + this.isHidden = const i0.Value.absent(), + this.color = const i0.Value.absent(), + this.birthDate = const i0.Value.absent(), + }); + PersonEntityCompanion.insert({ + required String id, + this.createdAt = const i0.Value.absent(), + this.updatedAt = const i0.Value.absent(), + required String ownerId, + required String name, + this.faceAssetId = const i0.Value.absent(), + required bool isFavorite, + required bool isHidden, + this.color = const i0.Value.absent(), + this.birthDate = const i0.Value.absent(), + }) : id = i0.Value(id), + ownerId = i0.Value(ownerId), + name = i0.Value(name), + isFavorite = i0.Value(isFavorite), + isHidden = i0.Value(isHidden); + static i0.Insertable custom({ + i0.Expression? id, + i0.Expression? createdAt, + i0.Expression? updatedAt, + i0.Expression? ownerId, + i0.Expression? name, + i0.Expression? faceAssetId, + i0.Expression? isFavorite, + i0.Expression? isHidden, + i0.Expression? color, + i0.Expression? birthDate, + }) { + return i0.RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (name != null) 'name': name, + if (faceAssetId != null) 'face_asset_id': faceAssetId, + if (isFavorite != null) 'is_favorite': isFavorite, + if (isHidden != null) 'is_hidden': isHidden, + if (color != null) 'color': color, + if (birthDate != null) 'birth_date': birthDate, + }); + } + + i1.PersonEntityCompanion copyWith({ + i0.Value? id, + i0.Value? createdAt, + i0.Value? updatedAt, + i0.Value? ownerId, + i0.Value? name, + i0.Value? faceAssetId, + i0.Value? isFavorite, + i0.Value? isHidden, + i0.Value? color, + i0.Value? birthDate, + }) { + return i1.PersonEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId ?? this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color ?? this.color, + birthDate: birthDate ?? this.birthDate, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = i0.Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = i0.Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = i0.Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = i0.Variable(ownerId.value); + } + if (name.present) { + map['name'] = i0.Variable(name.value); + } + if (faceAssetId.present) { + map['face_asset_id'] = i0.Variable(faceAssetId.value); + } + if (isFavorite.present) { + map['is_favorite'] = i0.Variable(isFavorite.value); + } + if (isHidden.present) { + map['is_hidden'] = i0.Variable(isHidden.value); + } + if (color.present) { + map['color'] = i0.Variable(color.value); + } + if (birthDate.present) { + map['birth_date'] = i0.Variable(birthDate.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PersonEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } +} diff --git a/mobile/lib/infrastructure/entities/remote_album.entity.dart b/mobile/lib/infrastructure/entities/remote_album.entity.dart index 377d67446f..74b00dd9ee 100644 --- a/mobile/lib/infrastructure/entities/remote_album.entity.dart +++ b/mobile/lib/infrastructure/entities/remote_album.entity.dart @@ -17,15 +17,12 @@ class RemoteAlbumEntity extends Table with DriftDefaultsMixin { DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)(); - TextColumn get ownerId => - text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); + TextColumn get ownerId => text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); - TextColumn get thumbnailAssetId => text() - .references(RemoteAssetEntity, #id, onDelete: KeyAction.setNull) - .nullable()(); + TextColumn get thumbnailAssetId => + text().references(RemoteAssetEntity, #id, onDelete: KeyAction.setNull).nullable()(); - BoolColumn get isActivityEnabled => - boolean().withDefault(const Constant(true))(); + BoolColumn get isActivityEnabled => boolean().withDefault(const Constant(true))(); IntColumn get order => intEnum()(); diff --git a/mobile/lib/infrastructure/entities/remote_album.entity.drift.dart b/mobile/lib/infrastructure/entities/remote_album.entity.drift.dart index bc13c8cb5c..30a6d0b535 100644 --- a/mobile/lib/infrastructure/entities/remote_album.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/remote_album.entity.drift.dart @@ -13,89 +13,107 @@ import 'package:drift/internal/modular.dart' as i6; import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart' as i7; -typedef $$RemoteAlbumEntityTableCreateCompanionBuilder - = i1.RemoteAlbumEntityCompanion Function({ - required String id, - required String name, - i0.Value description, - i0.Value createdAt, - i0.Value updatedAt, - required String ownerId, - i0.Value thumbnailAssetId, - i0.Value isActivityEnabled, - required i2.AlbumAssetOrder order, -}); -typedef $$RemoteAlbumEntityTableUpdateCompanionBuilder - = i1.RemoteAlbumEntityCompanion Function({ - i0.Value id, - i0.Value name, - i0.Value description, - i0.Value createdAt, - i0.Value updatedAt, - i0.Value ownerId, - i0.Value thumbnailAssetId, - i0.Value isActivityEnabled, - i0.Value order, -}); +typedef $$RemoteAlbumEntityTableCreateCompanionBuilder = + i1.RemoteAlbumEntityCompanion Function({ + required String id, + required String name, + i0.Value description, + i0.Value createdAt, + i0.Value updatedAt, + required String ownerId, + i0.Value thumbnailAssetId, + i0.Value isActivityEnabled, + required i2.AlbumAssetOrder order, + }); +typedef $$RemoteAlbumEntityTableUpdateCompanionBuilder = + i1.RemoteAlbumEntityCompanion Function({ + i0.Value id, + i0.Value name, + i0.Value description, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value ownerId, + i0.Value thumbnailAssetId, + i0.Value isActivityEnabled, + i0.Value order, + }); -final class $$RemoteAlbumEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, - i1.$RemoteAlbumEntityTable, - i1.RemoteAlbumEntityData> { +final class $$RemoteAlbumEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$RemoteAlbumEntityTable, + i1.RemoteAlbumEntityData + > { $$RemoteAlbumEntityTableReferences( - super.$_db, super.$_table, super.$_typedResult); + super.$_db, + super.$_table, + super.$_typedResult, + ); static i5.$UserEntityTable _ownerIdTable(i0.GeneratedDatabase db) => i6.ReadDatabaseContainer(db) .resultSet('user_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i6.ReadDatabaseContainer(db) .resultSet('remote_album_entity') .ownerId, - i6.ReadDatabaseContainer(db) - .resultSet('user_entity') - .id)); + i6.ReadDatabaseContainer( + db, + ).resultSet('user_entity').id, + ), + ); i5.$$UserEntityTableProcessedTableManager get ownerId { final $_column = $_itemColumn('owner_id')!; final manager = i5 .$$UserEntityTableTableManager( + $_db, + i6.ReadDatabaseContainer( $_db, - i6.ReadDatabaseContainer($_db) - .resultSet('user_entity')) + ).resultSet('user_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_ownerIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } static i7.$RemoteAssetEntityTable _thumbnailAssetIdTable( - i0.GeneratedDatabase db) => - i6.ReadDatabaseContainer(db) - .resultSet('remote_asset_entity') - .createAlias(i0.$_aliasNameGenerator( - i6.ReadDatabaseContainer(db) - .resultSet('remote_album_entity') - .thumbnailAssetId, - i6.ReadDatabaseContainer(db) - .resultSet('remote_asset_entity') - .id)); + i0.GeneratedDatabase db, + ) => i6.ReadDatabaseContainer(db) + .resultSet('remote_asset_entity') + .createAlias( + i0.$_aliasNameGenerator( + i6.ReadDatabaseContainer(db) + .resultSet('remote_album_entity') + .thumbnailAssetId, + i6.ReadDatabaseContainer( + db, + ).resultSet('remote_asset_entity').id, + ), + ); i7.$$RemoteAssetEntityTableProcessedTableManager? get thumbnailAssetId { final $_column = $_itemColumn('thumbnail_asset_id'); if ($_column == null) return null; final manager = i7 .$$RemoteAssetEntityTableTableManager( + $_db, + i6.ReadDatabaseContainer( $_db, - i6.ReadDatabaseContainer($_db) - .resultSet('remote_asset_entity')) + ).resultSet('remote_asset_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_thumbnailAssetIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -109,71 +127,92 @@ class $$RemoteAlbumEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnFilters(column)); + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnFilters(column)); + column: $table.name, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get description => $composableBuilder( - column: $table.description, - builder: (column) => i0.ColumnFilters(column)); + column: $table.description, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get isActivityEnabled => $composableBuilder( - column: $table.isActivityEnabled, - builder: (column) => i0.ColumnFilters(column)); + column: $table.isActivityEnabled, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnWithTypeConverterFilters - get order => $composableBuilder( - column: $table.order, - builder: (column) => i0.ColumnWithTypeConverterFilters(column)); + get order => $composableBuilder( + column: $table.order, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); i5.$$UserEntityTableFilterComposer get ownerId { final i5.$$UserEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableFilterComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableFilterComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i7.$$RemoteAssetEntityTableFilterComposer get thumbnailAssetId { final i7.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.thumbnailAssetId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i7.$$RemoteAssetEntityTableFilterComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.thumbnailAssetId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i7.$$RemoteAssetEntityTableFilterComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -188,73 +227,92 @@ class $$RemoteAlbumEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnOrderings(column)); + column: $table.name, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get description => $composableBuilder( - column: $table.description, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.description, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get createdAt => $composableBuilder( - column: $table.createdAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get updatedAt => $composableBuilder( - column: $table.updatedAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get isActivityEnabled => $composableBuilder( - column: $table.isActivityEnabled, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.isActivityEnabled, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get order => $composableBuilder( - column: $table.order, builder: (column) => i0.ColumnOrderings(column)); + column: $table.order, + builder: (column) => i0.ColumnOrderings(column), + ); i5.$$UserEntityTableOrderingComposer get ownerId { final i5.$$UserEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableOrderingComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i7.$$RemoteAssetEntityTableOrderingComposer get thumbnailAssetId { final i7.$$RemoteAssetEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.thumbnailAssetId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i7.$$RemoteAssetEntityTableOrderingComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.thumbnailAssetId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i7.$$RemoteAssetEntityTableOrderingComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -275,7 +333,9 @@ class $$RemoteAlbumEntityTableAnnotationComposer $composableBuilder(column: $table.name, builder: (column) => column); i0.GeneratedColumn get description => $composableBuilder( - column: $table.description, builder: (column) => column); + column: $table.description, + builder: (column) => column, + ); i0.GeneratedColumn get createdAt => $composableBuilder(column: $table.createdAt, builder: (column) => column); @@ -284,73 +344,89 @@ class $$RemoteAlbumEntityTableAnnotationComposer $composableBuilder(column: $table.updatedAt, builder: (column) => column); i0.GeneratedColumn get isActivityEnabled => $composableBuilder( - column: $table.isActivityEnabled, builder: (column) => column); + column: $table.isActivityEnabled, + builder: (column) => column, + ); i0.GeneratedColumnWithTypeConverter get order => $composableBuilder(column: $table.order, builder: (column) => column); i5.$$UserEntityTableAnnotationComposer get ownerId { final i5.$$UserEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableAnnotationComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i7.$$RemoteAssetEntityTableAnnotationComposer get thumbnailAssetId { final i7.$$RemoteAssetEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.thumbnailAssetId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i7.$$RemoteAssetEntityTableAnnotationComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.thumbnailAssetId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i7.$$RemoteAssetEntityTableAnnotationComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$RemoteAlbumEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$RemoteAlbumEntityTable, - i1.RemoteAlbumEntityData, - i1.$$RemoteAlbumEntityTableFilterComposer, - i1.$$RemoteAlbumEntityTableOrderingComposer, - i1.$$RemoteAlbumEntityTableAnnotationComposer, - $$RemoteAlbumEntityTableCreateCompanionBuilder, - $$RemoteAlbumEntityTableUpdateCompanionBuilder, - (i1.RemoteAlbumEntityData, i1.$$RemoteAlbumEntityTableReferences), - i1.RemoteAlbumEntityData, - i0.PrefetchHooks Function({bool ownerId, bool thumbnailAssetId})> { +class $$RemoteAlbumEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$RemoteAlbumEntityTable, + i1.RemoteAlbumEntityData, + i1.$$RemoteAlbumEntityTableFilterComposer, + i1.$$RemoteAlbumEntityTableOrderingComposer, + i1.$$RemoteAlbumEntityTableAnnotationComposer, + $$RemoteAlbumEntityTableCreateCompanionBuilder, + $$RemoteAlbumEntityTableUpdateCompanionBuilder, + (i1.RemoteAlbumEntityData, i1.$$RemoteAlbumEntityTableReferences), + i1.RemoteAlbumEntityData, + i0.PrefetchHooks Function({bool ownerId, bool thumbnailAssetId}) + > { $$RemoteAlbumEntityTableTableManager( - i0.GeneratedDatabase db, i1.$RemoteAlbumEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$RemoteAlbumEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -359,63 +435,68 @@ class $$RemoteAlbumEntityTableTableManager extends i0.RootTableManager< .$$RemoteAlbumEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$RemoteAlbumEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value id = const i0.Value.absent(), - i0.Value name = const i0.Value.absent(), - i0.Value description = const i0.Value.absent(), - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value ownerId = const i0.Value.absent(), - i0.Value thumbnailAssetId = const i0.Value.absent(), - i0.Value isActivityEnabled = const i0.Value.absent(), - i0.Value order = const i0.Value.absent(), - }) => - i1.RemoteAlbumEntityCompanion( - id: id, - name: name, - description: description, - createdAt: createdAt, - updatedAt: updatedAt, - ownerId: ownerId, - thumbnailAssetId: thumbnailAssetId, - isActivityEnabled: isActivityEnabled, - order: order, - ), - createCompanionCallback: ({ - required String id, - required String name, - i0.Value description = const i0.Value.absent(), - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - required String ownerId, - i0.Value thumbnailAssetId = const i0.Value.absent(), - i0.Value isActivityEnabled = const i0.Value.absent(), - required i2.AlbumAssetOrder order, - }) => - i1.RemoteAlbumEntityCompanion.insert( - id: id, - name: name, - description: description, - createdAt: createdAt, - updatedAt: updatedAt, - ownerId: ownerId, - thumbnailAssetId: thumbnailAssetId, - isActivityEnabled: isActivityEnabled, - order: order, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value id = const i0.Value.absent(), + i0.Value name = const i0.Value.absent(), + i0.Value description = const i0.Value.absent(), + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value ownerId = const i0.Value.absent(), + i0.Value thumbnailAssetId = const i0.Value.absent(), + i0.Value isActivityEnabled = const i0.Value.absent(), + i0.Value order = const i0.Value.absent(), + }) => i1.RemoteAlbumEntityCompanion( + id: id, + name: name, + description: description, + createdAt: createdAt, + updatedAt: updatedAt, + ownerId: ownerId, + thumbnailAssetId: thumbnailAssetId, + isActivityEnabled: isActivityEnabled, + order: order, + ), + createCompanionCallback: + ({ + required String id, + required String name, + i0.Value description = const i0.Value.absent(), + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + required String ownerId, + i0.Value thumbnailAssetId = const i0.Value.absent(), + i0.Value isActivityEnabled = const i0.Value.absent(), + required i2.AlbumAssetOrder order, + }) => i1.RemoteAlbumEntityCompanion.insert( + id: id, + name: name, + description: description, + createdAt: createdAt, + updatedAt: updatedAt, + ownerId: ownerId, + thumbnailAssetId: thumbnailAssetId, + isActivityEnabled: isActivityEnabled, + order: order, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$RemoteAlbumEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$RemoteAlbumEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({ownerId = false, thumbnailAssetId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -426,53 +507,65 @@ class $$RemoteAlbumEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (ownerId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.ownerId, - referencedTable: - i1.$$RemoteAlbumEntityTableReferences._ownerIdTable(db), - referencedColumn: i1.$$RemoteAlbumEntityTableReferences - ._ownerIdTable(db) - .id, - ) as T; - } - if (thumbnailAssetId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.thumbnailAssetId, - referencedTable: i1.$$RemoteAlbumEntityTableReferences - ._thumbnailAssetIdTable(db), - referencedColumn: i1.$$RemoteAlbumEntityTableReferences - ._thumbnailAssetIdTable(db) - .id, - ) as T; - } + dynamic + > + >(state) { + if (ownerId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.ownerId, + referencedTable: i1 + .$$RemoteAlbumEntityTableReferences + ._ownerIdTable(db), + referencedColumn: i1 + .$$RemoteAlbumEntityTableReferences + ._ownerIdTable(db) + .id, + ) + as T; + } + if (thumbnailAssetId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.thumbnailAssetId, + referencedTable: i1 + .$$RemoteAlbumEntityTableReferences + ._thumbnailAssetIdTable(db), + referencedColumn: i1 + .$$RemoteAlbumEntityTableReferences + ._thumbnailAssetIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$RemoteAlbumEntityTableProcessedTableManager - = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$RemoteAlbumEntityTable, - i1.RemoteAlbumEntityData, - i1.$$RemoteAlbumEntityTableFilterComposer, - i1.$$RemoteAlbumEntityTableOrderingComposer, - i1.$$RemoteAlbumEntityTableAnnotationComposer, - $$RemoteAlbumEntityTableCreateCompanionBuilder, - $$RemoteAlbumEntityTableUpdateCompanionBuilder, - (i1.RemoteAlbumEntityData, i1.$$RemoteAlbumEntityTableReferences), - i1.RemoteAlbumEntityData, - i0.PrefetchHooks Function({bool ownerId, bool thumbnailAssetId})>; +typedef $$RemoteAlbumEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$RemoteAlbumEntityTable, + i1.RemoteAlbumEntityData, + i1.$$RemoteAlbumEntityTableFilterComposer, + i1.$$RemoteAlbumEntityTableOrderingComposer, + i1.$$RemoteAlbumEntityTableAnnotationComposer, + $$RemoteAlbumEntityTableCreateCompanionBuilder, + $$RemoteAlbumEntityTableUpdateCompanionBuilder, + (i1.RemoteAlbumEntityData, i1.$$RemoteAlbumEntityTableReferences), + i1.RemoteAlbumEntityData, + i0.PrefetchHooks Function({bool ownerId, bool thumbnailAssetId}) + >; class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity with i0.TableInfo<$RemoteAlbumEntityTable, i1.RemoteAlbumEntityData> { @@ -483,84 +576,129 @@ class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); @override late final i0.GeneratedColumn id = i0.GeneratedColumn( - 'id', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _nameMeta = - const i0.VerificationMeta('name'); + 'id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _nameMeta = const i0.VerificationMeta( + 'name', + ); @override late final i0.GeneratedColumn name = i0.GeneratedColumn( - 'name', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _descriptionMeta = - const i0.VerificationMeta('description'); + 'name', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _descriptionMeta = const i0.VerificationMeta( + 'description', + ); @override late final i0.GeneratedColumn description = - i0.GeneratedColumn('description', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: false, - defaultValue: const i4.Constant('')); - static const i0.VerificationMeta _createdAtMeta = - const i0.VerificationMeta('createdAt'); + i0.GeneratedColumn( + 'description', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const i4.Constant(''), + ); + static const i0.VerificationMeta _createdAtMeta = const i0.VerificationMeta( + 'createdAt', + ); @override late final i0.GeneratedColumn createdAt = - i0.GeneratedColumn('created_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i4.currentDateAndTime); - static const i0.VerificationMeta _updatedAtMeta = - const i0.VerificationMeta('updatedAt'); + i0.GeneratedColumn( + 'created_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); + static const i0.VerificationMeta _updatedAtMeta = const i0.VerificationMeta( + 'updatedAt', + ); @override late final i0.GeneratedColumn updatedAt = - i0.GeneratedColumn('updated_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i4.currentDateAndTime); - static const i0.VerificationMeta _ownerIdMeta = - const i0.VerificationMeta('ownerId'); + i0.GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); + static const i0.VerificationMeta _ownerIdMeta = const i0.VerificationMeta( + 'ownerId', + ); @override late final i0.GeneratedColumn ownerId = i0.GeneratedColumn( - 'owner_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'owner_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); static const i0.VerificationMeta _thumbnailAssetIdMeta = const i0.VerificationMeta('thumbnailAssetId'); @override late final i0.GeneratedColumn thumbnailAssetId = - i0.GeneratedColumn('thumbnail_asset_id', aliasedName, true, - type: i0.DriftSqlType.string, - requiredDuringInsert: false, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL')); + i0.GeneratedColumn( + 'thumbnail_asset_id', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); static const i0.VerificationMeta _isActivityEnabledMeta = const i0.VerificationMeta('isActivityEnabled'); @override late final i0.GeneratedColumn isActivityEnabled = - i0.GeneratedColumn('is_activity_enabled', aliasedName, false, - type: i0.DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'CHECK ("is_activity_enabled" IN (0, 1))'), - defaultValue: const i4.Constant(true)); + i0.GeneratedColumn( + 'is_activity_enabled', + aliasedName, + false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const i4.Constant(true), + ); @override late final i0.GeneratedColumnWithTypeConverter - order = i0.GeneratedColumn('order', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true) - .withConverter( - i1.$RemoteAlbumEntityTable.$converterorder); + order = + i0.GeneratedColumn( + 'order', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ).withConverter( + i1.$RemoteAlbumEntityTable.$converterorder, + ); @override List get $columns => [ - id, - name, - description, - createdAt, - updatedAt, - ownerId, - thumbnailAssetId, - isActivityEnabled, - order - ]; + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -568,8 +706,9 @@ class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity static const String $name = 'remote_album_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('id')) { @@ -579,41 +718,58 @@ class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity } if (data.containsKey('name')) { context.handle( - _nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta)); + _nameMeta, + name.isAcceptableOrUnknown(data['name']!, _nameMeta), + ); } else if (isInserting) { context.missing(_nameMeta); } if (data.containsKey('description')) { context.handle( + _descriptionMeta, + description.isAcceptableOrUnknown( + data['description']!, _descriptionMeta, - description.isAcceptableOrUnknown( - data['description']!, _descriptionMeta)); + ), + ); } if (data.containsKey('created_at')) { - context.handle(_createdAtMeta, - createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); + context.handle( + _createdAtMeta, + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta), + ); } if (data.containsKey('updated_at')) { - context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); + context.handle( + _updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), + ); } if (data.containsKey('owner_id')) { - context.handle(_ownerIdMeta, - ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta)); + context.handle( + _ownerIdMeta, + ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta), + ); } else if (isInserting) { context.missing(_ownerIdMeta); } if (data.containsKey('thumbnail_asset_id')) { context.handle( + _thumbnailAssetIdMeta, + thumbnailAssetId.isAcceptableOrUnknown( + data['thumbnail_asset_id']!, _thumbnailAssetIdMeta, - thumbnailAssetId.isAcceptableOrUnknown( - data['thumbnail_asset_id']!, _thumbnailAssetIdMeta)); + ), + ); } if (data.containsKey('is_activity_enabled')) { context.handle( + _isActivityEnabledMeta, + isActivityEnabled.isAcceptableOrUnknown( + data['is_activity_enabled']!, _isActivityEnabledMeta, - isActivityEnabled.isAcceptableOrUnknown( - data['is_activity_enabled']!, _isActivityEnabledMeta)); + ), + ); } return context; } @@ -621,29 +777,50 @@ class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity @override Set get $primaryKey => {id}; @override - i1.RemoteAlbumEntityData map(Map data, - {String? tablePrefix}) { + i1.RemoteAlbumEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.RemoteAlbumEntityData( - id: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!, - description: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}description'])!, + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + description: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}description'], + )!, createdAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, updatedAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, thumbnailAssetId: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, data['${effectivePrefix}thumbnail_asset_id']), + i0.DriftSqlType.string, + data['${effectivePrefix}thumbnail_asset_id'], + ), isActivityEnabled: attachedDatabase.typeMapping.read( - i0.DriftSqlType.bool, data['${effectivePrefix}is_activity_enabled'])!, - order: i1.$RemoteAlbumEntityTable.$converterorder.fromSql(attachedDatabase - .typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}order'])!), + i0.DriftSqlType.bool, + data['${effectivePrefix}is_activity_enabled'], + )!, + order: i1.$RemoteAlbumEntityTable.$converterorder.fromSql( + attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}order'], + )!, + ), ); } @@ -654,7 +831,8 @@ class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity static i0.JsonTypeConverter2 $converterorder = const i0.EnumIndexConverter( - i2.AlbumAssetOrder.values); + i2.AlbumAssetOrder.values, + ); @override bool get withoutRowId => true; @override @@ -672,16 +850,17 @@ class RemoteAlbumEntityData extends i0.DataClass final String? thumbnailAssetId; final bool isActivityEnabled; final i2.AlbumAssetOrder order; - const RemoteAlbumEntityData( - {required this.id, - required this.name, - required this.description, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - this.thumbnailAssetId, - required this.isActivityEnabled, - required this.order}); + const RemoteAlbumEntityData({ + required this.id, + required this.name, + required this.description, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + this.thumbnailAssetId, + required this.isActivityEnabled, + required this.order, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -697,13 +876,16 @@ class RemoteAlbumEntityData extends i0.DataClass map['is_activity_enabled'] = i0.Variable(isActivityEnabled); { map['order'] = i0.Variable( - i1.$RemoteAlbumEntityTable.$converterorder.toSql(order)); + i1.$RemoteAlbumEntityTable.$converterorder.toSql(order), + ); } return map; } - factory RemoteAlbumEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory RemoteAlbumEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return RemoteAlbumEntityData( id: serializer.fromJson(json['id']), @@ -714,8 +896,9 @@ class RemoteAlbumEntityData extends i0.DataClass ownerId: serializer.fromJson(json['ownerId']), thumbnailAssetId: serializer.fromJson(json['thumbnailAssetId']), isActivityEnabled: serializer.fromJson(json['isActivityEnabled']), - order: i1.$RemoteAlbumEntityTable.$converterorder - .fromJson(serializer.fromJson(json['order'])), + order: i1.$RemoteAlbumEntityTable.$converterorder.fromJson( + serializer.fromJson(json['order']), + ), ); } @override @@ -731,39 +914,41 @@ class RemoteAlbumEntityData extends i0.DataClass 'thumbnailAssetId': serializer.toJson(thumbnailAssetId), 'isActivityEnabled': serializer.toJson(isActivityEnabled), 'order': serializer.toJson( - i1.$RemoteAlbumEntityTable.$converterorder.toJson(order)), + i1.$RemoteAlbumEntityTable.$converterorder.toJson(order), + ), }; } - i1.RemoteAlbumEntityData copyWith( - {String? id, - String? name, - String? description, - DateTime? createdAt, - DateTime? updatedAt, - String? ownerId, - i0.Value thumbnailAssetId = const i0.Value.absent(), - bool? isActivityEnabled, - i2.AlbumAssetOrder? order}) => - i1.RemoteAlbumEntityData( - id: id ?? this.id, - name: name ?? this.name, - description: description ?? this.description, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - thumbnailAssetId: thumbnailAssetId.present - ? thumbnailAssetId.value - : this.thumbnailAssetId, - isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, - order: order ?? this.order, - ); + i1.RemoteAlbumEntityData copyWith({ + String? id, + String? name, + String? description, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + i0.Value thumbnailAssetId = const i0.Value.absent(), + bool? isActivityEnabled, + i2.AlbumAssetOrder? order, + }) => i1.RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId.present + ? thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); RemoteAlbumEntityData copyWithCompanion(i1.RemoteAlbumEntityCompanion data) { return RemoteAlbumEntityData( id: data.id.present ? data.id.value : this.id, name: data.name.present ? data.name.value : this.name, - description: - data.description.present ? data.description.value : this.description, + description: data.description.present + ? data.description.value + : this.description, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, @@ -794,8 +979,17 @@ class RemoteAlbumEntityData extends i0.DataClass } @override - int get hashCode => Object.hash(id, name, description, createdAt, updatedAt, - ownerId, thumbnailAssetId, isActivityEnabled, order); + int get hashCode => Object.hash( + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -843,10 +1037,10 @@ class RemoteAlbumEntityCompanion this.thumbnailAssetId = const i0.Value.absent(), this.isActivityEnabled = const i0.Value.absent(), required i2.AlbumAssetOrder order, - }) : id = i0.Value(id), - name = i0.Value(name), - ownerId = i0.Value(ownerId), - order = i0.Value(order); + }) : id = i0.Value(id), + name = i0.Value(name), + ownerId = i0.Value(ownerId), + order = i0.Value(order); static i0.Insertable custom({ i0.Expression? id, i0.Expression? name, @@ -871,16 +1065,17 @@ class RemoteAlbumEntityCompanion }); } - i1.RemoteAlbumEntityCompanion copyWith( - {i0.Value? id, - i0.Value? name, - i0.Value? description, - i0.Value? createdAt, - i0.Value? updatedAt, - i0.Value? ownerId, - i0.Value? thumbnailAssetId, - i0.Value? isActivityEnabled, - i0.Value? order}) { + i1.RemoteAlbumEntityCompanion copyWith({ + i0.Value? id, + i0.Value? name, + i0.Value? description, + i0.Value? createdAt, + i0.Value? updatedAt, + i0.Value? ownerId, + i0.Value? thumbnailAssetId, + i0.Value? isActivityEnabled, + i0.Value? order, + }) { return i1.RemoteAlbumEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -923,7 +1118,8 @@ class RemoteAlbumEntityCompanion } if (order.present) { map['order'] = i0.Variable( - i1.$RemoteAlbumEntityTable.$converterorder.toSql(order.value)); + i1.$RemoteAlbumEntityTable.$converterorder.toSql(order.value), + ); } return map; } diff --git a/mobile/lib/infrastructure/entities/remote_album_asset.entity.dart b/mobile/lib/infrastructure/entities/remote_album_asset.entity.dart index 1dcc336ed8..e99f5364a4 100644 --- a/mobile/lib/infrastructure/entities/remote_album_asset.entity.dart +++ b/mobile/lib/infrastructure/entities/remote_album_asset.entity.dart @@ -6,11 +6,9 @@ import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; class RemoteAlbumAssetEntity extends Table with DriftDefaultsMixin { const RemoteAlbumAssetEntity(); - TextColumn get assetId => - text().references(RemoteAssetEntity, #id, onDelete: KeyAction.cascade)(); + TextColumn get assetId => text().references(RemoteAssetEntity, #id, onDelete: KeyAction.cascade)(); - TextColumn get albumId => - text().references(RemoteAlbumEntity, #id, onDelete: KeyAction.cascade)(); + TextColumn get albumId => text().references(RemoteAlbumEntity, #id, onDelete: KeyAction.cascade)(); @override Set get primaryKey => {assetId, albumId}; diff --git a/mobile/lib/infrastructure/entities/remote_album_asset.entity.drift.dart b/mobile/lib/infrastructure/entities/remote_album_asset.entity.drift.dart index ab50607c96..adf22635c1 100644 --- a/mobile/lib/infrastructure/entities/remote_album_asset.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/remote_album_asset.entity.drift.dart @@ -11,76 +11,96 @@ import 'package:drift/internal/modular.dart' as i4; import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart' as i5; -typedef $$RemoteAlbumAssetEntityTableCreateCompanionBuilder - = i1.RemoteAlbumAssetEntityCompanion Function({ - required String assetId, - required String albumId, -}); -typedef $$RemoteAlbumAssetEntityTableUpdateCompanionBuilder - = i1.RemoteAlbumAssetEntityCompanion Function({ - i0.Value assetId, - i0.Value albumId, -}); +typedef $$RemoteAlbumAssetEntityTableCreateCompanionBuilder = + i1.RemoteAlbumAssetEntityCompanion Function({ + required String assetId, + required String albumId, + }); +typedef $$RemoteAlbumAssetEntityTableUpdateCompanionBuilder = + i1.RemoteAlbumAssetEntityCompanion Function({ + i0.Value assetId, + i0.Value albumId, + }); -final class $$RemoteAlbumAssetEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, - i1.$RemoteAlbumAssetEntityTable, - i1.RemoteAlbumAssetEntityData> { +final class $$RemoteAlbumAssetEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$RemoteAlbumAssetEntityTable, + i1.RemoteAlbumAssetEntityData + > { $$RemoteAlbumAssetEntityTableReferences( - super.$_db, super.$_table, super.$_typedResult); + super.$_db, + super.$_table, + super.$_typedResult, + ); static i3.$RemoteAssetEntityTable _assetIdTable(i0.GeneratedDatabase db) => i4.ReadDatabaseContainer(db) .resultSet('remote_asset_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i4.ReadDatabaseContainer(db) .resultSet( - 'remote_album_asset_entity') + 'remote_album_asset_entity', + ) .assetId, - i4.ReadDatabaseContainer(db) - .resultSet('remote_asset_entity') - .id)); + i4.ReadDatabaseContainer( + db, + ).resultSet('remote_asset_entity').id, + ), + ); i3.$$RemoteAssetEntityTableProcessedTableManager get assetId { final $_column = $_itemColumn('asset_id')!; final manager = i3 .$$RemoteAssetEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer( $_db, - i4.ReadDatabaseContainer($_db) - .resultSet('remote_asset_entity')) + ).resultSet('remote_asset_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_assetIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } static i5.$RemoteAlbumEntityTable _albumIdTable(i0.GeneratedDatabase db) => i4.ReadDatabaseContainer(db) .resultSet('remote_album_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i4.ReadDatabaseContainer(db) .resultSet( - 'remote_album_asset_entity') + 'remote_album_asset_entity', + ) .albumId, - i4.ReadDatabaseContainer(db) - .resultSet('remote_album_entity') - .id)); + i4.ReadDatabaseContainer( + db, + ).resultSet('remote_album_entity').id, + ), + ); i5.$$RemoteAlbumEntityTableProcessedTableManager get albumId { final $_column = $_itemColumn('album_id')!; final manager = i5 .$$RemoteAlbumEntityTableTableManager( + $_db, + i4.ReadDatabaseContainer( $_db, - i4.ReadDatabaseContainer($_db) - .resultSet('remote_album_entity')) + ).resultSet('remote_album_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_albumIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -95,45 +115,55 @@ class $$RemoteAlbumAssetEntityTableFilterComposer }); i3.$$RemoteAssetEntityTableFilterComposer get assetId { final i3.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableFilterComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$RemoteAlbumEntityTableFilterComposer get albumId { final i5.$$RemoteAlbumEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.albumId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_album_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$RemoteAlbumEntityTableFilterComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet('remote_album_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.albumId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$RemoteAlbumEntityTableFilterComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -150,48 +180,56 @@ class $$RemoteAlbumAssetEntityTableOrderingComposer i3.$$RemoteAssetEntityTableOrderingComposer get assetId { final i3.$$RemoteAssetEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableOrderingComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$RemoteAlbumEntityTableOrderingComposer get albumId { final i5.$$RemoteAlbumEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.albumId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_album_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$RemoteAlbumEntityTableOrderingComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'remote_album_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.albumId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$RemoteAlbumEntityTableOrderingComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -208,106 +246,129 @@ class $$RemoteAlbumAssetEntityTableAnnotationComposer i3.$$RemoteAssetEntityTableAnnotationComposer get assetId { final i3.$$RemoteAssetEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.assetId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i3.$$RemoteAssetEntityTableAnnotationComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.assetId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i3.$$RemoteAssetEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_asset_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i5.$$RemoteAlbumEntityTableAnnotationComposer get albumId { final i5.$$RemoteAlbumEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.albumId, - referencedTable: i4.ReadDatabaseContainer($db) - .resultSet('remote_album_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$RemoteAlbumEntityTableAnnotationComposer( - $db: $db, - $table: i4.ReadDatabaseContainer($db) - .resultSet( - 'remote_album_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.albumId, + referencedTable: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$RemoteAlbumEntityTableAnnotationComposer( + $db: $db, + $table: i4.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$RemoteAlbumAssetEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$RemoteAlbumAssetEntityTable, - i1.RemoteAlbumAssetEntityData, - i1.$$RemoteAlbumAssetEntityTableFilterComposer, - i1.$$RemoteAlbumAssetEntityTableOrderingComposer, - i1.$$RemoteAlbumAssetEntityTableAnnotationComposer, - $$RemoteAlbumAssetEntityTableCreateCompanionBuilder, - $$RemoteAlbumAssetEntityTableUpdateCompanionBuilder, - (i1.RemoteAlbumAssetEntityData, i1.$$RemoteAlbumAssetEntityTableReferences), - i1.RemoteAlbumAssetEntityData, - i0.PrefetchHooks Function({bool assetId, bool albumId})> { +class $$RemoteAlbumAssetEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$RemoteAlbumAssetEntityTable, + i1.RemoteAlbumAssetEntityData, + i1.$$RemoteAlbumAssetEntityTableFilterComposer, + i1.$$RemoteAlbumAssetEntityTableOrderingComposer, + i1.$$RemoteAlbumAssetEntityTableAnnotationComposer, + $$RemoteAlbumAssetEntityTableCreateCompanionBuilder, + $$RemoteAlbumAssetEntityTableUpdateCompanionBuilder, + ( + i1.RemoteAlbumAssetEntityData, + i1.$$RemoteAlbumAssetEntityTableReferences, + ), + i1.RemoteAlbumAssetEntityData, + i0.PrefetchHooks Function({bool assetId, bool albumId}) + > { $$RemoteAlbumAssetEntityTableTableManager( - i0.GeneratedDatabase db, i1.$RemoteAlbumAssetEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$RemoteAlbumAssetEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => i1.$$RemoteAlbumAssetEntityTableFilterComposer( - $db: db, $table: table), + $db: db, + $table: table, + ), createOrderingComposer: () => i1.$$RemoteAlbumAssetEntityTableOrderingComposer( - $db: db, $table: table), + $db: db, + $table: table, + ), createComputedFieldComposer: () => i1.$$RemoteAlbumAssetEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value assetId = const i0.Value.absent(), - i0.Value albumId = const i0.Value.absent(), - }) => - i1.RemoteAlbumAssetEntityCompanion( - assetId: assetId, - albumId: albumId, - ), - createCompanionCallback: ({ - required String assetId, - required String albumId, - }) => - i1.RemoteAlbumAssetEntityCompanion.insert( - assetId: assetId, - albumId: albumId, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value assetId = const i0.Value.absent(), + i0.Value albumId = const i0.Value.absent(), + }) => i1.RemoteAlbumAssetEntityCompanion( + assetId: assetId, + albumId: albumId, + ), + createCompanionCallback: + ({required String assetId, required String albumId}) => + i1.RemoteAlbumAssetEntityCompanion.insert( + assetId: assetId, + albumId: albumId, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$RemoteAlbumAssetEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$RemoteAlbumAssetEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({assetId = false, albumId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -318,83 +379,107 @@ class $$RemoteAlbumAssetEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (assetId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.assetId, - referencedTable: i1.$$RemoteAlbumAssetEntityTableReferences - ._assetIdTable(db), - referencedColumn: i1.$$RemoteAlbumAssetEntityTableReferences - ._assetIdTable(db) - .id, - ) as T; - } - if (albumId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.albumId, - referencedTable: i1.$$RemoteAlbumAssetEntityTableReferences - ._albumIdTable(db), - referencedColumn: i1.$$RemoteAlbumAssetEntityTableReferences - ._albumIdTable(db) - .id, - ) as T; - } + dynamic + > + >(state) { + if (assetId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.assetId, + referencedTable: i1 + .$$RemoteAlbumAssetEntityTableReferences + ._assetIdTable(db), + referencedColumn: i1 + .$$RemoteAlbumAssetEntityTableReferences + ._assetIdTable(db) + .id, + ) + as T; + } + if (albumId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.albumId, + referencedTable: i1 + .$$RemoteAlbumAssetEntityTableReferences + ._albumIdTable(db), + referencedColumn: i1 + .$$RemoteAlbumAssetEntityTableReferences + ._albumIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$RemoteAlbumAssetEntityTableProcessedTableManager - = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$RemoteAlbumAssetEntityTable, +typedef $$RemoteAlbumAssetEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$RemoteAlbumAssetEntityTable, + i1.RemoteAlbumAssetEntityData, + i1.$$RemoteAlbumAssetEntityTableFilterComposer, + i1.$$RemoteAlbumAssetEntityTableOrderingComposer, + i1.$$RemoteAlbumAssetEntityTableAnnotationComposer, + $$RemoteAlbumAssetEntityTableCreateCompanionBuilder, + $$RemoteAlbumAssetEntityTableUpdateCompanionBuilder, + ( i1.RemoteAlbumAssetEntityData, - i1.$$RemoteAlbumAssetEntityTableFilterComposer, - i1.$$RemoteAlbumAssetEntityTableOrderingComposer, - i1.$$RemoteAlbumAssetEntityTableAnnotationComposer, - $$RemoteAlbumAssetEntityTableCreateCompanionBuilder, - $$RemoteAlbumAssetEntityTableUpdateCompanionBuilder, - ( - i1.RemoteAlbumAssetEntityData, - i1.$$RemoteAlbumAssetEntityTableReferences - ), - i1.RemoteAlbumAssetEntityData, - i0.PrefetchHooks Function({bool assetId, bool albumId})>; + i1.$$RemoteAlbumAssetEntityTableReferences, + ), + i1.RemoteAlbumAssetEntityData, + i0.PrefetchHooks Function({bool assetId, bool albumId}) + >; class $RemoteAlbumAssetEntityTable extends i2.RemoteAlbumAssetEntity with - i0.TableInfo<$RemoteAlbumAssetEntityTable, - i1.RemoteAlbumAssetEntityData> { + i0.TableInfo< + $RemoteAlbumAssetEntityTable, + i1.RemoteAlbumAssetEntityData + > { @override final i0.GeneratedDatabase attachedDatabase; final String? _alias; $RemoteAlbumAssetEntityTable(this.attachedDatabase, [this._alias]); - static const i0.VerificationMeta _assetIdMeta = - const i0.VerificationMeta('assetId'); + static const i0.VerificationMeta _assetIdMeta = const i0.VerificationMeta( + 'assetId', + ); @override late final i0.GeneratedColumn assetId = i0.GeneratedColumn( - 'asset_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); - static const i0.VerificationMeta _albumIdMeta = - const i0.VerificationMeta('albumId'); + 'asset_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + static const i0.VerificationMeta _albumIdMeta = const i0.VerificationMeta( + 'albumId', + ); @override late final i0.GeneratedColumn albumId = i0.GeneratedColumn( - 'album_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_album_entity (id) ON DELETE CASCADE')); + 'album_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, albumId]; @override @@ -404,19 +489,24 @@ class $RemoteAlbumAssetEntityTable extends i2.RemoteAlbumAssetEntity static const String $name = 'remote_album_asset_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('asset_id')) { - context.handle(_assetIdMeta, - assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta)); + context.handle( + _assetIdMeta, + assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta), + ); } else if (isInserting) { context.missing(_assetIdMeta); } if (data.containsKey('album_id')) { - context.handle(_albumIdMeta, - albumId.isAcceptableOrUnknown(data['album_id']!, _albumIdMeta)); + context.handle( + _albumIdMeta, + albumId.isAcceptableOrUnknown(data['album_id']!, _albumIdMeta), + ); } else if (isInserting) { context.missing(_albumIdMeta); } @@ -426,14 +516,20 @@ class $RemoteAlbumAssetEntityTable extends i2.RemoteAlbumAssetEntity @override Set get $primaryKey => {assetId, albumId}; @override - i1.RemoteAlbumAssetEntityData map(Map data, - {String? tablePrefix}) { + i1.RemoteAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.RemoteAlbumAssetEntityData( - assetId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - albumId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}album_id'])!, + assetId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, ); } @@ -452,8 +548,10 @@ class RemoteAlbumAssetEntityData extends i0.DataClass implements i0.Insertable { final String assetId; final String albumId; - const RemoteAlbumAssetEntityData( - {required this.assetId, required this.albumId}); + const RemoteAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -462,8 +560,10 @@ class RemoteAlbumAssetEntityData extends i0.DataClass return map; } - factory RemoteAlbumAssetEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory RemoteAlbumAssetEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return RemoteAlbumAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -485,7 +585,8 @@ class RemoteAlbumAssetEntityData extends i0.DataClass albumId: albumId ?? this.albumId, ); RemoteAlbumAssetEntityData copyWithCompanion( - i1.RemoteAlbumAssetEntityCompanion data) { + i1.RemoteAlbumAssetEntityCompanion data, + ) { return RemoteAlbumAssetEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, albumId: data.albumId.present ? data.albumId.value : this.albumId, @@ -522,8 +623,8 @@ class RemoteAlbumAssetEntityCompanion RemoteAlbumAssetEntityCompanion.insert({ required String assetId, required String albumId, - }) : assetId = i0.Value(assetId), - albumId = i0.Value(albumId); + }) : assetId = i0.Value(assetId), + albumId = i0.Value(albumId); static i0.Insertable custom({ i0.Expression? assetId, i0.Expression? albumId, @@ -534,8 +635,10 @@ class RemoteAlbumAssetEntityCompanion }); } - i1.RemoteAlbumAssetEntityCompanion copyWith( - {i0.Value? assetId, i0.Value? albumId}) { + i1.RemoteAlbumAssetEntityCompanion copyWith({ + i0.Value? assetId, + i0.Value? albumId, + }) { return i1.RemoteAlbumAssetEntityCompanion( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, diff --git a/mobile/lib/infrastructure/entities/remote_album_user.entity.dart b/mobile/lib/infrastructure/entities/remote_album_user.entity.dart index 4198fb7e46..9cb277f9d0 100644 --- a/mobile/lib/infrastructure/entities/remote_album_user.entity.dart +++ b/mobile/lib/infrastructure/entities/remote_album_user.entity.dart @@ -7,11 +7,9 @@ import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; class RemoteAlbumUserEntity extends Table with DriftDefaultsMixin { const RemoteAlbumUserEntity(); - TextColumn get albumId => - text().references(RemoteAlbumEntity, #id, onDelete: KeyAction.cascade)(); + TextColumn get albumId => text().references(RemoteAlbumEntity, #id, onDelete: KeyAction.cascade)(); - TextColumn get userId => - text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); + TextColumn get userId => text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); IntColumn get role => intEnum()(); diff --git a/mobile/lib/infrastructure/entities/remote_album_user.entity.drift.dart b/mobile/lib/infrastructure/entities/remote_album_user.entity.drift.dart index 7ec1151a8a..f8167ddf94 100644 --- a/mobile/lib/infrastructure/entities/remote_album_user.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/remote_album_user.entity.drift.dart @@ -12,78 +12,98 @@ import 'package:drift/internal/modular.dart' as i5; import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' as i6; -typedef $$RemoteAlbumUserEntityTableCreateCompanionBuilder - = i1.RemoteAlbumUserEntityCompanion Function({ - required String albumId, - required String userId, - required i2.AlbumUserRole role, -}); -typedef $$RemoteAlbumUserEntityTableUpdateCompanionBuilder - = i1.RemoteAlbumUserEntityCompanion Function({ - i0.Value albumId, - i0.Value userId, - i0.Value role, -}); +typedef $$RemoteAlbumUserEntityTableCreateCompanionBuilder = + i1.RemoteAlbumUserEntityCompanion Function({ + required String albumId, + required String userId, + required i2.AlbumUserRole role, + }); +typedef $$RemoteAlbumUserEntityTableUpdateCompanionBuilder = + i1.RemoteAlbumUserEntityCompanion Function({ + i0.Value albumId, + i0.Value userId, + i0.Value role, + }); -final class $$RemoteAlbumUserEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, - i1.$RemoteAlbumUserEntityTable, - i1.RemoteAlbumUserEntityData> { +final class $$RemoteAlbumUserEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$RemoteAlbumUserEntityTable, + i1.RemoteAlbumUserEntityData + > { $$RemoteAlbumUserEntityTableReferences( - super.$_db, super.$_table, super.$_typedResult); + super.$_db, + super.$_table, + super.$_typedResult, + ); static i4.$RemoteAlbumEntityTable _albumIdTable(i0.GeneratedDatabase db) => i5.ReadDatabaseContainer(db) .resultSet('remote_album_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i5.ReadDatabaseContainer(db) .resultSet( - 'remote_album_user_entity') + 'remote_album_user_entity', + ) .albumId, - i5.ReadDatabaseContainer(db) - .resultSet('remote_album_entity') - .id)); + i5.ReadDatabaseContainer( + db, + ).resultSet('remote_album_entity').id, + ), + ); i4.$$RemoteAlbumEntityTableProcessedTableManager get albumId { final $_column = $_itemColumn('album_id')!; final manager = i4 .$$RemoteAlbumEntityTableTableManager( + $_db, + i5.ReadDatabaseContainer( $_db, - i5.ReadDatabaseContainer($_db) - .resultSet('remote_album_entity')) + ).resultSet('remote_album_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_albumIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } static i6.$UserEntityTable _userIdTable(i0.GeneratedDatabase db) => i5.ReadDatabaseContainer(db) .resultSet('user_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i5.ReadDatabaseContainer(db) .resultSet( - 'remote_album_user_entity') + 'remote_album_user_entity', + ) .userId, - i5.ReadDatabaseContainer(db) - .resultSet('user_entity') - .id)); + i5.ReadDatabaseContainer( + db, + ).resultSet('user_entity').id, + ), + ); i6.$$UserEntityTableProcessedTableManager get userId { final $_column = $_itemColumn('user_id')!; final manager = i6 .$$UserEntityTableTableManager( + $_db, + i5.ReadDatabaseContainer( $_db, - i5.ReadDatabaseContainer($_db) - .resultSet('user_entity')) + ).resultSet('user_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_userIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -97,51 +117,62 @@ class $$RemoteAlbumUserEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnWithTypeConverterFilters - get role => $composableBuilder( - column: $table.role, - builder: (column) => i0.ColumnWithTypeConverterFilters(column)); + get role => $composableBuilder( + column: $table.role, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); i4.$$RemoteAlbumEntityTableFilterComposer get albumId { final i4.$$RemoteAlbumEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.albumId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('remote_album_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$RemoteAlbumEntityTableFilterComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('remote_album_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.albumId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$RemoteAlbumEntityTableFilterComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i6.$$UserEntityTableFilterComposer get userId { final i6.$$UserEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.userId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i6.$$UserEntityTableFilterComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.userId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i6.$$UserEntityTableFilterComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -156,51 +187,62 @@ class $$RemoteAlbumUserEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get role => $composableBuilder( - column: $table.role, builder: (column) => i0.ColumnOrderings(column)); + column: $table.role, + builder: (column) => i0.ColumnOrderings(column), + ); i4.$$RemoteAlbumEntityTableOrderingComposer get albumId { final i4.$$RemoteAlbumEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.albumId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('remote_album_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$RemoteAlbumEntityTableOrderingComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet( - 'remote_album_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.albumId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$RemoteAlbumEntityTableOrderingComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i6.$$UserEntityTableOrderingComposer get userId { final i6.$$UserEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.userId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i6.$$UserEntityTableOrderingComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.userId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i6.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -220,108 +262,134 @@ class $$RemoteAlbumUserEntityTableAnnotationComposer i4.$$RemoteAlbumEntityTableAnnotationComposer get albumId { final i4.$$RemoteAlbumEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.albumId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('remote_album_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$RemoteAlbumEntityTableAnnotationComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet( - 'remote_album_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.albumId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$RemoteAlbumEntityTableAnnotationComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('remote_album_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } i6.$$UserEntityTableAnnotationComposer get userId { final i6.$$UserEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.userId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i6.$$UserEntityTableAnnotationComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.userId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i6.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$RemoteAlbumUserEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$RemoteAlbumUserEntityTable, - i1.RemoteAlbumUserEntityData, - i1.$$RemoteAlbumUserEntityTableFilterComposer, - i1.$$RemoteAlbumUserEntityTableOrderingComposer, - i1.$$RemoteAlbumUserEntityTableAnnotationComposer, - $$RemoteAlbumUserEntityTableCreateCompanionBuilder, - $$RemoteAlbumUserEntityTableUpdateCompanionBuilder, - (i1.RemoteAlbumUserEntityData, i1.$$RemoteAlbumUserEntityTableReferences), - i1.RemoteAlbumUserEntityData, - i0.PrefetchHooks Function({bool albumId, bool userId})> { +class $$RemoteAlbumUserEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$RemoteAlbumUserEntityTable, + i1.RemoteAlbumUserEntityData, + i1.$$RemoteAlbumUserEntityTableFilterComposer, + i1.$$RemoteAlbumUserEntityTableOrderingComposer, + i1.$$RemoteAlbumUserEntityTableAnnotationComposer, + $$RemoteAlbumUserEntityTableCreateCompanionBuilder, + $$RemoteAlbumUserEntityTableUpdateCompanionBuilder, + ( + i1.RemoteAlbumUserEntityData, + i1.$$RemoteAlbumUserEntityTableReferences, + ), + i1.RemoteAlbumUserEntityData, + i0.PrefetchHooks Function({bool albumId, bool userId}) + > { $$RemoteAlbumUserEntityTableTableManager( - i0.GeneratedDatabase db, i1.$RemoteAlbumUserEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$RemoteAlbumUserEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => i1.$$RemoteAlbumUserEntityTableFilterComposer( - $db: db, $table: table), + $db: db, + $table: table, + ), createOrderingComposer: () => i1.$$RemoteAlbumUserEntityTableOrderingComposer( - $db: db, $table: table), + $db: db, + $table: table, + ), createComputedFieldComposer: () => i1.$$RemoteAlbumUserEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value albumId = const i0.Value.absent(), - i0.Value userId = const i0.Value.absent(), - i0.Value role = const i0.Value.absent(), - }) => - i1.RemoteAlbumUserEntityCompanion( - albumId: albumId, - userId: userId, - role: role, - ), - createCompanionCallback: ({ - required String albumId, - required String userId, - required i2.AlbumUserRole role, - }) => - i1.RemoteAlbumUserEntityCompanion.insert( - albumId: albumId, - userId: userId, - role: role, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value albumId = const i0.Value.absent(), + i0.Value userId = const i0.Value.absent(), + i0.Value role = const i0.Value.absent(), + }) => i1.RemoteAlbumUserEntityCompanion( + albumId: albumId, + userId: userId, + role: role, + ), + createCompanionCallback: + ({ + required String albumId, + required String userId, + required i2.AlbumUserRole role, + }) => i1.RemoteAlbumUserEntityCompanion.insert( + albumId: albumId, + userId: userId, + role: role, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$RemoteAlbumUserEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$RemoteAlbumUserEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({albumId = false, userId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -332,89 +400,115 @@ class $$RemoteAlbumUserEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (albumId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.albumId, - referencedTable: i1.$$RemoteAlbumUserEntityTableReferences - ._albumIdTable(db), - referencedColumn: i1.$$RemoteAlbumUserEntityTableReferences - ._albumIdTable(db) - .id, - ) as T; - } - if (userId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.userId, - referencedTable: i1.$$RemoteAlbumUserEntityTableReferences - ._userIdTable(db), - referencedColumn: i1.$$RemoteAlbumUserEntityTableReferences - ._userIdTable(db) - .id, - ) as T; - } + dynamic + > + >(state) { + if (albumId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.albumId, + referencedTable: i1 + .$$RemoteAlbumUserEntityTableReferences + ._albumIdTable(db), + referencedColumn: i1 + .$$RemoteAlbumUserEntityTableReferences + ._albumIdTable(db) + .id, + ) + as T; + } + if (userId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.userId, + referencedTable: i1 + .$$RemoteAlbumUserEntityTableReferences + ._userIdTable(db), + referencedColumn: i1 + .$$RemoteAlbumUserEntityTableReferences + ._userIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$RemoteAlbumUserEntityTableProcessedTableManager - = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$RemoteAlbumUserEntityTable, - i1.RemoteAlbumUserEntityData, - i1.$$RemoteAlbumUserEntityTableFilterComposer, - i1.$$RemoteAlbumUserEntityTableOrderingComposer, - i1.$$RemoteAlbumUserEntityTableAnnotationComposer, - $$RemoteAlbumUserEntityTableCreateCompanionBuilder, - $$RemoteAlbumUserEntityTableUpdateCompanionBuilder, - ( - i1.RemoteAlbumUserEntityData, - i1.$$RemoteAlbumUserEntityTableReferences - ), - i1.RemoteAlbumUserEntityData, - i0.PrefetchHooks Function({bool albumId, bool userId})>; +typedef $$RemoteAlbumUserEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$RemoteAlbumUserEntityTable, + i1.RemoteAlbumUserEntityData, + i1.$$RemoteAlbumUserEntityTableFilterComposer, + i1.$$RemoteAlbumUserEntityTableOrderingComposer, + i1.$$RemoteAlbumUserEntityTableAnnotationComposer, + $$RemoteAlbumUserEntityTableCreateCompanionBuilder, + $$RemoteAlbumUserEntityTableUpdateCompanionBuilder, + (i1.RemoteAlbumUserEntityData, i1.$$RemoteAlbumUserEntityTableReferences), + i1.RemoteAlbumUserEntityData, + i0.PrefetchHooks Function({bool albumId, bool userId}) + >; class $RemoteAlbumUserEntityTable extends i3.RemoteAlbumUserEntity with - i0 - .TableInfo<$RemoteAlbumUserEntityTable, i1.RemoteAlbumUserEntityData> { + i0.TableInfo< + $RemoteAlbumUserEntityTable, + i1.RemoteAlbumUserEntityData + > { @override final i0.GeneratedDatabase attachedDatabase; final String? _alias; $RemoteAlbumUserEntityTable(this.attachedDatabase, [this._alias]); - static const i0.VerificationMeta _albumIdMeta = - const i0.VerificationMeta('albumId'); + static const i0.VerificationMeta _albumIdMeta = const i0.VerificationMeta( + 'albumId', + ); @override late final i0.GeneratedColumn albumId = i0.GeneratedColumn( - 'album_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_album_entity (id) ON DELETE CASCADE')); - static const i0.VerificationMeta _userIdMeta = - const i0.VerificationMeta('userId'); + 'album_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + static const i0.VerificationMeta _userIdMeta = const i0.VerificationMeta( + 'userId', + ); @override late final i0.GeneratedColumn userId = i0.GeneratedColumn( - 'user_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'user_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); @override late final i0.GeneratedColumnWithTypeConverter role = - i0.GeneratedColumn('role', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true) - .withConverter( - i1.$RemoteAlbumUserEntityTable.$converterrole); + i0.GeneratedColumn( + 'role', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ).withConverter( + i1.$RemoteAlbumUserEntityTable.$converterrole, + ); @override List get $columns => [albumId, userId, role]; @override @@ -424,19 +518,24 @@ class $RemoteAlbumUserEntityTable extends i3.RemoteAlbumUserEntity static const String $name = 'remote_album_user_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('album_id')) { - context.handle(_albumIdMeta, - albumId.isAcceptableOrUnknown(data['album_id']!, _albumIdMeta)); + context.handle( + _albumIdMeta, + albumId.isAcceptableOrUnknown(data['album_id']!, _albumIdMeta), + ); } else if (isInserting) { context.missing(_albumIdMeta); } if (data.containsKey('user_id')) { - context.handle(_userIdMeta, - userId.isAcceptableOrUnknown(data['user_id']!, _userIdMeta)); + context.handle( + _userIdMeta, + userId.isAcceptableOrUnknown(data['user_id']!, _userIdMeta), + ); } else if (isInserting) { context.missing(_userIdMeta); } @@ -446,17 +545,26 @@ class $RemoteAlbumUserEntityTable extends i3.RemoteAlbumUserEntity @override Set get $primaryKey => {albumId, userId}; @override - i1.RemoteAlbumUserEntityData map(Map data, - {String? tablePrefix}) { + i1.RemoteAlbumUserEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.RemoteAlbumUserEntityData( - albumId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}album_id'])!, - userId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}user_id'])!, + albumId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + userId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, role: i1.$RemoteAlbumUserEntityTable.$converterrole.fromSql( - attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}role'])!), + attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}role'], + )!, + ), ); } @@ -478,8 +586,11 @@ class RemoteAlbumUserEntityData extends i0.DataClass final String albumId; final String userId; final i2.AlbumUserRole role; - const RemoteAlbumUserEntityData( - {required this.albumId, required this.userId, required this.role}); + const RemoteAlbumUserEntityData({ + required this.albumId, + required this.userId, + required this.role, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -487,19 +598,23 @@ class RemoteAlbumUserEntityData extends i0.DataClass map['user_id'] = i0.Variable(userId); { map['role'] = i0.Variable( - i1.$RemoteAlbumUserEntityTable.$converterrole.toSql(role)); + i1.$RemoteAlbumUserEntityTable.$converterrole.toSql(role), + ); } return map; } - factory RemoteAlbumUserEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory RemoteAlbumUserEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return RemoteAlbumUserEntityData( albumId: serializer.fromJson(json['albumId']), userId: serializer.fromJson(json['userId']), - role: i1.$RemoteAlbumUserEntityTable.$converterrole - .fromJson(serializer.fromJson(json['role'])), + role: i1.$RemoteAlbumUserEntityTable.$converterrole.fromJson( + serializer.fromJson(json['role']), + ), ); } @override @@ -509,19 +624,23 @@ class RemoteAlbumUserEntityData extends i0.DataClass 'albumId': serializer.toJson(albumId), 'userId': serializer.toJson(userId), 'role': serializer.toJson( - i1.$RemoteAlbumUserEntityTable.$converterrole.toJson(role)), + i1.$RemoteAlbumUserEntityTable.$converterrole.toJson(role), + ), }; } - i1.RemoteAlbumUserEntityData copyWith( - {String? albumId, String? userId, i2.AlbumUserRole? role}) => - i1.RemoteAlbumUserEntityData( - albumId: albumId ?? this.albumId, - userId: userId ?? this.userId, - role: role ?? this.role, - ); + i1.RemoteAlbumUserEntityData copyWith({ + String? albumId, + String? userId, + i2.AlbumUserRole? role, + }) => i1.RemoteAlbumUserEntityData( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); RemoteAlbumUserEntityData copyWithCompanion( - i1.RemoteAlbumUserEntityCompanion data) { + i1.RemoteAlbumUserEntityCompanion data, + ) { return RemoteAlbumUserEntityData( albumId: data.albumId.present ? data.albumId.value : this.albumId, userId: data.userId.present ? data.userId.value : this.userId, @@ -564,9 +683,9 @@ class RemoteAlbumUserEntityCompanion required String albumId, required String userId, required i2.AlbumUserRole role, - }) : albumId = i0.Value(albumId), - userId = i0.Value(userId), - role = i0.Value(role); + }) : albumId = i0.Value(albumId), + userId = i0.Value(userId), + role = i0.Value(role); static i0.Insertable custom({ i0.Expression? albumId, i0.Expression? userId, @@ -579,10 +698,11 @@ class RemoteAlbumUserEntityCompanion }); } - i1.RemoteAlbumUserEntityCompanion copyWith( - {i0.Value? albumId, - i0.Value? userId, - i0.Value? role}) { + i1.RemoteAlbumUserEntityCompanion copyWith({ + i0.Value? albumId, + i0.Value? userId, + i0.Value? role, + }) { return i1.RemoteAlbumUserEntityCompanion( albumId: albumId ?? this.albumId, userId: userId ?? this.userId, @@ -601,7 +721,8 @@ class RemoteAlbumUserEntityCompanion } if (role.present) { map['role'] = i0.Variable( - i1.$RemoteAlbumUserEntityTable.$converterrole.toSql(role.value)); + i1.$RemoteAlbumUserEntityTable.$converterrole.toSql(role.value), + ); } return map; } diff --git a/mobile/lib/infrastructure/entities/remote_asset.entity.dart b/mobile/lib/infrastructure/entities/remote_asset.entity.dart index 0b2896538e..ecc0aa3d76 100644 --- a/mobile/lib/infrastructure/entities/remote_asset.entity.dart +++ b/mobile/lib/infrastructure/entities/remote_asset.entity.dart @@ -5,14 +5,19 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:immich_mobile/infrastructure/utils/asset.mixin.dart'; import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; -@TableIndex( - name: 'UQ_remote_asset_owner_checksum', - columns: {#checksum, #ownerId}, - unique: true, -) +@TableIndex(name: 'idx_remote_asset_owner_checksum', columns: {#ownerId, #checksum}) +@TableIndex.sql(''' +CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum +ON remote_asset_entity (owner_id, checksum) +WHERE (library_id IS NULL); +''') +@TableIndex.sql(''' +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); +''') @TableIndex(name: 'idx_remote_asset_checksum', columns: {#checksum}) -class RemoteAssetEntity extends Table - with DriftDefaultsMixin, AssetEntityMixin { +class RemoteAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin { const RemoteAssetEntity(); TextColumn get id => text()(); @@ -21,8 +26,7 @@ class RemoteAssetEntity extends Table BoolColumn get isFavorite => boolean().withDefault(const Constant(false))(); - TextColumn get ownerId => - text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); + TextColumn get ownerId => text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); DateTimeColumn get localDateTime => dateTime().nullable()(); @@ -36,27 +40,29 @@ class RemoteAssetEntity extends Table TextColumn get stackId => text().nullable()(); + TextColumn get libraryId => text().nullable()(); + @override Set get primaryKey => {id}; } extension RemoteAssetEntityDataDomainEx on RemoteAssetEntityData { RemoteAsset toDto() => RemoteAsset( - id: id, - name: name, - ownerId: ownerId, - checksum: checksum, - type: type, - createdAt: createdAt, - updatedAt: updatedAt, - durationInSeconds: durationInSeconds, - isFavorite: isFavorite, - height: height, - width: width, - thumbHash: thumbHash, - visibility: visibility, - livePhotoVideoId: livePhotoVideoId, - localId: null, - stackId: stackId, - ); + id: id, + name: name, + ownerId: ownerId, + checksum: checksum, + type: type, + createdAt: createdAt, + updatedAt: updatedAt, + durationInSeconds: durationInSeconds, + isFavorite: isFavorite, + height: height, + width: width, + thumbHash: thumbHash, + visibility: visibility, + livePhotoVideoId: livePhotoVideoId, + localId: null, + stackId: stackId, + ); } diff --git a/mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart b/mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart index 543ed65985..9681d1e75d 100644 --- a/mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart @@ -11,78 +11,92 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' as i5; import 'package:drift/internal/modular.dart' as i6; -typedef $$RemoteAssetEntityTableCreateCompanionBuilder - = i1.RemoteAssetEntityCompanion Function({ - required String name, - required i2.AssetType type, - i0.Value createdAt, - i0.Value updatedAt, - i0.Value width, - i0.Value height, - i0.Value durationInSeconds, - required String id, - required String checksum, - i0.Value isFavorite, - required String ownerId, - i0.Value localDateTime, - i0.Value thumbHash, - i0.Value deletedAt, - i0.Value livePhotoVideoId, - required i2.AssetVisibility visibility, - i0.Value stackId, -}); -typedef $$RemoteAssetEntityTableUpdateCompanionBuilder - = i1.RemoteAssetEntityCompanion Function({ - i0.Value name, - i0.Value type, - i0.Value createdAt, - i0.Value updatedAt, - i0.Value width, - i0.Value height, - i0.Value durationInSeconds, - i0.Value id, - i0.Value checksum, - i0.Value isFavorite, - i0.Value ownerId, - i0.Value localDateTime, - i0.Value thumbHash, - i0.Value deletedAt, - i0.Value livePhotoVideoId, - i0.Value visibility, - i0.Value stackId, -}); +typedef $$RemoteAssetEntityTableCreateCompanionBuilder = + i1.RemoteAssetEntityCompanion Function({ + required String name, + required i2.AssetType type, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value width, + i0.Value height, + i0.Value durationInSeconds, + required String id, + required String checksum, + i0.Value isFavorite, + required String ownerId, + i0.Value localDateTime, + i0.Value thumbHash, + i0.Value deletedAt, + i0.Value livePhotoVideoId, + required i2.AssetVisibility visibility, + i0.Value stackId, + i0.Value libraryId, + }); +typedef $$RemoteAssetEntityTableUpdateCompanionBuilder = + i1.RemoteAssetEntityCompanion Function({ + i0.Value name, + i0.Value type, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value width, + i0.Value height, + i0.Value durationInSeconds, + i0.Value id, + i0.Value checksum, + i0.Value isFavorite, + i0.Value ownerId, + i0.Value localDateTime, + i0.Value thumbHash, + i0.Value deletedAt, + i0.Value livePhotoVideoId, + i0.Value visibility, + i0.Value stackId, + i0.Value libraryId, + }); -final class $$RemoteAssetEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, - i1.$RemoteAssetEntityTable, - i1.RemoteAssetEntityData> { +final class $$RemoteAssetEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$RemoteAssetEntityTable, + i1.RemoteAssetEntityData + > { $$RemoteAssetEntityTableReferences( - super.$_db, super.$_table, super.$_typedResult); + super.$_db, + super.$_table, + super.$_typedResult, + ); static i5.$UserEntityTable _ownerIdTable(i0.GeneratedDatabase db) => i6.ReadDatabaseContainer(db) .resultSet('user_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i6.ReadDatabaseContainer(db) .resultSet('remote_asset_entity') .ownerId, - i6.ReadDatabaseContainer(db) - .resultSet('user_entity') - .id)); + i6.ReadDatabaseContainer( + db, + ).resultSet('user_entity').id, + ), + ); i5.$$UserEntityTableProcessedTableManager get ownerId { final $_column = $_itemColumn('owner_id')!; final manager = i5 .$$UserEntityTableTableManager( + $_db, + i6.ReadDatabaseContainer( $_db, - i6.ReadDatabaseContainer($_db) - .resultSet('user_entity')) + ).resultSet('user_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_ownerIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -96,79 +110,116 @@ class $$RemoteAssetEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnFilters(column)); + column: $table.name, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnWithTypeConverterFilters get type => $composableBuilder( - column: $table.type, - builder: (column) => i0.ColumnWithTypeConverterFilters(column)); + column: $table.type, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); i0.ColumnFilters get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get width => $composableBuilder( - column: $table.width, builder: (column) => i0.ColumnFilters(column)); + column: $table.width, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get height => $composableBuilder( - column: $table.height, builder: (column) => i0.ColumnFilters(column)); + column: $table.height, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get durationInSeconds => $composableBuilder( - column: $table.durationInSeconds, - builder: (column) => i0.ColumnFilters(column)); + column: $table.durationInSeconds, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnFilters(column)); + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get checksum => $composableBuilder( - column: $table.checksum, builder: (column) => i0.ColumnFilters(column)); + column: $table.checksum, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get isFavorite => $composableBuilder( - column: $table.isFavorite, builder: (column) => i0.ColumnFilters(column)); + column: $table.isFavorite, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get localDateTime => $composableBuilder( - column: $table.localDateTime, - builder: (column) => i0.ColumnFilters(column)); + column: $table.localDateTime, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get thumbHash => $composableBuilder( - column: $table.thumbHash, builder: (column) => i0.ColumnFilters(column)); + column: $table.thumbHash, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get deletedAt => $composableBuilder( - column: $table.deletedAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.deletedAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get livePhotoVideoId => $composableBuilder( - column: $table.livePhotoVideoId, - builder: (column) => i0.ColumnFilters(column)); + column: $table.livePhotoVideoId, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnWithTypeConverterFilters - get visibility => $composableBuilder( - column: $table.visibility, - builder: (column) => i0.ColumnWithTypeConverterFilters(column)); + get visibility => $composableBuilder( + column: $table.visibility, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); i0.ColumnFilters get stackId => $composableBuilder( - column: $table.stackId, builder: (column) => i0.ColumnFilters(column)); + column: $table.stackId, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get libraryId => $composableBuilder( + column: $table.libraryId, + builder: (column) => i0.ColumnFilters(column), + ); i5.$$UserEntityTableFilterComposer get ownerId { final i5.$$UserEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableFilterComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableFilterComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -183,81 +234,114 @@ class $$RemoteAssetEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnOrderings(column)); + column: $table.name, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get type => $composableBuilder( - column: $table.type, builder: (column) => i0.ColumnOrderings(column)); + column: $table.type, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get createdAt => $composableBuilder( - column: $table.createdAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get updatedAt => $composableBuilder( - column: $table.updatedAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get width => $composableBuilder( - column: $table.width, builder: (column) => i0.ColumnOrderings(column)); + column: $table.width, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get height => $composableBuilder( - column: $table.height, builder: (column) => i0.ColumnOrderings(column)); + column: $table.height, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get durationInSeconds => $composableBuilder( - column: $table.durationInSeconds, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.durationInSeconds, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get checksum => $composableBuilder( - column: $table.checksum, builder: (column) => i0.ColumnOrderings(column)); + column: $table.checksum, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get isFavorite => $composableBuilder( - column: $table.isFavorite, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.isFavorite, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get localDateTime => $composableBuilder( - column: $table.localDateTime, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.localDateTime, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get thumbHash => $composableBuilder( - column: $table.thumbHash, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.thumbHash, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get deletedAt => $composableBuilder( - column: $table.deletedAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.deletedAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get livePhotoVideoId => $composableBuilder( - column: $table.livePhotoVideoId, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.livePhotoVideoId, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get visibility => $composableBuilder( - column: $table.visibility, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.visibility, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get stackId => $composableBuilder( - column: $table.stackId, builder: (column) => i0.ColumnOrderings(column)); + column: $table.stackId, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get libraryId => $composableBuilder( + column: $table.libraryId, + builder: (column) => i0.ColumnOrderings(column), + ); i5.$$UserEntityTableOrderingComposer get ownerId { final i5.$$UserEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableOrderingComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -290,7 +374,9 @@ class $$RemoteAssetEntityTableAnnotationComposer $composableBuilder(column: $table.height, builder: (column) => column); i0.GeneratedColumn get durationInSeconds => $composableBuilder( - column: $table.durationInSeconds, builder: (column) => column); + column: $table.durationInSeconds, + builder: (column) => column, + ); i0.GeneratedColumn get id => $composableBuilder(column: $table.id, builder: (column) => column); @@ -299,10 +385,14 @@ class $$RemoteAssetEntityTableAnnotationComposer $composableBuilder(column: $table.checksum, builder: (column) => column); i0.GeneratedColumn get isFavorite => $composableBuilder( - column: $table.isFavorite, builder: (column) => column); + column: $table.isFavorite, + builder: (column) => column, + ); i0.GeneratedColumn get localDateTime => $composableBuilder( - column: $table.localDateTime, builder: (column) => column); + column: $table.localDateTime, + builder: (column) => column, + ); i0.GeneratedColumn get thumbHash => $composableBuilder(column: $table.thumbHash, builder: (column) => column); @@ -311,53 +401,70 @@ class $$RemoteAssetEntityTableAnnotationComposer $composableBuilder(column: $table.deletedAt, builder: (column) => column); i0.GeneratedColumn get livePhotoVideoId => $composableBuilder( - column: $table.livePhotoVideoId, builder: (column) => column); + column: $table.livePhotoVideoId, + builder: (column) => column, + ); i0.GeneratedColumnWithTypeConverter get visibility => $composableBuilder( - column: $table.visibility, builder: (column) => column); + column: $table.visibility, + builder: (column) => column, + ); i0.GeneratedColumn get stackId => $composableBuilder(column: $table.stackId, builder: (column) => column); + i0.GeneratedColumn get libraryId => + $composableBuilder(column: $table.libraryId, builder: (column) => column); + i5.$$UserEntityTableAnnotationComposer get ownerId { final i5.$$UserEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableAnnotationComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$RemoteAssetEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$RemoteAssetEntityTable, - i1.RemoteAssetEntityData, - i1.$$RemoteAssetEntityTableFilterComposer, - i1.$$RemoteAssetEntityTableOrderingComposer, - i1.$$RemoteAssetEntityTableAnnotationComposer, - $$RemoteAssetEntityTableCreateCompanionBuilder, - $$RemoteAssetEntityTableUpdateCompanionBuilder, - (i1.RemoteAssetEntityData, i1.$$RemoteAssetEntityTableReferences), - i1.RemoteAssetEntityData, - i0.PrefetchHooks Function({bool ownerId})> { +class $$RemoteAssetEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$RemoteAssetEntityTable, + i1.RemoteAssetEntityData, + i1.$$RemoteAssetEntityTableFilterComposer, + i1.$$RemoteAssetEntityTableOrderingComposer, + i1.$$RemoteAssetEntityTableAnnotationComposer, + $$RemoteAssetEntityTableCreateCompanionBuilder, + $$RemoteAssetEntityTableUpdateCompanionBuilder, + (i1.RemoteAssetEntityData, i1.$$RemoteAssetEntityTableReferences), + i1.RemoteAssetEntityData, + i0.PrefetchHooks Function({bool ownerId}) + > { $$RemoteAssetEntityTableTableManager( - i0.GeneratedDatabase db, i1.$RemoteAssetEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$RemoteAssetEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -366,95 +473,105 @@ class $$RemoteAssetEntityTableTableManager extends i0.RootTableManager< .$$RemoteAssetEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$RemoteAssetEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value name = const i0.Value.absent(), - i0.Value type = const i0.Value.absent(), - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value width = const i0.Value.absent(), - i0.Value height = const i0.Value.absent(), - i0.Value durationInSeconds = const i0.Value.absent(), - i0.Value id = const i0.Value.absent(), - i0.Value checksum = const i0.Value.absent(), - i0.Value isFavorite = const i0.Value.absent(), - i0.Value ownerId = const i0.Value.absent(), - i0.Value localDateTime = const i0.Value.absent(), - i0.Value thumbHash = const i0.Value.absent(), - i0.Value deletedAt = const i0.Value.absent(), - i0.Value livePhotoVideoId = const i0.Value.absent(), - i0.Value visibility = const i0.Value.absent(), - i0.Value stackId = const i0.Value.absent(), - }) => - i1.RemoteAssetEntityCompanion( - name: name, - type: type, - createdAt: createdAt, - updatedAt: updatedAt, - width: width, - height: height, - durationInSeconds: durationInSeconds, - id: id, - checksum: checksum, - isFavorite: isFavorite, - ownerId: ownerId, - localDateTime: localDateTime, - thumbHash: thumbHash, - deletedAt: deletedAt, - livePhotoVideoId: livePhotoVideoId, - visibility: visibility, - stackId: stackId, - ), - createCompanionCallback: ({ - required String name, - required i2.AssetType type, - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value width = const i0.Value.absent(), - i0.Value height = const i0.Value.absent(), - i0.Value durationInSeconds = const i0.Value.absent(), - required String id, - required String checksum, - i0.Value isFavorite = const i0.Value.absent(), - required String ownerId, - i0.Value localDateTime = const i0.Value.absent(), - i0.Value thumbHash = const i0.Value.absent(), - i0.Value deletedAt = const i0.Value.absent(), - i0.Value livePhotoVideoId = const i0.Value.absent(), - required i2.AssetVisibility visibility, - i0.Value stackId = const i0.Value.absent(), - }) => - i1.RemoteAssetEntityCompanion.insert( - name: name, - type: type, - createdAt: createdAt, - updatedAt: updatedAt, - width: width, - height: height, - durationInSeconds: durationInSeconds, - id: id, - checksum: checksum, - isFavorite: isFavorite, - ownerId: ownerId, - localDateTime: localDateTime, - thumbHash: thumbHash, - deletedAt: deletedAt, - livePhotoVideoId: livePhotoVideoId, - visibility: visibility, - stackId: stackId, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value name = const i0.Value.absent(), + i0.Value type = const i0.Value.absent(), + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value width = const i0.Value.absent(), + i0.Value height = const i0.Value.absent(), + i0.Value durationInSeconds = const i0.Value.absent(), + i0.Value id = const i0.Value.absent(), + i0.Value checksum = const i0.Value.absent(), + i0.Value isFavorite = const i0.Value.absent(), + i0.Value ownerId = const i0.Value.absent(), + i0.Value localDateTime = const i0.Value.absent(), + i0.Value thumbHash = const i0.Value.absent(), + i0.Value deletedAt = const i0.Value.absent(), + i0.Value livePhotoVideoId = const i0.Value.absent(), + i0.Value visibility = + const i0.Value.absent(), + i0.Value stackId = const i0.Value.absent(), + i0.Value libraryId = const i0.Value.absent(), + }) => i1.RemoteAssetEntityCompanion( + name: name, + type: type, + createdAt: createdAt, + updatedAt: updatedAt, + width: width, + height: height, + durationInSeconds: durationInSeconds, + id: id, + checksum: checksum, + isFavorite: isFavorite, + ownerId: ownerId, + localDateTime: localDateTime, + thumbHash: thumbHash, + deletedAt: deletedAt, + livePhotoVideoId: livePhotoVideoId, + visibility: visibility, + stackId: stackId, + libraryId: libraryId, + ), + createCompanionCallback: + ({ + required String name, + required i2.AssetType type, + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value width = const i0.Value.absent(), + i0.Value height = const i0.Value.absent(), + i0.Value durationInSeconds = const i0.Value.absent(), + required String id, + required String checksum, + i0.Value isFavorite = const i0.Value.absent(), + required String ownerId, + i0.Value localDateTime = const i0.Value.absent(), + i0.Value thumbHash = const i0.Value.absent(), + i0.Value deletedAt = const i0.Value.absent(), + i0.Value livePhotoVideoId = const i0.Value.absent(), + required i2.AssetVisibility visibility, + i0.Value stackId = const i0.Value.absent(), + i0.Value libraryId = const i0.Value.absent(), + }) => i1.RemoteAssetEntityCompanion.insert( + name: name, + type: type, + createdAt: createdAt, + updatedAt: updatedAt, + width: width, + height: height, + durationInSeconds: durationInSeconds, + id: id, + checksum: checksum, + isFavorite: isFavorite, + ownerId: ownerId, + localDateTime: localDateTime, + thumbHash: thumbHash, + deletedAt: deletedAt, + livePhotoVideoId: livePhotoVideoId, + visibility: visibility, + stackId: stackId, + libraryId: libraryId, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$RemoteAssetEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$RemoteAssetEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({ownerId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -465,45 +582,54 @@ class $$RemoteAssetEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (ownerId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.ownerId, - referencedTable: - i1.$$RemoteAssetEntityTableReferences._ownerIdTable(db), - referencedColumn: i1.$$RemoteAssetEntityTableReferences - ._ownerIdTable(db) - .id, - ) as T; - } + dynamic + > + >(state) { + if (ownerId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.ownerId, + referencedTable: i1 + .$$RemoteAssetEntityTableReferences + ._ownerIdTable(db), + referencedColumn: i1 + .$$RemoteAssetEntityTableReferences + ._ownerIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$RemoteAssetEntityTableProcessedTableManager - = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$RemoteAssetEntityTable, - i1.RemoteAssetEntityData, - i1.$$RemoteAssetEntityTableFilterComposer, - i1.$$RemoteAssetEntityTableOrderingComposer, - i1.$$RemoteAssetEntityTableAnnotationComposer, - $$RemoteAssetEntityTableCreateCompanionBuilder, - $$RemoteAssetEntityTableUpdateCompanionBuilder, - (i1.RemoteAssetEntityData, i1.$$RemoteAssetEntityTableReferences), - i1.RemoteAssetEntityData, - i0.PrefetchHooks Function({bool ownerId})>; -i0.Index get uQRemoteAssetOwnerChecksum => i0.Index( - 'UQ_remote_asset_owner_checksum', - 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)'); +typedef $$RemoteAssetEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$RemoteAssetEntityTable, + i1.RemoteAssetEntityData, + i1.$$RemoteAssetEntityTableFilterComposer, + i1.$$RemoteAssetEntityTableOrderingComposer, + i1.$$RemoteAssetEntityTableAnnotationComposer, + $$RemoteAssetEntityTableCreateCompanionBuilder, + $$RemoteAssetEntityTableUpdateCompanionBuilder, + (i1.RemoteAssetEntityData, i1.$$RemoteAssetEntityTableReferences), + i1.RemoteAssetEntityData, + i0.PrefetchHooks Function({bool ownerId}) + >; +i0.Index get idxRemoteAssetOwnerChecksum => i0.Index( + 'idx_remote_asset_owner_checksum', + 'CREATE INDEX idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)', +); class $RemoteAssetEntityTable extends i3.RemoteAssetEntity with i0.TableInfo<$RemoteAssetEntityTable, i1.RemoteAssetEntityData> { @@ -511,138 +637,234 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity final i0.GeneratedDatabase attachedDatabase; final String? _alias; $RemoteAssetEntityTable(this.attachedDatabase, [this._alias]); - static const i0.VerificationMeta _nameMeta = - const i0.VerificationMeta('name'); + static const i0.VerificationMeta _nameMeta = const i0.VerificationMeta( + 'name', + ); @override late final i0.GeneratedColumn name = i0.GeneratedColumn( - 'name', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); + 'name', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); @override late final i0.GeneratedColumnWithTypeConverter type = - i0.GeneratedColumn('type', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true) - .withConverter( - i1.$RemoteAssetEntityTable.$convertertype); - static const i0.VerificationMeta _createdAtMeta = - const i0.VerificationMeta('createdAt'); + i0.GeneratedColumn( + 'type', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ).withConverter(i1.$RemoteAssetEntityTable.$convertertype); + static const i0.VerificationMeta _createdAtMeta = const i0.VerificationMeta( + 'createdAt', + ); @override late final i0.GeneratedColumn createdAt = - i0.GeneratedColumn('created_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i4.currentDateAndTime); - static const i0.VerificationMeta _updatedAtMeta = - const i0.VerificationMeta('updatedAt'); + i0.GeneratedColumn( + 'created_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); + static const i0.VerificationMeta _updatedAtMeta = const i0.VerificationMeta( + 'updatedAt', + ); @override late final i0.GeneratedColumn updatedAt = - i0.GeneratedColumn('updated_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i4.currentDateAndTime); - static const i0.VerificationMeta _widthMeta = - const i0.VerificationMeta('width'); + i0.GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i4.currentDateAndTime, + ); + static const i0.VerificationMeta _widthMeta = const i0.VerificationMeta( + 'width', + ); @override late final i0.GeneratedColumn width = i0.GeneratedColumn( - 'width', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); - static const i0.VerificationMeta _heightMeta = - const i0.VerificationMeta('height'); + 'width', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _heightMeta = const i0.VerificationMeta( + 'height', + ); @override late final i0.GeneratedColumn height = i0.GeneratedColumn( - 'height', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); + 'height', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); static const i0.VerificationMeta _durationInSecondsMeta = const i0.VerificationMeta('durationInSeconds'); @override late final i0.GeneratedColumn durationInSeconds = - i0.GeneratedColumn('duration_in_seconds', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); + i0.GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: i0.DriftSqlType.int, + requiredDuringInsert: false, + ); static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); @override late final i0.GeneratedColumn id = i0.GeneratedColumn( - 'id', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _checksumMeta = - const i0.VerificationMeta('checksum'); + 'id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _checksumMeta = const i0.VerificationMeta( + 'checksum', + ); @override late final i0.GeneratedColumn checksum = i0.GeneratedColumn( - 'checksum', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _isFavoriteMeta = - const i0.VerificationMeta('isFavorite'); + 'checksum', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _isFavoriteMeta = const i0.VerificationMeta( + 'isFavorite', + ); @override late final i0.GeneratedColumn isFavorite = i0.GeneratedColumn( - 'is_favorite', aliasedName, false, - type: i0.DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'CHECK ("is_favorite" IN (0, 1))'), - defaultValue: const i4.Constant(false)); - static const i0.VerificationMeta _ownerIdMeta = - const i0.VerificationMeta('ownerId'); + 'is_favorite', + aliasedName, + false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const i4.Constant(false), + ); + static const i0.VerificationMeta _ownerIdMeta = const i0.VerificationMeta( + 'ownerId', + ); @override late final i0.GeneratedColumn ownerId = i0.GeneratedColumn( - 'owner_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'owner_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); static const i0.VerificationMeta _localDateTimeMeta = const i0.VerificationMeta('localDateTime'); @override late final i0.GeneratedColumn localDateTime = - i0.GeneratedColumn('local_date_time', aliasedName, true, - type: i0.DriftSqlType.dateTime, requiredDuringInsert: false); - static const i0.VerificationMeta _thumbHashMeta = - const i0.VerificationMeta('thumbHash'); + i0.GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _thumbHashMeta = const i0.VerificationMeta( + 'thumbHash', + ); @override late final i0.GeneratedColumn thumbHash = i0.GeneratedColumn( - 'thumb_hash', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _deletedAtMeta = - const i0.VerificationMeta('deletedAt'); + 'thumb_hash', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _deletedAtMeta = const i0.VerificationMeta( + 'deletedAt', + ); @override late final i0.GeneratedColumn deletedAt = - i0.GeneratedColumn('deleted_at', aliasedName, true, - type: i0.DriftSqlType.dateTime, requiredDuringInsert: false); + i0.GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + ); static const i0.VerificationMeta _livePhotoVideoIdMeta = const i0.VerificationMeta('livePhotoVideoId'); @override late final i0.GeneratedColumn livePhotoVideoId = - i0.GeneratedColumn('live_photo_video_id', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); + i0.GeneratedColumn( + 'live_photo_video_id', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); @override late final i0.GeneratedColumnWithTypeConverter - visibility = i0.GeneratedColumn('visibility', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true) - .withConverter( - i1.$RemoteAssetEntityTable.$convertervisibility); - static const i0.VerificationMeta _stackIdMeta = - const i0.VerificationMeta('stackId'); + visibility = + i0.GeneratedColumn( + 'visibility', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ).withConverter( + i1.$RemoteAssetEntityTable.$convertervisibility, + ); + static const i0.VerificationMeta _stackIdMeta = const i0.VerificationMeta( + 'stackId', + ); @override late final i0.GeneratedColumn stackId = i0.GeneratedColumn( - 'stack_id', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); + 'stack_id', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); + static const i0.VerificationMeta _libraryIdMeta = const i0.VerificationMeta( + 'libraryId', + ); + @override + late final i0.GeneratedColumn libraryId = i0.GeneratedColumn( + 'library_id', + aliasedName, + true, + type: i0.DriftSqlType.string, + requiredDuringInsert: false, + ); @override List get $columns => [ - name, - type, - createdAt, - updatedAt, - width, - height, - durationInSeconds, - id, - checksum, - isFavorite, - ownerId, - localDateTime, - thumbHash, - deletedAt, - livePhotoVideoId, - visibility, - stackId - ]; + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + libraryId, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -650,37 +872,51 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity static const String $name = 'remote_asset_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('name')) { context.handle( - _nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta)); + _nameMeta, + name.isAcceptableOrUnknown(data['name']!, _nameMeta), + ); } else if (isInserting) { context.missing(_nameMeta); } if (data.containsKey('created_at')) { - context.handle(_createdAtMeta, - createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); + context.handle( + _createdAtMeta, + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta), + ); } if (data.containsKey('updated_at')) { - context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); + context.handle( + _updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), + ); } if (data.containsKey('width')) { context.handle( - _widthMeta, width.isAcceptableOrUnknown(data['width']!, _widthMeta)); + _widthMeta, + width.isAcceptableOrUnknown(data['width']!, _widthMeta), + ); } if (data.containsKey('height')) { - context.handle(_heightMeta, - height.isAcceptableOrUnknown(data['height']!, _heightMeta)); + context.handle( + _heightMeta, + height.isAcceptableOrUnknown(data['height']!, _heightMeta), + ); } if (data.containsKey('duration_in_seconds')) { context.handle( + _durationInSecondsMeta, + durationInSeconds.isAcceptableOrUnknown( + data['duration_in_seconds']!, _durationInSecondsMeta, - durationInSeconds.isAcceptableOrUnknown( - data['duration_in_seconds']!, _durationInSecondsMeta)); + ), + ); } if (data.containsKey('id')) { context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); @@ -688,46 +924,68 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity context.missing(_idMeta); } if (data.containsKey('checksum')) { - context.handle(_checksumMeta, - checksum.isAcceptableOrUnknown(data['checksum']!, _checksumMeta)); + context.handle( + _checksumMeta, + checksum.isAcceptableOrUnknown(data['checksum']!, _checksumMeta), + ); } else if (isInserting) { context.missing(_checksumMeta); } if (data.containsKey('is_favorite')) { context.handle( - _isFavoriteMeta, - isFavorite.isAcceptableOrUnknown( - data['is_favorite']!, _isFavoriteMeta)); + _isFavoriteMeta, + isFavorite.isAcceptableOrUnknown(data['is_favorite']!, _isFavoriteMeta), + ); } if (data.containsKey('owner_id')) { - context.handle(_ownerIdMeta, - ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta)); + context.handle( + _ownerIdMeta, + ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta), + ); } else if (isInserting) { context.missing(_ownerIdMeta); } if (data.containsKey('local_date_time')) { context.handle( + _localDateTimeMeta, + localDateTime.isAcceptableOrUnknown( + data['local_date_time']!, _localDateTimeMeta, - localDateTime.isAcceptableOrUnknown( - data['local_date_time']!, _localDateTimeMeta)); + ), + ); } if (data.containsKey('thumb_hash')) { - context.handle(_thumbHashMeta, - thumbHash.isAcceptableOrUnknown(data['thumb_hash']!, _thumbHashMeta)); + context.handle( + _thumbHashMeta, + thumbHash.isAcceptableOrUnknown(data['thumb_hash']!, _thumbHashMeta), + ); } if (data.containsKey('deleted_at')) { - context.handle(_deletedAtMeta, - deletedAt.isAcceptableOrUnknown(data['deleted_at']!, _deletedAtMeta)); + context.handle( + _deletedAtMeta, + deletedAt.isAcceptableOrUnknown(data['deleted_at']!, _deletedAtMeta), + ); } if (data.containsKey('live_photo_video_id')) { context.handle( + _livePhotoVideoIdMeta, + livePhotoVideoId.isAcceptableOrUnknown( + data['live_photo_video_id']!, _livePhotoVideoIdMeta, - livePhotoVideoId.isAcceptableOrUnknown( - data['live_photo_video_id']!, _livePhotoVideoIdMeta)); + ), + ); } if (data.containsKey('stack_id')) { - context.handle(_stackIdMeta, - stackId.isAcceptableOrUnknown(data['stack_id']!, _stackIdMeta)); + context.handle( + _stackIdMeta, + stackId.isAcceptableOrUnknown(data['stack_id']!, _stackIdMeta), + ); + } + if (data.containsKey('library_id')) { + context.handle( + _libraryIdMeta, + libraryId.isAcceptableOrUnknown(data['library_id']!, _libraryIdMeta), + ); } return context; } @@ -735,47 +993,88 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity @override Set get $primaryKey => {id}; @override - i1.RemoteAssetEntityData map(Map data, - {String? tablePrefix}) { + i1.RemoteAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.RemoteAssetEntityData( - name: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!, - type: i1.$RemoteAssetEntityTable.$convertertype.fromSql(attachedDatabase - .typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}type'])!), + name: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: i1.$RemoteAssetEntityTable.$convertertype.fromSql( + attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + ), createdAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, updatedAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - width: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}width']), - height: attachedDatabase.typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}height']), + i0.DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}height'], + ), durationInSeconds: attachedDatabase.typeMapping.read( - i0.DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), - id: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!, - checksum: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}checksum'])!, - isFavorite: attachedDatabase.typeMapping - .read(i0.DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - ownerId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + i0.DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}checksum'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + i0.DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + ownerId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, localDateTime: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}local_date_time']), - thumbHash: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}thumb_hash']), - deletedAt: attachedDatabase.typeMapping - .read(i0.DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), + i0.DriftSqlType.dateTime, + data['${effectivePrefix}local_date_time'], + ), + thumbHash: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}thumb_hash'], + ), + deletedAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), livePhotoVideoId: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, - data['${effectivePrefix}live_photo_video_id']), + i0.DriftSqlType.string, + data['${effectivePrefix}live_photo_video_id'], + ), visibility: i1.$RemoteAssetEntityTable.$convertervisibility.fromSql( - attachedDatabase.typeMapping.read( - i0.DriftSqlType.int, data['${effectivePrefix}visibility'])!), - stackId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}stack_id']), + attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}visibility'], + )!, + ), + stackId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}stack_id'], + ), + libraryId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}library_id'], + ), ); } @@ -787,8 +1086,9 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity static i0.JsonTypeConverter2 $convertertype = const i0.EnumIndexConverter(i2.AssetType.values); static i0.JsonTypeConverter2 - $convertervisibility = const i0.EnumIndexConverter( - i2.AssetVisibility.values); + $convertervisibility = const i0.EnumIndexConverter( + i2.AssetVisibility.values, + ); @override bool get withoutRowId => true; @override @@ -814,31 +1114,35 @@ class RemoteAssetEntityData extends i0.DataClass final String? livePhotoVideoId; final i2.AssetVisibility visibility; final String? stackId; - const RemoteAssetEntityData( - {required this.name, - required this.type, - required this.createdAt, - required this.updatedAt, - this.width, - this.height, - this.durationInSeconds, - required this.id, - required this.checksum, - required this.isFavorite, - required this.ownerId, - this.localDateTime, - this.thumbHash, - this.deletedAt, - this.livePhotoVideoId, - required this.visibility, - this.stackId}); + final String? libraryId; + const RemoteAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + required this.checksum, + required this.isFavorite, + required this.ownerId, + this.localDateTime, + this.thumbHash, + this.deletedAt, + this.livePhotoVideoId, + required this.visibility, + this.stackId, + this.libraryId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; map['name'] = i0.Variable(name); { map['type'] = i0.Variable( - i1.$RemoteAssetEntityTable.$convertertype.toSql(type)); + i1.$RemoteAssetEntityTable.$convertertype.toSql(type), + ); } map['created_at'] = i0.Variable(createdAt); map['updated_at'] = i0.Variable(updatedAt); @@ -869,21 +1173,28 @@ class RemoteAssetEntityData extends i0.DataClass } { map['visibility'] = i0.Variable( - i1.$RemoteAssetEntityTable.$convertervisibility.toSql(visibility)); + i1.$RemoteAssetEntityTable.$convertervisibility.toSql(visibility), + ); } if (!nullToAbsent || stackId != null) { map['stack_id'] = i0.Variable(stackId); } + if (!nullToAbsent || libraryId != null) { + map['library_id'] = i0.Variable(libraryId); + } return map; } - factory RemoteAssetEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory RemoteAssetEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return RemoteAssetEntityData( name: serializer.fromJson(json['name']), - type: i1.$RemoteAssetEntityTable.$convertertype - .fromJson(serializer.fromJson(json['type'])), + type: i1.$RemoteAssetEntityTable.$convertertype.fromJson( + serializer.fromJson(json['type']), + ), createdAt: serializer.fromJson(json['createdAt']), updatedAt: serializer.fromJson(json['updatedAt']), width: serializer.fromJson(json['width']), @@ -897,9 +1208,11 @@ class RemoteAssetEntityData extends i0.DataClass thumbHash: serializer.fromJson(json['thumbHash']), deletedAt: serializer.fromJson(json['deletedAt']), livePhotoVideoId: serializer.fromJson(json['livePhotoVideoId']), - visibility: i1.$RemoteAssetEntityTable.$convertervisibility - .fromJson(serializer.fromJson(json['visibility'])), + visibility: i1.$RemoteAssetEntityTable.$convertervisibility.fromJson( + serializer.fromJson(json['visibility']), + ), stackId: serializer.fromJson(json['stackId']), + libraryId: serializer.fromJson(json['libraryId']), ); } @override @@ -907,8 +1220,9 @@ class RemoteAssetEntityData extends i0.DataClass serializer ??= i0.driftRuntimeOptions.defaultSerializer; return { 'name': serializer.toJson(name), - 'type': serializer - .toJson(i1.$RemoteAssetEntityTable.$convertertype.toJson(type)), + 'type': serializer.toJson( + i1.$RemoteAssetEntityTable.$convertertype.toJson(type), + ), 'createdAt': serializer.toJson(createdAt), 'updatedAt': serializer.toJson(updatedAt), 'width': serializer.toJson(width), @@ -923,53 +1237,58 @@ class RemoteAssetEntityData extends i0.DataClass 'deletedAt': serializer.toJson(deletedAt), 'livePhotoVideoId': serializer.toJson(livePhotoVideoId), 'visibility': serializer.toJson( - i1.$RemoteAssetEntityTable.$convertervisibility.toJson(visibility)), + i1.$RemoteAssetEntityTable.$convertervisibility.toJson(visibility), + ), 'stackId': serializer.toJson(stackId), + 'libraryId': serializer.toJson(libraryId), }; } - i1.RemoteAssetEntityData copyWith( - {String? name, - i2.AssetType? type, - DateTime? createdAt, - DateTime? updatedAt, - i0.Value width = const i0.Value.absent(), - i0.Value height = const i0.Value.absent(), - i0.Value durationInSeconds = const i0.Value.absent(), - String? id, - String? checksum, - bool? isFavorite, - String? ownerId, - i0.Value localDateTime = const i0.Value.absent(), - i0.Value thumbHash = const i0.Value.absent(), - i0.Value deletedAt = const i0.Value.absent(), - i0.Value livePhotoVideoId = const i0.Value.absent(), - i2.AssetVisibility? visibility, - i0.Value stackId = const i0.Value.absent()}) => - i1.RemoteAssetEntityData( - name: name ?? this.name, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - width: width.present ? width.value : this.width, - height: height.present ? height.value : this.height, - durationInSeconds: durationInSeconds.present - ? durationInSeconds.value - : this.durationInSeconds, - id: id ?? this.id, - checksum: checksum ?? this.checksum, - isFavorite: isFavorite ?? this.isFavorite, - ownerId: ownerId ?? this.ownerId, - localDateTime: - localDateTime.present ? localDateTime.value : this.localDateTime, - thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, - deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, - livePhotoVideoId: livePhotoVideoId.present - ? livePhotoVideoId.value - : this.livePhotoVideoId, - visibility: visibility ?? this.visibility, - stackId: stackId.present ? stackId.value : this.stackId, - ); + i1.RemoteAssetEntityData copyWith({ + String? name, + i2.AssetType? type, + DateTime? createdAt, + DateTime? updatedAt, + i0.Value width = const i0.Value.absent(), + i0.Value height = const i0.Value.absent(), + i0.Value durationInSeconds = const i0.Value.absent(), + String? id, + String? checksum, + bool? isFavorite, + String? ownerId, + i0.Value localDateTime = const i0.Value.absent(), + i0.Value thumbHash = const i0.Value.absent(), + i0.Value deletedAt = const i0.Value.absent(), + i0.Value livePhotoVideoId = const i0.Value.absent(), + i2.AssetVisibility? visibility, + i0.Value stackId = const i0.Value.absent(), + i0.Value libraryId = const i0.Value.absent(), + }) => i1.RemoteAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime.present + ? localDateTime.value + : this.localDateTime, + thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + livePhotoVideoId: livePhotoVideoId.present + ? livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId.present ? stackId.value : this.stackId, + libraryId: libraryId.present ? libraryId.value : this.libraryId, + ); RemoteAssetEntityData copyWithCompanion(i1.RemoteAssetEntityCompanion data) { return RemoteAssetEntityData( name: data.name.present ? data.name.value : this.name, @@ -983,8 +1302,9 @@ class RemoteAssetEntityData extends i0.DataClass : this.durationInSeconds, id: data.id.present ? data.id.value : this.id, checksum: data.checksum.present ? data.checksum.value : this.checksum, - isFavorite: - data.isFavorite.present ? data.isFavorite.value : this.isFavorite, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, localDateTime: data.localDateTime.present ? data.localDateTime.value @@ -994,9 +1314,11 @@ class RemoteAssetEntityData extends i0.DataClass livePhotoVideoId: data.livePhotoVideoId.present ? data.livePhotoVideoId.value : this.livePhotoVideoId, - visibility: - data.visibility.present ? data.visibility.value : this.visibility, + visibility: data.visibility.present + ? data.visibility.value + : this.visibility, stackId: data.stackId.present ? data.stackId.value : this.stackId, + libraryId: data.libraryId.present ? data.libraryId.value : this.libraryId, ); } @@ -1019,30 +1341,33 @@ class RemoteAssetEntityData extends i0.DataClass ..write('deletedAt: $deletedAt, ') ..write('livePhotoVideoId: $livePhotoVideoId, ') ..write('visibility: $visibility, ') - ..write('stackId: $stackId') + ..write('stackId: $stackId, ') + ..write('libraryId: $libraryId') ..write(')')) .toString(); } @override int get hashCode => Object.hash( - name, - type, - createdAt, - updatedAt, - width, - height, - durationInSeconds, - id, - checksum, - isFavorite, - ownerId, - localDateTime, - thumbHash, - deletedAt, - livePhotoVideoId, - visibility, - stackId); + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + libraryId, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -1063,7 +1388,8 @@ class RemoteAssetEntityData extends i0.DataClass other.deletedAt == this.deletedAt && other.livePhotoVideoId == this.livePhotoVideoId && other.visibility == this.visibility && - other.stackId == this.stackId); + other.stackId == this.stackId && + other.libraryId == this.libraryId); } class RemoteAssetEntityCompanion @@ -1085,6 +1411,7 @@ class RemoteAssetEntityCompanion final i0.Value livePhotoVideoId; final i0.Value visibility; final i0.Value stackId; + final i0.Value libraryId; const RemoteAssetEntityCompanion({ this.name = const i0.Value.absent(), this.type = const i0.Value.absent(), @@ -1103,6 +1430,7 @@ class RemoteAssetEntityCompanion this.livePhotoVideoId = const i0.Value.absent(), this.visibility = const i0.Value.absent(), this.stackId = const i0.Value.absent(), + this.libraryId = const i0.Value.absent(), }); RemoteAssetEntityCompanion.insert({ required String name, @@ -1122,12 +1450,13 @@ class RemoteAssetEntityCompanion this.livePhotoVideoId = const i0.Value.absent(), required i2.AssetVisibility visibility, this.stackId = const i0.Value.absent(), - }) : name = i0.Value(name), - type = i0.Value(type), - id = i0.Value(id), - checksum = i0.Value(checksum), - ownerId = i0.Value(ownerId), - visibility = i0.Value(visibility); + this.libraryId = const i0.Value.absent(), + }) : name = i0.Value(name), + type = i0.Value(type), + id = i0.Value(id), + checksum = i0.Value(checksum), + ownerId = i0.Value(ownerId), + visibility = i0.Value(visibility); static i0.Insertable custom({ i0.Expression? name, i0.Expression? type, @@ -1146,6 +1475,7 @@ class RemoteAssetEntityCompanion i0.Expression? livePhotoVideoId, i0.Expression? visibility, i0.Expression? stackId, + i0.Expression? libraryId, }) { return i0.RawValuesInsertable({ if (name != null) 'name': name, @@ -1165,27 +1495,30 @@ class RemoteAssetEntityCompanion if (livePhotoVideoId != null) 'live_photo_video_id': livePhotoVideoId, if (visibility != null) 'visibility': visibility, if (stackId != null) 'stack_id': stackId, + if (libraryId != null) 'library_id': libraryId, }); } - i1.RemoteAssetEntityCompanion copyWith( - {i0.Value? name, - i0.Value? type, - i0.Value? createdAt, - i0.Value? updatedAt, - i0.Value? width, - i0.Value? height, - i0.Value? durationInSeconds, - i0.Value? id, - i0.Value? checksum, - i0.Value? isFavorite, - i0.Value? ownerId, - i0.Value? localDateTime, - i0.Value? thumbHash, - i0.Value? deletedAt, - i0.Value? livePhotoVideoId, - i0.Value? visibility, - i0.Value? stackId}) { + i1.RemoteAssetEntityCompanion copyWith({ + i0.Value? name, + i0.Value? type, + i0.Value? createdAt, + i0.Value? updatedAt, + i0.Value? width, + i0.Value? height, + i0.Value? durationInSeconds, + i0.Value? id, + i0.Value? checksum, + i0.Value? isFavorite, + i0.Value? ownerId, + i0.Value? localDateTime, + i0.Value? thumbHash, + i0.Value? deletedAt, + i0.Value? livePhotoVideoId, + i0.Value? visibility, + i0.Value? stackId, + i0.Value? libraryId, + }) { return i1.RemoteAssetEntityCompanion( name: name ?? this.name, type: type ?? this.type, @@ -1204,6 +1537,7 @@ class RemoteAssetEntityCompanion livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, visibility: visibility ?? this.visibility, stackId: stackId ?? this.stackId, + libraryId: libraryId ?? this.libraryId, ); } @@ -1215,7 +1549,8 @@ class RemoteAssetEntityCompanion } if (type.present) { map['type'] = i0.Variable( - i1.$RemoteAssetEntityTable.$convertertype.toSql(type.value)); + i1.$RemoteAssetEntityTable.$convertertype.toSql(type.value), + ); } if (createdAt.present) { map['created_at'] = i0.Variable(createdAt.value); @@ -1257,13 +1592,16 @@ class RemoteAssetEntityCompanion map['live_photo_video_id'] = i0.Variable(livePhotoVideoId.value); } if (visibility.present) { - map['visibility'] = i0.Variable(i1 - .$RemoteAssetEntityTable.$convertervisibility - .toSql(visibility.value)); + map['visibility'] = i0.Variable( + i1.$RemoteAssetEntityTable.$convertervisibility.toSql(visibility.value), + ); } if (stackId.present) { map['stack_id'] = i0.Variable(stackId.value); } + if (libraryId.present) { + map['library_id'] = i0.Variable(libraryId.value); + } return map; } @@ -1286,11 +1624,22 @@ class RemoteAssetEntityCompanion ..write('deletedAt: $deletedAt, ') ..write('livePhotoVideoId: $livePhotoVideoId, ') ..write('visibility: $visibility, ') - ..write('stackId: $stackId') + ..write('stackId: $stackId, ') + ..write('libraryId: $libraryId') ..write(')')) .toString(); } } -i0.Index get idxRemoteAssetChecksum => i0.Index('idx_remote_asset_checksum', - 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)'); +i0.Index get uQRemoteAssetsOwnerChecksum => i0.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)', +); +i0.Index get uQRemoteAssetsOwnerLibraryChecksum => i0.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)', +); +i0.Index get idxRemoteAssetChecksum => i0.Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', +); diff --git a/mobile/lib/infrastructure/entities/stack.entity.dart b/mobile/lib/infrastructure/entities/stack.entity.dart index 92375f19db..be50d7e330 100644 --- a/mobile/lib/infrastructure/entities/stack.entity.dart +++ b/mobile/lib/infrastructure/entities/stack.entity.dart @@ -1,5 +1,4 @@ import 'package:drift/drift.dart'; -import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; @@ -12,10 +11,9 @@ class StackEntity extends Table with DriftDefaultsMixin { DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)(); - TextColumn get ownerId => - text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); + TextColumn get ownerId => text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); - TextColumn get primaryAssetId => text().references(RemoteAssetEntity, #id)(); + TextColumn get primaryAssetId => text()(); @override Set get primaryKey => {id}; diff --git a/mobile/lib/infrastructure/entities/stack.entity.drift.dart b/mobile/lib/infrastructure/entities/stack.entity.drift.dart index c0d000e02a..ff7a3c3444 100644 --- a/mobile/lib/infrastructure/entities/stack.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/stack.entity.drift.dart @@ -8,81 +8,63 @@ import 'package:drift/src/runtime/query_builder/query_builder.dart' as i3; import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' as i4; import 'package:drift/internal/modular.dart' as i5; -import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart' - as i6; -typedef $$StackEntityTableCreateCompanionBuilder = i1.StackEntityCompanion - Function({ - required String id, - i0.Value createdAt, - i0.Value updatedAt, - required String ownerId, - required String primaryAssetId, -}); -typedef $$StackEntityTableUpdateCompanionBuilder = i1.StackEntityCompanion - Function({ - i0.Value id, - i0.Value createdAt, - i0.Value updatedAt, - i0.Value ownerId, - i0.Value primaryAssetId, -}); +typedef $$StackEntityTableCreateCompanionBuilder = + i1.StackEntityCompanion Function({ + required String id, + i0.Value createdAt, + i0.Value updatedAt, + required String ownerId, + required String primaryAssetId, + }); +typedef $$StackEntityTableUpdateCompanionBuilder = + i1.StackEntityCompanion Function({ + i0.Value id, + i0.Value createdAt, + i0.Value updatedAt, + i0.Value ownerId, + i0.Value primaryAssetId, + }); -final class $$StackEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, i1.$StackEntityTable, i1.StackEntityData> { +final class $$StackEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$StackEntityTable, + i1.StackEntityData + > { $$StackEntityTableReferences(super.$_db, super.$_table, super.$_typedResult); static i4.$UserEntityTable _ownerIdTable(i0.GeneratedDatabase db) => i5.ReadDatabaseContainer(db) .resultSet('user_entity') - .createAlias(i0.$_aliasNameGenerator( - i5.ReadDatabaseContainer(db) - .resultSet('stack_entity') - .ownerId, - i5.ReadDatabaseContainer(db) - .resultSet('user_entity') - .id)); + .createAlias( + i0.$_aliasNameGenerator( + i5.ReadDatabaseContainer( + db, + ).resultSet('stack_entity').ownerId, + i5.ReadDatabaseContainer( + db, + ).resultSet('user_entity').id, + ), + ); i4.$$UserEntityTableProcessedTableManager get ownerId { final $_column = $_itemColumn('owner_id')!; final manager = i4 .$$UserEntityTableTableManager( + $_db, + i5.ReadDatabaseContainer( $_db, - i5.ReadDatabaseContainer($_db) - .resultSet('user_entity')) + ).resultSet('user_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_ownerIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); - } - - static i6.$RemoteAssetEntityTable _primaryAssetIdTable( - i0.GeneratedDatabase db) => - i5.ReadDatabaseContainer(db) - .resultSet('remote_asset_entity') - .createAlias(i0.$_aliasNameGenerator( - i5.ReadDatabaseContainer(db) - .resultSet('stack_entity') - .primaryAssetId, - i5.ReadDatabaseContainer(db) - .resultSet('remote_asset_entity') - .id)); - - i6.$$RemoteAssetEntityTableProcessedTableManager get primaryAssetId { - final $_column = $_itemColumn('primary_asset_id')!; - - final manager = i6 - .$$RemoteAssetEntityTableTableManager( - $_db, - i5.ReadDatabaseContainer($_db) - .resultSet('remote_asset_entity')) - .filter((f) => f.id.sqlEquals($_column)); - final item = $_typedResult.readTableOrNull(_primaryAssetIdTable($_db)); - if (item == null) return manager; - return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -96,55 +78,49 @@ class $$StackEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnFilters(column)); + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get createdAt => $composableBuilder( - column: $table.createdAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get primaryAssetId => $composableBuilder( + column: $table.primaryAssetId, + builder: (column) => i0.ColumnFilters(column), + ); i4.$$UserEntityTableFilterComposer get ownerId { final i4.$$UserEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableFilterComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return composer; - } - - i6.$$RemoteAssetEntityTableFilterComposer get primaryAssetId { - final i6.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.primaryAssetId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i6.$$RemoteAssetEntityTableFilterComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableFilterComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -159,59 +135,49 @@ class $$StackEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get createdAt => $composableBuilder( - column: $table.createdAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.createdAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get updatedAt => $composableBuilder( - column: $table.updatedAt, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get primaryAssetId => $composableBuilder( + column: $table.primaryAssetId, + builder: (column) => i0.ColumnOrderings(column), + ); i4.$$UserEntityTableOrderingComposer get ownerId { final i4.$$UserEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableOrderingComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return composer; - } - - i6.$$RemoteAssetEntityTableOrderingComposer get primaryAssetId { - final i6.$$RemoteAssetEntityTableOrderingComposer composer = - $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.primaryAssetId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i6.$$RemoteAssetEntityTableOrderingComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -234,68 +200,59 @@ class $$StackEntityTableAnnotationComposer i0.GeneratedColumn get updatedAt => $composableBuilder(column: $table.updatedAt, builder: (column) => column); + i0.GeneratedColumn get primaryAssetId => $composableBuilder( + column: $table.primaryAssetId, + builder: (column) => column, + ); + i4.$$UserEntityTableAnnotationComposer get ownerId { final i4.$$UserEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.ownerId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i4.$$UserEntityTableAnnotationComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); - return composer; - } - - i6.$$RemoteAssetEntityTableAnnotationComposer get primaryAssetId { - final i6.$$RemoteAssetEntityTableAnnotationComposer composer = - $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.primaryAssetId, - referencedTable: i5.ReadDatabaseContainer($db) - .resultSet('remote_asset_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i6.$$RemoteAssetEntityTableAnnotationComposer( - $db: $db, - $table: i5.ReadDatabaseContainer($db) - .resultSet( - 'remote_asset_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.ownerId, + referencedTable: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i4.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i5.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$StackEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$StackEntityTable, - i1.StackEntityData, - i1.$$StackEntityTableFilterComposer, - i1.$$StackEntityTableOrderingComposer, - i1.$$StackEntityTableAnnotationComposer, - $$StackEntityTableCreateCompanionBuilder, - $$StackEntityTableUpdateCompanionBuilder, - (i1.StackEntityData, i1.$$StackEntityTableReferences), - i1.StackEntityData, - i0.PrefetchHooks Function({bool ownerId, bool primaryAssetId})> { +class $$StackEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$StackEntityTable, + i1.StackEntityData, + i1.$$StackEntityTableFilterComposer, + i1.$$StackEntityTableOrderingComposer, + i1.$$StackEntityTableAnnotationComposer, + $$StackEntityTableCreateCompanionBuilder, + $$StackEntityTableUpdateCompanionBuilder, + (i1.StackEntityData, i1.$$StackEntityTableReferences), + i1.StackEntityData, + i0.PrefetchHooks Function({bool ownerId}) + > { $$StackEntityTableTableManager( - i0.GeneratedDatabase db, i1.$StackEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$StackEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -304,46 +261,49 @@ class $$StackEntityTableTableManager extends i0.RootTableManager< i1.$$StackEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$StackEntityTableAnnotationComposer($db: db, $table: table), - updateCompanionCallback: ({ - i0.Value id = const i0.Value.absent(), - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value ownerId = const i0.Value.absent(), - i0.Value primaryAssetId = const i0.Value.absent(), - }) => - i1.StackEntityCompanion( - id: id, - createdAt: createdAt, - updatedAt: updatedAt, - ownerId: ownerId, - primaryAssetId: primaryAssetId, - ), - createCompanionCallback: ({ - required String id, - i0.Value createdAt = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - required String ownerId, - required String primaryAssetId, - }) => - i1.StackEntityCompanion.insert( - id: id, - createdAt: createdAt, - updatedAt: updatedAt, - ownerId: ownerId, - primaryAssetId: primaryAssetId, - ), + updateCompanionCallback: + ({ + i0.Value id = const i0.Value.absent(), + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + i0.Value ownerId = const i0.Value.absent(), + i0.Value primaryAssetId = const i0.Value.absent(), + }) => i1.StackEntityCompanion( + id: id, + createdAt: createdAt, + updatedAt: updatedAt, + ownerId: ownerId, + primaryAssetId: primaryAssetId, + ), + createCompanionCallback: + ({ + required String id, + i0.Value createdAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + required String ownerId, + required String primaryAssetId, + }) => i1.StackEntityCompanion.insert( + id: id, + createdAt: createdAt, + updatedAt: updatedAt, + ownerId: ownerId, + primaryAssetId: primaryAssetId, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$StackEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$StackEntityTableReferences(db, table, e), + ), + ) .toList(), - prefetchHooksCallback: ({ownerId = false, primaryAssetId = false}) { + prefetchHooksCallback: ({ownerId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -354,51 +314,49 @@ class $$StackEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (ownerId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.ownerId, - referencedTable: - i1.$$StackEntityTableReferences._ownerIdTable(db), - referencedColumn: - i1.$$StackEntityTableReferences._ownerIdTable(db).id, - ) as T; - } - if (primaryAssetId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.primaryAssetId, - referencedTable: i1.$$StackEntityTableReferences - ._primaryAssetIdTable(db), - referencedColumn: i1.$$StackEntityTableReferences - ._primaryAssetIdTable(db) - .id, - ) as T; - } + dynamic + > + >(state) { + if (ownerId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.ownerId, + referencedTable: i1.$$StackEntityTableReferences + ._ownerIdTable(db), + referencedColumn: i1 + .$$StackEntityTableReferences + ._ownerIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$StackEntityTableProcessedTableManager = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$StackEntityTable, - i1.StackEntityData, - i1.$$StackEntityTableFilterComposer, - i1.$$StackEntityTableOrderingComposer, - i1.$$StackEntityTableAnnotationComposer, - $$StackEntityTableCreateCompanionBuilder, - $$StackEntityTableUpdateCompanionBuilder, - (i1.StackEntityData, i1.$$StackEntityTableReferences), - i1.StackEntityData, - i0.PrefetchHooks Function({bool ownerId, bool primaryAssetId})>; +typedef $$StackEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$StackEntityTable, + i1.StackEntityData, + i1.$$StackEntityTableFilterComposer, + i1.$$StackEntityTableOrderingComposer, + i1.$$StackEntityTableAnnotationComposer, + $$StackEntityTableCreateCompanionBuilder, + $$StackEntityTableUpdateCompanionBuilder, + (i1.StackEntityData, i1.$$StackEntityTableReferences), + i1.StackEntityData, + i0.PrefetchHooks Function({bool ownerId}) + >; class $StackEntityTable extends i2.StackEntity with i0.TableInfo<$StackEntityTable, i1.StackEntityData> { @@ -409,46 +367,71 @@ class $StackEntityTable extends i2.StackEntity static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); @override late final i0.GeneratedColumn id = i0.GeneratedColumn( - 'id', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _createdAtMeta = - const i0.VerificationMeta('createdAt'); + 'id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _createdAtMeta = const i0.VerificationMeta( + 'createdAt', + ); @override late final i0.GeneratedColumn createdAt = - i0.GeneratedColumn('created_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i3.currentDateAndTime); - static const i0.VerificationMeta _updatedAtMeta = - const i0.VerificationMeta('updatedAt'); + i0.GeneratedColumn( + 'created_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i3.currentDateAndTime, + ); + static const i0.VerificationMeta _updatedAtMeta = const i0.VerificationMeta( + 'updatedAt', + ); @override late final i0.GeneratedColumn updatedAt = - i0.GeneratedColumn('updated_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i3.currentDateAndTime); - static const i0.VerificationMeta _ownerIdMeta = - const i0.VerificationMeta('ownerId'); + i0.GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i3.currentDateAndTime, + ); + static const i0.VerificationMeta _ownerIdMeta = const i0.VerificationMeta( + 'ownerId', + ); @override late final i0.GeneratedColumn ownerId = i0.GeneratedColumn( - 'owner_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'owner_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); static const i0.VerificationMeta _primaryAssetIdMeta = const i0.VerificationMeta('primaryAssetId'); @override late final i0.GeneratedColumn primaryAssetId = i0.GeneratedColumn( - 'primary_asset_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id)')); + 'primary_asset_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); @override - List get $columns => - [id, createdAt, updatedAt, ownerId, primaryAssetId]; + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + primaryAssetId, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -456,8 +439,9 @@ class $StackEntityTable extends i2.StackEntity static const String $name = 'stack_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('id')) { @@ -466,24 +450,33 @@ class $StackEntityTable extends i2.StackEntity context.missing(_idMeta); } if (data.containsKey('created_at')) { - context.handle(_createdAtMeta, - createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); + context.handle( + _createdAtMeta, + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta), + ); } if (data.containsKey('updated_at')) { - context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); + context.handle( + _updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), + ); } if (data.containsKey('owner_id')) { - context.handle(_ownerIdMeta, - ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta)); + context.handle( + _ownerIdMeta, + ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta), + ); } else if (isInserting) { context.missing(_ownerIdMeta); } if (data.containsKey('primary_asset_id')) { context.handle( + _primaryAssetIdMeta, + primaryAssetId.isAcceptableOrUnknown( + data['primary_asset_id']!, _primaryAssetIdMeta, - primaryAssetId.isAcceptableOrUnknown( - data['primary_asset_id']!, _primaryAssetIdMeta)); + ), + ); } else if (isInserting) { context.missing(_primaryAssetIdMeta); } @@ -496,16 +489,26 @@ class $StackEntityTable extends i2.StackEntity i1.StackEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.StackEntityData( - id: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!, + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}id'], + )!, createdAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, updatedAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, primaryAssetId: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, data['${effectivePrefix}primary_asset_id'])!, + i0.DriftSqlType.string, + data['${effectivePrefix}primary_asset_id'], + )!, ); } @@ -527,12 +530,13 @@ class StackEntityData extends i0.DataClass final DateTime updatedAt; final String ownerId; final String primaryAssetId; - const StackEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - required this.primaryAssetId}); + const StackEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -544,8 +548,10 @@ class StackEntityData extends i0.DataClass return map; } - factory StackEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory StackEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return StackEntityData( id: serializer.fromJson(json['id']), @@ -567,19 +573,19 @@ class StackEntityData extends i0.DataClass }; } - i1.StackEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - String? ownerId, - String? primaryAssetId}) => - i1.StackEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - primaryAssetId: primaryAssetId ?? this.primaryAssetId, - ); + i1.StackEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId, + }) => i1.StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); StackEntityData copyWithCompanion(i1.StackEntityCompanion data) { return StackEntityData( id: data.id.present ? data.id.value : this.id, @@ -637,9 +643,9 @@ class StackEntityCompanion extends i0.UpdateCompanion { this.updatedAt = const i0.Value.absent(), required String ownerId, required String primaryAssetId, - }) : id = i0.Value(id), - ownerId = i0.Value(ownerId), - primaryAssetId = i0.Value(primaryAssetId); + }) : id = i0.Value(id), + ownerId = i0.Value(ownerId), + primaryAssetId = i0.Value(primaryAssetId); static i0.Insertable custom({ i0.Expression? id, i0.Expression? createdAt, @@ -656,12 +662,13 @@ class StackEntityCompanion extends i0.UpdateCompanion { }); } - i1.StackEntityCompanion copyWith( - {i0.Value? id, - i0.Value? createdAt, - i0.Value? updatedAt, - i0.Value? ownerId, - i0.Value? primaryAssetId}) { + i1.StackEntityCompanion copyWith({ + i0.Value? id, + i0.Value? createdAt, + i0.Value? updatedAt, + i0.Value? ownerId, + i0.Value? primaryAssetId, + }) { return i1.StackEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, diff --git a/mobile/lib/infrastructure/entities/store.entity.g.dart b/mobile/lib/infrastructure/entities/store.entity.g.dart index b97b5b0a28..7da92cf778 100644 --- a/mobile/lib/infrastructure/entities/store.entity.g.dart +++ b/mobile/lib/infrastructure/entities/store.entity.g.dart @@ -17,17 +17,14 @@ const StoreValueSchema = CollectionSchema( name: r'StoreValue', id: 902899285492123510, properties: { - r'intValue': PropertySchema( - id: 0, - name: r'intValue', - type: IsarType.long, - ), + r'intValue': PropertySchema(id: 0, name: r'intValue', type: IsarType.long), r'strValue': PropertySchema( id: 1, name: r'strValue', type: IsarType.string, - ) + ), }, + estimateSize: _storeValueEstimateSize, serialize: _storeValueSerialize, deserialize: _storeValueDeserialize, @@ -36,6 +33,7 @@ const StoreValueSchema = CollectionSchema( indexes: {}, links: {}, embeddedSchemas: {}, + getId: _storeValueGetId, getLinks: _storeValueGetLinks, attach: _storeValueAttach, @@ -120,10 +118,7 @@ extension StoreValueQueryWhere on QueryBuilder { QueryBuilder idEqualTo(Id id) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: id, - upper: id, - )); + return query.addWhereClause(IdWhereClause.between(lower: id, upper: id)); }); } @@ -149,8 +144,10 @@ extension StoreValueQueryWhere }); } - QueryBuilder idGreaterThan(Id id, - {bool include = false}) { + QueryBuilder idGreaterThan( + Id id, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: id, includeLower: include), @@ -158,8 +155,10 @@ extension StoreValueQueryWhere }); } - QueryBuilder idLessThan(Id id, - {bool include = false}) { + QueryBuilder idLessThan( + Id id, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: id, includeUpper: include), @@ -174,12 +173,14 @@ extension StoreValueQueryWhere bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerId, - includeLower: includeLower, - upper: upperId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + ), + ); }); } } @@ -187,12 +188,12 @@ extension StoreValueQueryWhere extension StoreValueQueryFilter on QueryBuilder { QueryBuilder idEqualTo( - Id value) { + Id value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: value), + ); }); } @@ -201,11 +202,13 @@ extension StoreValueQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + ), + ); }); } @@ -214,11 +217,13 @@ extension StoreValueQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + ), + ); }); } @@ -229,54 +234,55 @@ extension StoreValueQueryFilter bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder intValueIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'intValue', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'intValue'), + ); }); } QueryBuilder - intValueIsNotNull() { + intValueIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'intValue', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'intValue'), + ); }); } QueryBuilder intValueEqualTo( - int? value) { + int? value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'intValue', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'intValue', value: value), + ); }); } QueryBuilder - intValueGreaterThan( - int? value, { - bool include = false, - }) { + intValueGreaterThan(int? value, {bool include = false}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'intValue', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'intValue', + value: value, + ), + ); }); } @@ -285,11 +291,13 @@ extension StoreValueQueryFilter bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'intValue', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'intValue', + value: value, + ), + ); }); } @@ -300,30 +308,32 @@ extension StoreValueQueryFilter bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'intValue', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'intValue', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder strValueIsNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNull( - property: r'strValue', - )); + return query.addFilterCondition( + const FilterCondition.isNull(property: r'strValue'), + ); }); } QueryBuilder - strValueIsNotNull() { + strValueIsNotNull() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(const FilterCondition.isNotNull( - property: r'strValue', - )); + return query.addFilterCondition( + const FilterCondition.isNotNull(property: r'strValue'), + ); }); } @@ -332,27 +342,31 @@ extension StoreValueQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'strValue', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'strValue', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - strValueGreaterThan( + strValueGreaterThan( String? value, { bool include = false, bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'strValue', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'strValue', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -362,12 +376,14 @@ extension StoreValueQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'strValue', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'strValue', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -379,28 +395,29 @@ extension StoreValueQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'strValue', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'strValue', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - strValueStartsWith( - String value, { - bool caseSensitive = true, - }) { + strValueStartsWith(String value, {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'strValue', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'strValue', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -409,55 +426,61 @@ extension StoreValueQueryFilter bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'strValue', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'strValue', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder strValueContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'strValue', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'strValue', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder strValueMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'strValue', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'strValue', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder - strValueIsEmpty() { + strValueIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'strValue', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'strValue', value: ''), + ); }); } QueryBuilder - strValueIsNotEmpty() { + strValueIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'strValue', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'strValue', value: ''), + ); }); } } @@ -542,8 +565,9 @@ extension StoreValueQueryWhereDistinct }); } - QueryBuilder distinctByStrValue( - {bool caseSensitive = true}) { + QueryBuilder distinctByStrValue({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'strValue', caseSensitive: caseSensitive); }); diff --git a/mobile/lib/infrastructure/entities/user.entity.dart b/mobile/lib/infrastructure/entities/user.entity.dart index b0c1e6e866..78fc76b45d 100644 --- a/mobile/lib/infrastructure/entities/user.entity.dart +++ b/mobile/lib/infrastructure/entities/user.entity.dart @@ -43,36 +43,35 @@ class User { }); static User fromDto(UserDto dto) => User( - id: dto.id, - updatedAt: dto.updatedAt, - email: dto.email, - name: dto.name, - isAdmin: dto.isAdmin, - isPartnerSharedBy: dto.isPartnerSharedBy, - isPartnerSharedWith: dto.isPartnerSharedWith, - profileImagePath: dto.profileImagePath ?? "", - avatarColor: dto.avatarColor, - memoryEnabled: dto.memoryEnabled, - inTimeline: dto.inTimeline, - quotaUsageInBytes: dto.quotaUsageInBytes, - quotaSizeInBytes: dto.quotaSizeInBytes, - ); + id: dto.id, + updatedAt: dto.updatedAt, + email: dto.email, + name: dto.name, + isAdmin: dto.isAdmin, + isPartnerSharedBy: dto.isPartnerSharedBy, + isPartnerSharedWith: dto.isPartnerSharedWith, + profileImagePath: dto.hasProfileImage ? "HAS_PROFILE_IMAGE" : "", + avatarColor: dto.avatarColor, + memoryEnabled: dto.memoryEnabled, + inTimeline: dto.inTimeline, + ); UserDto toDto() => UserDto( - id: id, - email: email, - name: name, - isAdmin: isAdmin, - updatedAt: updatedAt, - profileImagePath: profileImagePath.isEmpty ? null : profileImagePath, - avatarColor: avatarColor, - memoryEnabled: memoryEnabled, - inTimeline: inTimeline, - isPartnerSharedBy: isPartnerSharedBy, - isPartnerSharedWith: isPartnerSharedWith, - quotaUsageInBytes: quotaUsageInBytes, - quotaSizeInBytes: quotaSizeInBytes, - ); + id: id, + email: email, + name: name, + isAdmin: isAdmin, + updatedAt: updatedAt, + avatarColor: avatarColor, + memoryEnabled: memoryEnabled, + inTimeline: inTimeline, + isPartnerSharedBy: isPartnerSharedBy, + isPartnerSharedWith: isPartnerSharedWith, + hasProfileImage: profileImagePath.isNotEmpty, + profileChangedAt: updatedAt, + quotaUsageInBytes: quotaUsageInBytes, + quotaSizeInBytes: quotaSizeInBytes, + ); } class UserEntity extends Table with DriftDefaultsMixin { @@ -82,11 +81,11 @@ class UserEntity extends Table with DriftDefaultsMixin { TextColumn get name => text()(); BoolColumn get isAdmin => boolean().withDefault(const Constant(false))(); TextColumn get email => text()(); - TextColumn get profileImagePath => text().nullable()(); + + BoolColumn get hasProfileImage => boolean().withDefault(const Constant(false))(); + DateTimeColumn get profileChangedAt => dateTime().withDefault(currentDateAndTime)(); + DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)(); - // Quota - IntColumn get quotaSizeInBytes => integer().nullable()(); - IntColumn get quotaUsageInBytes => integer().withDefault(const Constant(0))(); @override Set get primaryKey => {id}; diff --git a/mobile/lib/infrastructure/entities/user.entity.drift.dart b/mobile/lib/infrastructure/entities/user.entity.drift.dart index 32be969518..dbfddab4a0 100644 --- a/mobile/lib/infrastructure/entities/user.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/user.entity.drift.dart @@ -6,28 +6,26 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' import 'package:immich_mobile/infrastructure/entities/user.entity.dart' as i2; import 'package:drift/src/runtime/query_builder/query_builder.dart' as i3; -typedef $$UserEntityTableCreateCompanionBuilder = i1.UserEntityCompanion - Function({ - required String id, - required String name, - i0.Value isAdmin, - required String email, - i0.Value profileImagePath, - i0.Value updatedAt, - i0.Value quotaSizeInBytes, - i0.Value quotaUsageInBytes, -}); -typedef $$UserEntityTableUpdateCompanionBuilder = i1.UserEntityCompanion - Function({ - i0.Value id, - i0.Value name, - i0.Value isAdmin, - i0.Value email, - i0.Value profileImagePath, - i0.Value updatedAt, - i0.Value quotaSizeInBytes, - i0.Value quotaUsageInBytes, -}); +typedef $$UserEntityTableCreateCompanionBuilder = + i1.UserEntityCompanion Function({ + required String id, + required String name, + i0.Value isAdmin, + required String email, + i0.Value hasProfileImage, + i0.Value profileChangedAt, + i0.Value updatedAt, + }); +typedef $$UserEntityTableUpdateCompanionBuilder = + i1.UserEntityCompanion Function({ + i0.Value id, + i0.Value name, + i0.Value isAdmin, + i0.Value email, + i0.Value hasProfileImage, + i0.Value profileChangedAt, + i0.Value updatedAt, + }); class $$UserEntityTableFilterComposer extends i0.Composer { @@ -39,31 +37,39 @@ class $$UserEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnFilters get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnFilters(column)); + column: $table.id, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnFilters(column)); + column: $table.name, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get isAdmin => $composableBuilder( - column: $table.isAdmin, builder: (column) => i0.ColumnFilters(column)); + column: $table.isAdmin, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get email => $composableBuilder( - column: $table.email, builder: (column) => i0.ColumnFilters(column)); + column: $table.email, + builder: (column) => i0.ColumnFilters(column), + ); - i0.ColumnFilters get profileImagePath => $composableBuilder( - column: $table.profileImagePath, - builder: (column) => i0.ColumnFilters(column)); + i0.ColumnFilters get hasProfileImage => $composableBuilder( + column: $table.hasProfileImage, + builder: (column) => i0.ColumnFilters(column), + ); + + i0.ColumnFilters get profileChangedAt => $composableBuilder( + column: $table.profileChangedAt, + builder: (column) => i0.ColumnFilters(column), + ); i0.ColumnFilters get updatedAt => $composableBuilder( - column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column)); - - i0.ColumnFilters get quotaSizeInBytes => $composableBuilder( - column: $table.quotaSizeInBytes, - builder: (column) => i0.ColumnFilters(column)); - - i0.ColumnFilters get quotaUsageInBytes => $composableBuilder( - column: $table.quotaUsageInBytes, - builder: (column) => i0.ColumnFilters(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnFilters(column), + ); } class $$UserEntityTableOrderingComposer @@ -76,32 +82,39 @@ class $$UserEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get id => $composableBuilder( - column: $table.id, builder: (column) => i0.ColumnOrderings(column)); + column: $table.id, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get name => $composableBuilder( - column: $table.name, builder: (column) => i0.ColumnOrderings(column)); + column: $table.name, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get isAdmin => $composableBuilder( - column: $table.isAdmin, builder: (column) => i0.ColumnOrderings(column)); + column: $table.isAdmin, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get email => $composableBuilder( - column: $table.email, builder: (column) => i0.ColumnOrderings(column)); + column: $table.email, + builder: (column) => i0.ColumnOrderings(column), + ); - i0.ColumnOrderings get profileImagePath => $composableBuilder( - column: $table.profileImagePath, - builder: (column) => i0.ColumnOrderings(column)); + i0.ColumnOrderings get hasProfileImage => $composableBuilder( + column: $table.hasProfileImage, + builder: (column) => i0.ColumnOrderings(column), + ); + + i0.ColumnOrderings get profileChangedAt => $composableBuilder( + column: $table.profileChangedAt, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get updatedAt => $composableBuilder( - column: $table.updatedAt, - builder: (column) => i0.ColumnOrderings(column)); - - i0.ColumnOrderings get quotaSizeInBytes => $composableBuilder( - column: $table.quotaSizeInBytes, - builder: (column) => i0.ColumnOrderings(column)); - - i0.ColumnOrderings get quotaUsageInBytes => $composableBuilder( - column: $table.quotaUsageInBytes, - builder: (column) => i0.ColumnOrderings(column)); + column: $table.updatedAt, + builder: (column) => i0.ColumnOrderings(column), + ); } class $$UserEntityTableAnnotationComposer @@ -125,38 +138,47 @@ class $$UserEntityTableAnnotationComposer i0.GeneratedColumn get email => $composableBuilder(column: $table.email, builder: (column) => column); - i0.GeneratedColumn get profileImagePath => $composableBuilder( - column: $table.profileImagePath, builder: (column) => column); + i0.GeneratedColumn get hasProfileImage => $composableBuilder( + column: $table.hasProfileImage, + builder: (column) => column, + ); + + i0.GeneratedColumn get profileChangedAt => $composableBuilder( + column: $table.profileChangedAt, + builder: (column) => column, + ); i0.GeneratedColumn get updatedAt => $composableBuilder(column: $table.updatedAt, builder: (column) => column); - - i0.GeneratedColumn get quotaSizeInBytes => $composableBuilder( - column: $table.quotaSizeInBytes, builder: (column) => column); - - i0.GeneratedColumn get quotaUsageInBytes => $composableBuilder( - column: $table.quotaUsageInBytes, builder: (column) => column); } -class $$UserEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$UserEntityTable, - i1.UserEntityData, - i1.$$UserEntityTableFilterComposer, - i1.$$UserEntityTableOrderingComposer, - i1.$$UserEntityTableAnnotationComposer, - $$UserEntityTableCreateCompanionBuilder, - $$UserEntityTableUpdateCompanionBuilder, - ( - i1.UserEntityData, - i0.BaseReferences - ), - i1.UserEntityData, - i0.PrefetchHooks Function()> { +class $$UserEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$UserEntityTable, + i1.UserEntityData, + i1.$$UserEntityTableFilterComposer, + i1.$$UserEntityTableOrderingComposer, + i1.$$UserEntityTableAnnotationComposer, + $$UserEntityTableCreateCompanionBuilder, + $$UserEntityTableUpdateCompanionBuilder, + ( + i1.UserEntityData, + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$UserEntityTable, + i1.UserEntityData + >, + ), + i1.UserEntityData, + i0.PrefetchHooks Function() + > { $$UserEntityTableTableManager( - i0.GeneratedDatabase db, i1.$UserEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$UserEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => @@ -165,69 +187,71 @@ class $$UserEntityTableTableManager extends i0.RootTableManager< i1.$$UserEntityTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => i1.$$UserEntityTableAnnotationComposer($db: db, $table: table), - updateCompanionCallback: ({ - i0.Value id = const i0.Value.absent(), - i0.Value name = const i0.Value.absent(), - i0.Value isAdmin = const i0.Value.absent(), - i0.Value email = const i0.Value.absent(), - i0.Value profileImagePath = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value quotaSizeInBytes = const i0.Value.absent(), - i0.Value quotaUsageInBytes = const i0.Value.absent(), - }) => - i1.UserEntityCompanion( - id: id, - name: name, - isAdmin: isAdmin, - email: email, - profileImagePath: profileImagePath, - updatedAt: updatedAt, - quotaSizeInBytes: quotaSizeInBytes, - quotaUsageInBytes: quotaUsageInBytes, - ), - createCompanionCallback: ({ - required String id, - required String name, - i0.Value isAdmin = const i0.Value.absent(), - required String email, - i0.Value profileImagePath = const i0.Value.absent(), - i0.Value updatedAt = const i0.Value.absent(), - i0.Value quotaSizeInBytes = const i0.Value.absent(), - i0.Value quotaUsageInBytes = const i0.Value.absent(), - }) => - i1.UserEntityCompanion.insert( - id: id, - name: name, - isAdmin: isAdmin, - email: email, - profileImagePath: profileImagePath, - updatedAt: updatedAt, - quotaSizeInBytes: quotaSizeInBytes, - quotaUsageInBytes: quotaUsageInBytes, - ), + updateCompanionCallback: + ({ + i0.Value id = const i0.Value.absent(), + i0.Value name = const i0.Value.absent(), + i0.Value isAdmin = const i0.Value.absent(), + i0.Value email = const i0.Value.absent(), + i0.Value hasProfileImage = const i0.Value.absent(), + i0.Value profileChangedAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + }) => i1.UserEntityCompanion( + id: id, + name: name, + isAdmin: isAdmin, + email: email, + hasProfileImage: hasProfileImage, + profileChangedAt: profileChangedAt, + updatedAt: updatedAt, + ), + createCompanionCallback: + ({ + required String id, + required String name, + i0.Value isAdmin = const i0.Value.absent(), + required String email, + i0.Value hasProfileImage = const i0.Value.absent(), + i0.Value profileChangedAt = const i0.Value.absent(), + i0.Value updatedAt = const i0.Value.absent(), + }) => i1.UserEntityCompanion.insert( + id: id, + name: name, + isAdmin: isAdmin, + email: email, + hasProfileImage: hasProfileImage, + profileChangedAt: profileChangedAt, + updatedAt: updatedAt, + ), withReferenceMapper: (p0) => p0 .map((e) => (e.readTable(table), i0.BaseReferences(db, table, e))) .toList(), prefetchHooksCallback: null, - )); + ), + ); } -typedef $$UserEntityTableProcessedTableManager = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$UserEntityTable, - i1.UserEntityData, - i1.$$UserEntityTableFilterComposer, - i1.$$UserEntityTableOrderingComposer, - i1.$$UserEntityTableAnnotationComposer, - $$UserEntityTableCreateCompanionBuilder, - $$UserEntityTableUpdateCompanionBuilder, - ( +typedef $$UserEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$UserEntityTable, i1.UserEntityData, - i0.BaseReferences - ), - i1.UserEntityData, - i0.PrefetchHooks Function()>; + i1.$$UserEntityTableFilterComposer, + i1.$$UserEntityTableOrderingComposer, + i1.$$UserEntityTableAnnotationComposer, + $$UserEntityTableCreateCompanionBuilder, + $$UserEntityTableUpdateCompanionBuilder, + ( + i1.UserEntityData, + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$UserEntityTable, + i1.UserEntityData + >, + ), + i1.UserEntityData, + i0.PrefetchHooks Function() + >; class $UserEntityTable extends i2.UserEntity with i0.TableInfo<$UserEntityTable, i1.UserEntityData> { @@ -238,69 +262,99 @@ class $UserEntityTable extends i2.UserEntity static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); @override late final i0.GeneratedColumn id = i0.GeneratedColumn( - 'id', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _nameMeta = - const i0.VerificationMeta('name'); + 'id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _nameMeta = const i0.VerificationMeta( + 'name', + ); @override late final i0.GeneratedColumn name = i0.GeneratedColumn( - 'name', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _isAdminMeta = - const i0.VerificationMeta('isAdmin'); + 'name', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _isAdminMeta = const i0.VerificationMeta( + 'isAdmin', + ); @override late final i0.GeneratedColumn isAdmin = i0.GeneratedColumn( - 'is_admin', aliasedName, false, - type: i0.DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - i0.GeneratedColumn.constraintIsAlways('CHECK ("is_admin" IN (0, 1))'), - defaultValue: const i3.Constant(false)); - static const i0.VerificationMeta _emailMeta = - const i0.VerificationMeta('email'); + 'is_admin', + aliasedName, + false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const i3.Constant(false), + ); + static const i0.VerificationMeta _emailMeta = const i0.VerificationMeta( + 'email', + ); @override late final i0.GeneratedColumn email = i0.GeneratedColumn( - 'email', aliasedName, false, - type: i0.DriftSqlType.string, requiredDuringInsert: true); - static const i0.VerificationMeta _profileImagePathMeta = - const i0.VerificationMeta('profileImagePath'); + 'email', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + ); + static const i0.VerificationMeta _hasProfileImageMeta = + const i0.VerificationMeta('hasProfileImage'); @override - late final i0.GeneratedColumn profileImagePath = - i0.GeneratedColumn('profile_image_path', aliasedName, true, - type: i0.DriftSqlType.string, requiredDuringInsert: false); - static const i0.VerificationMeta _updatedAtMeta = - const i0.VerificationMeta('updatedAt'); + late final i0.GeneratedColumn hasProfileImage = + i0.GeneratedColumn( + 'has_profile_image', + aliasedName, + false, + type: i0.DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'CHECK ("has_profile_image" IN (0, 1))', + ), + defaultValue: const i3.Constant(false), + ); + static const i0.VerificationMeta _profileChangedAtMeta = + const i0.VerificationMeta('profileChangedAt'); + @override + late final i0.GeneratedColumn profileChangedAt = + i0.GeneratedColumn( + 'profile_changed_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i3.currentDateAndTime, + ); + static const i0.VerificationMeta _updatedAtMeta = const i0.VerificationMeta( + 'updatedAt', + ); @override late final i0.GeneratedColumn updatedAt = - i0.GeneratedColumn('updated_at', aliasedName, false, - type: i0.DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: i3.currentDateAndTime); - static const i0.VerificationMeta _quotaSizeInBytesMeta = - const i0.VerificationMeta('quotaSizeInBytes'); - @override - late final i0.GeneratedColumn quotaSizeInBytes = i0.GeneratedColumn( - 'quota_size_in_bytes', aliasedName, true, - type: i0.DriftSqlType.int, requiredDuringInsert: false); - static const i0.VerificationMeta _quotaUsageInBytesMeta = - const i0.VerificationMeta('quotaUsageInBytes'); - @override - late final i0.GeneratedColumn quotaUsageInBytes = - i0.GeneratedColumn('quota_usage_in_bytes', aliasedName, false, - type: i0.DriftSqlType.int, - requiredDuringInsert: false, - defaultValue: const i3.Constant(0)); + i0.GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: i0.DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: i3.currentDateAndTime, + ); @override List get $columns => [ - id, - name, - isAdmin, - email, - profileImagePath, - updatedAt, - quotaSizeInBytes, - quotaUsageInBytes - ]; + id, + name, + isAdmin, + email, + hasProfileImage, + profileChangedAt, + updatedAt, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -308,8 +362,9 @@ class $UserEntityTable extends i2.UserEntity static const String $name = 'user_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('id')) { @@ -319,41 +374,49 @@ class $UserEntityTable extends i2.UserEntity } if (data.containsKey('name')) { context.handle( - _nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta)); + _nameMeta, + name.isAcceptableOrUnknown(data['name']!, _nameMeta), + ); } else if (isInserting) { context.missing(_nameMeta); } if (data.containsKey('is_admin')) { - context.handle(_isAdminMeta, - isAdmin.isAcceptableOrUnknown(data['is_admin']!, _isAdminMeta)); + context.handle( + _isAdminMeta, + isAdmin.isAcceptableOrUnknown(data['is_admin']!, _isAdminMeta), + ); } if (data.containsKey('email')) { context.handle( - _emailMeta, email.isAcceptableOrUnknown(data['email']!, _emailMeta)); + _emailMeta, + email.isAcceptableOrUnknown(data['email']!, _emailMeta), + ); } else if (isInserting) { context.missing(_emailMeta); } - if (data.containsKey('profile_image_path')) { + if (data.containsKey('has_profile_image')) { context.handle( - _profileImagePathMeta, - profileImagePath.isAcceptableOrUnknown( - data['profile_image_path']!, _profileImagePathMeta)); + _hasProfileImageMeta, + hasProfileImage.isAcceptableOrUnknown( + data['has_profile_image']!, + _hasProfileImageMeta, + ), + ); + } + if (data.containsKey('profile_changed_at')) { + context.handle( + _profileChangedAtMeta, + profileChangedAt.isAcceptableOrUnknown( + data['profile_changed_at']!, + _profileChangedAtMeta, + ), + ); } if (data.containsKey('updated_at')) { - context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); - } - if (data.containsKey('quota_size_in_bytes')) { context.handle( - _quotaSizeInBytesMeta, - quotaSizeInBytes.isAcceptableOrUnknown( - data['quota_size_in_bytes']!, _quotaSizeInBytesMeta)); - } - if (data.containsKey('quota_usage_in_bytes')) { - context.handle( - _quotaUsageInBytesMeta, - quotaUsageInBytes.isAcceptableOrUnknown( - data['quota_usage_in_bytes']!, _quotaUsageInBytesMeta)); + _updatedAtMeta, + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), + ); } return context; } @@ -364,22 +427,34 @@ class $UserEntityTable extends i2.UserEntity i1.UserEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.UserEntityData( - id: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!, - isAdmin: attachedDatabase.typeMapping - .read(i0.DriftSqlType.bool, data['${effectivePrefix}is_admin'])!, - email: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}email'])!, - profileImagePath: attachedDatabase.typeMapping.read( - i0.DriftSqlType.string, data['${effectivePrefix}profile_image_path']), + id: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + isAdmin: attachedDatabase.typeMapping.read( + i0.DriftSqlType.bool, + data['${effectivePrefix}is_admin'], + )!, + email: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}email'], + )!, + hasProfileImage: attachedDatabase.typeMapping.read( + i0.DriftSqlType.bool, + data['${effectivePrefix}has_profile_image'], + )!, + profileChangedAt: attachedDatabase.typeMapping.read( + i0.DriftSqlType.dateTime, + data['${effectivePrefix}profile_changed_at'], + )!, updatedAt: attachedDatabase.typeMapping.read( - i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - quotaSizeInBytes: attachedDatabase.typeMapping.read( - i0.DriftSqlType.int, data['${effectivePrefix}quota_size_in_bytes']), - quotaUsageInBytes: attachedDatabase.typeMapping.read( - i0.DriftSqlType.int, data['${effectivePrefix}quota_usage_in_bytes'])!, + i0.DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, ); } @@ -400,19 +475,18 @@ class UserEntityData extends i0.DataClass final String name; final bool isAdmin; final String email; - final String? profileImagePath; + final bool hasProfileImage; + final DateTime profileChangedAt; final DateTime updatedAt; - final int? quotaSizeInBytes; - final int quotaUsageInBytes; - const UserEntityData( - {required this.id, - required this.name, - required this.isAdmin, - required this.email, - this.profileImagePath, - required this.updatedAt, - this.quotaSizeInBytes, - required this.quotaUsageInBytes}); + const UserEntityData({ + required this.id, + required this.name, + required this.isAdmin, + required this.email, + required this.hasProfileImage, + required this.profileChangedAt, + required this.updatedAt, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -420,29 +494,25 @@ class UserEntityData extends i0.DataClass map['name'] = i0.Variable(name); map['is_admin'] = i0.Variable(isAdmin); map['email'] = i0.Variable(email); - if (!nullToAbsent || profileImagePath != null) { - map['profile_image_path'] = i0.Variable(profileImagePath); - } + map['has_profile_image'] = i0.Variable(hasProfileImage); + map['profile_changed_at'] = i0.Variable(profileChangedAt); map['updated_at'] = i0.Variable(updatedAt); - if (!nullToAbsent || quotaSizeInBytes != null) { - map['quota_size_in_bytes'] = i0.Variable(quotaSizeInBytes); - } - map['quota_usage_in_bytes'] = i0.Variable(quotaUsageInBytes); return map; } - factory UserEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory UserEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return UserEntityData( id: serializer.fromJson(json['id']), name: serializer.fromJson(json['name']), isAdmin: serializer.fromJson(json['isAdmin']), email: serializer.fromJson(json['email']), - profileImagePath: serializer.fromJson(json['profileImagePath']), + hasProfileImage: serializer.fromJson(json['hasProfileImage']), + profileChangedAt: serializer.fromJson(json['profileChangedAt']), updatedAt: serializer.fromJson(json['updatedAt']), - quotaSizeInBytes: serializer.fromJson(json['quotaSizeInBytes']), - quotaUsageInBytes: serializer.fromJson(json['quotaUsageInBytes']), ); } @override @@ -453,52 +523,42 @@ class UserEntityData extends i0.DataClass 'name': serializer.toJson(name), 'isAdmin': serializer.toJson(isAdmin), 'email': serializer.toJson(email), - 'profileImagePath': serializer.toJson(profileImagePath), + 'hasProfileImage': serializer.toJson(hasProfileImage), + 'profileChangedAt': serializer.toJson(profileChangedAt), 'updatedAt': serializer.toJson(updatedAt), - 'quotaSizeInBytes': serializer.toJson(quotaSizeInBytes), - 'quotaUsageInBytes': serializer.toJson(quotaUsageInBytes), }; } - i1.UserEntityData copyWith( - {String? id, - String? name, - bool? isAdmin, - String? email, - i0.Value profileImagePath = const i0.Value.absent(), - DateTime? updatedAt, - i0.Value quotaSizeInBytes = const i0.Value.absent(), - int? quotaUsageInBytes}) => - i1.UserEntityData( - id: id ?? this.id, - name: name ?? this.name, - isAdmin: isAdmin ?? this.isAdmin, - email: email ?? this.email, - profileImagePath: profileImagePath.present - ? profileImagePath.value - : this.profileImagePath, - updatedAt: updatedAt ?? this.updatedAt, - quotaSizeInBytes: quotaSizeInBytes.present - ? quotaSizeInBytes.value - : this.quotaSizeInBytes, - quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, - ); + i1.UserEntityData copyWith({ + String? id, + String? name, + bool? isAdmin, + String? email, + bool? hasProfileImage, + DateTime? profileChangedAt, + DateTime? updatedAt, + }) => i1.UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + updatedAt: updatedAt ?? this.updatedAt, + ); UserEntityData copyWithCompanion(i1.UserEntityCompanion data) { return UserEntityData( id: data.id.present ? data.id.value : this.id, name: data.name.present ? data.name.value : this.name, isAdmin: data.isAdmin.present ? data.isAdmin.value : this.isAdmin, email: data.email.present ? data.email.value : this.email, - profileImagePath: data.profileImagePath.present - ? data.profileImagePath.value - : this.profileImagePath, + hasProfileImage: data.hasProfileImage.present + ? data.hasProfileImage.value + : this.hasProfileImage, + profileChangedAt: data.profileChangedAt.present + ? data.profileChangedAt.value + : this.profileChangedAt, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, - quotaSizeInBytes: data.quotaSizeInBytes.present - ? data.quotaSizeInBytes.value - : this.quotaSizeInBytes, - quotaUsageInBytes: data.quotaUsageInBytes.present - ? data.quotaUsageInBytes.value - : this.quotaUsageInBytes, ); } @@ -509,17 +569,23 @@ class UserEntityData extends i0.DataClass ..write('name: $name, ') ..write('isAdmin: $isAdmin, ') ..write('email: $email, ') - ..write('profileImagePath: $profileImagePath, ') - ..write('updatedAt: $updatedAt, ') - ..write('quotaSizeInBytes: $quotaSizeInBytes, ') - ..write('quotaUsageInBytes: $quotaUsageInBytes') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('updatedAt: $updatedAt') ..write(')')) .toString(); } @override - int get hashCode => Object.hash(id, name, isAdmin, email, profileImagePath, - updatedAt, quotaSizeInBytes, quotaUsageInBytes); + int get hashCode => Object.hash( + id, + name, + isAdmin, + email, + hasProfileImage, + profileChangedAt, + updatedAt, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -528,10 +594,9 @@ class UserEntityData extends i0.DataClass other.name == this.name && other.isAdmin == this.isAdmin && other.email == this.email && - other.profileImagePath == this.profileImagePath && - other.updatedAt == this.updatedAt && - other.quotaSizeInBytes == this.quotaSizeInBytes && - other.quotaUsageInBytes == this.quotaUsageInBytes); + other.hasProfileImage == this.hasProfileImage && + other.profileChangedAt == this.profileChangedAt && + other.updatedAt == this.updatedAt); } class UserEntityCompanion extends i0.UpdateCompanion { @@ -539,72 +604,66 @@ class UserEntityCompanion extends i0.UpdateCompanion { final i0.Value name; final i0.Value isAdmin; final i0.Value email; - final i0.Value profileImagePath; + final i0.Value hasProfileImage; + final i0.Value profileChangedAt; final i0.Value updatedAt; - final i0.Value quotaSizeInBytes; - final i0.Value quotaUsageInBytes; const UserEntityCompanion({ this.id = const i0.Value.absent(), this.name = const i0.Value.absent(), this.isAdmin = const i0.Value.absent(), this.email = const i0.Value.absent(), - this.profileImagePath = const i0.Value.absent(), + this.hasProfileImage = const i0.Value.absent(), + this.profileChangedAt = const i0.Value.absent(), this.updatedAt = const i0.Value.absent(), - this.quotaSizeInBytes = const i0.Value.absent(), - this.quotaUsageInBytes = const i0.Value.absent(), }); UserEntityCompanion.insert({ required String id, required String name, this.isAdmin = const i0.Value.absent(), required String email, - this.profileImagePath = const i0.Value.absent(), + this.hasProfileImage = const i0.Value.absent(), + this.profileChangedAt = const i0.Value.absent(), this.updatedAt = const i0.Value.absent(), - this.quotaSizeInBytes = const i0.Value.absent(), - this.quotaUsageInBytes = const i0.Value.absent(), - }) : id = i0.Value(id), - name = i0.Value(name), - email = i0.Value(email); + }) : id = i0.Value(id), + name = i0.Value(name), + email = i0.Value(email); static i0.Insertable custom({ i0.Expression? id, i0.Expression? name, i0.Expression? isAdmin, i0.Expression? email, - i0.Expression? profileImagePath, + i0.Expression? hasProfileImage, + i0.Expression? profileChangedAt, i0.Expression? updatedAt, - i0.Expression? quotaSizeInBytes, - i0.Expression? quotaUsageInBytes, }) { return i0.RawValuesInsertable({ if (id != null) 'id': id, if (name != null) 'name': name, if (isAdmin != null) 'is_admin': isAdmin, if (email != null) 'email': email, - if (profileImagePath != null) 'profile_image_path': profileImagePath, + if (hasProfileImage != null) 'has_profile_image': hasProfileImage, + if (profileChangedAt != null) 'profile_changed_at': profileChangedAt, if (updatedAt != null) 'updated_at': updatedAt, - if (quotaSizeInBytes != null) 'quota_size_in_bytes': quotaSizeInBytes, - if (quotaUsageInBytes != null) 'quota_usage_in_bytes': quotaUsageInBytes, }); } - i1.UserEntityCompanion copyWith( - {i0.Value? id, - i0.Value? name, - i0.Value? isAdmin, - i0.Value? email, - i0.Value? profileImagePath, - i0.Value? updatedAt, - i0.Value? quotaSizeInBytes, - i0.Value? quotaUsageInBytes}) { + i1.UserEntityCompanion copyWith({ + i0.Value? id, + i0.Value? name, + i0.Value? isAdmin, + i0.Value? email, + i0.Value? hasProfileImage, + i0.Value? profileChangedAt, + i0.Value? updatedAt, + }) { return i1.UserEntityCompanion( id: id ?? this.id, name: name ?? this.name, isAdmin: isAdmin ?? this.isAdmin, email: email ?? this.email, - profileImagePath: profileImagePath ?? this.profileImagePath, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, updatedAt: updatedAt ?? this.updatedAt, - quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes, - quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, ); } @@ -623,18 +682,15 @@ class UserEntityCompanion extends i0.UpdateCompanion { if (email.present) { map['email'] = i0.Variable(email.value); } - if (profileImagePath.present) { - map['profile_image_path'] = i0.Variable(profileImagePath.value); + if (hasProfileImage.present) { + map['has_profile_image'] = i0.Variable(hasProfileImage.value); + } + if (profileChangedAt.present) { + map['profile_changed_at'] = i0.Variable(profileChangedAt.value); } if (updatedAt.present) { map['updated_at'] = i0.Variable(updatedAt.value); } - if (quotaSizeInBytes.present) { - map['quota_size_in_bytes'] = i0.Variable(quotaSizeInBytes.value); - } - if (quotaUsageInBytes.present) { - map['quota_usage_in_bytes'] = i0.Variable(quotaUsageInBytes.value); - } return map; } @@ -645,10 +701,9 @@ class UserEntityCompanion extends i0.UpdateCompanion { ..write('name: $name, ') ..write('isAdmin: $isAdmin, ') ..write('email: $email, ') - ..write('profileImagePath: $profileImagePath, ') - ..write('updatedAt: $updatedAt, ') - ..write('quotaSizeInBytes: $quotaSizeInBytes, ') - ..write('quotaUsageInBytes: $quotaUsageInBytes') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('updatedAt: $updatedAt') ..write(')')) .toString(); } diff --git a/mobile/lib/infrastructure/entities/user.entity.g.dart b/mobile/lib/infrastructure/entities/user.entity.g.dart index 37a793b2c3..bb87051731 100644 --- a/mobile/lib/infrastructure/entities/user.entity.g.dart +++ b/mobile/lib/infrastructure/entities/user.entity.g.dart @@ -23,26 +23,14 @@ const UserSchema = CollectionSchema( type: IsarType.byte, enumMap: _UseravatarColorEnumValueMap, ), - r'email': PropertySchema( - id: 1, - name: r'email', - type: IsarType.string, - ), - r'id': PropertySchema( - id: 2, - name: r'id', - type: IsarType.string, - ), + r'email': PropertySchema(id: 1, name: r'email', type: IsarType.string), + r'id': PropertySchema(id: 2, name: r'id', type: IsarType.string), r'inTimeline': PropertySchema( id: 3, name: r'inTimeline', type: IsarType.bool, ), - r'isAdmin': PropertySchema( - id: 4, - name: r'isAdmin', - type: IsarType.bool, - ), + r'isAdmin': PropertySchema(id: 4, name: r'isAdmin', type: IsarType.bool), r'isPartnerSharedBy': PropertySchema( id: 5, name: r'isPartnerSharedBy', @@ -58,11 +46,7 @@ const UserSchema = CollectionSchema( name: r'memoryEnabled', type: IsarType.bool, ), - r'name': PropertySchema( - id: 8, - name: r'name', - type: IsarType.string, - ), + r'name': PropertySchema(id: 8, name: r'name', type: IsarType.string), r'profileImagePath': PropertySchema( id: 9, name: r'profileImagePath', @@ -82,8 +66,9 @@ const UserSchema = CollectionSchema( id: 12, name: r'updatedAt', type: IsarType.dateTime, - ) + ), }, + estimateSize: _userEstimateSize, serialize: _userSerialize, deserialize: _userDeserialize, @@ -100,12 +85,13 @@ const UserSchema = CollectionSchema( name: r'id', type: IndexType.hash, caseSensitive: true, - ) + ), ], - ) + ), }, links: {}, embeddedSchemas: {}, + getId: _userGetId, getLinks: _userGetLinks, attach: _userAttach, @@ -155,7 +141,7 @@ User _userDeserialize( final object = User( avatarColor: _UseravatarColorValueEnumMap[reader.readByteOrNull(offsets[0])] ?? - AvatarColor.primary, + AvatarColor.primary, email: reader.readString(offsets[1]), id: reader.readString(offsets[2]), inTimeline: reader.readBoolOrNull(offsets[3]) ?? false, @@ -181,7 +167,8 @@ P _userDeserializeProp

( switch (propertyId) { case 0: return (_UseravatarColorValueEnumMap[reader.readByteOrNull(offset)] ?? - AvatarColor.primary) as P; + AvatarColor.primary) + as P; case 1: return (reader.readString(offset)) as P; case 2: @@ -311,10 +298,9 @@ extension UserQueryWhereSort on QueryBuilder { extension UserQueryWhere on QueryBuilder { QueryBuilder isarIdEqualTo(Id isarId) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: isarId, - upper: isarId, - )); + return query.addWhereClause( + IdWhereClause.between(lower: isarId, upper: isarId), + ); }); } @@ -340,8 +326,10 @@ extension UserQueryWhere on QueryBuilder { }); } - QueryBuilder isarIdGreaterThan(Id isarId, - {bool include = false}) { + QueryBuilder isarIdGreaterThan( + Id isarId, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.greaterThan(lower: isarId, includeLower: include), @@ -349,8 +337,10 @@ extension UserQueryWhere on QueryBuilder { }); } - QueryBuilder isarIdLessThan(Id isarId, - {bool include = false}) { + QueryBuilder isarIdLessThan( + Id isarId, { + bool include = false, + }) { return QueryBuilder.apply(this, (query) { return query.addWhereClause( IdWhereClause.lessThan(upper: isarId, includeUpper: include), @@ -365,21 +355,22 @@ extension UserQueryWhere on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IdWhereClause.between( - lower: lowerIsarId, - includeLower: includeLower, - upper: upperIsarId, - includeUpper: includeUpper, - )); + return query.addWhereClause( + IdWhereClause.between( + lower: lowerIsarId, + includeLower: includeLower, + upper: upperIsarId, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder idEqualTo(String id) { return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'id', - value: [id], - )); + return query.addWhereClause( + IndexWhereClause.equalTo(indexName: r'id', value: [id]), + ); }); } @@ -387,32 +378,40 @@ extension UserQueryWhere on QueryBuilder { return QueryBuilder.apply(this, (query) { if (query.whereSort == Sort.asc) { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [], - upper: [id], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [id], - includeLower: false, - upper: [], - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [], + upper: [id], + includeUpper: false, + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [id], + includeLower: false, + upper: [], + ), + ); } else { return query - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [id], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'id', - lower: [], - upper: [id], - includeUpper: false, - )); + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [id], + includeLower: false, + upper: [], + ), + ) + .addWhereClause( + IndexWhereClause.between( + indexName: r'id', + lower: [], + upper: [id], + includeUpper: false, + ), + ); } }); } @@ -420,12 +419,12 @@ extension UserQueryWhere on QueryBuilder { extension UserQueryFilter on QueryBuilder { QueryBuilder avatarColorEqualTo( - AvatarColor value) { + AvatarColor value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'avatarColor', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'avatarColor', value: value), + ); }); } @@ -434,11 +433,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'avatarColor', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'avatarColor', + value: value, + ), + ); }); } @@ -447,11 +448,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'avatarColor', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'avatarColor', + value: value, + ), + ); }); } @@ -462,13 +465,15 @@ extension UserQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'avatarColor', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'avatarColor', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } @@ -477,11 +482,13 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'email', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'email', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -491,12 +498,14 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'email', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'email', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -506,12 +515,14 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'email', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'email', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -523,14 +534,16 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'email', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'email', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -539,11 +552,13 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'email', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'email', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -552,51 +567,59 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'email', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'email', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder emailContains(String value, - {bool caseSensitive = true}) { + QueryBuilder emailContains( + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'email', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'email', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder emailMatches(String pattern, - {bool caseSensitive = true}) { + QueryBuilder emailMatches( + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'email', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'email', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder emailIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'email', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'email', value: ''), + ); }); } QueryBuilder emailIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'email', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'email', value: ''), + ); }); } @@ -605,11 +628,13 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -619,12 +644,14 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -634,12 +661,14 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -651,14 +680,16 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'id', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -667,11 +698,13 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -680,99 +713,105 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder idContains(String value, - {bool caseSensitive = true}) { + QueryBuilder idContains( + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'id', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'id', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder idMatches(String pattern, - {bool caseSensitive = true}) { + QueryBuilder idMatches( + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'id', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'id', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder idIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'id', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'id', value: ''), + ); }); } QueryBuilder idIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'id', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'id', value: ''), + ); }); } QueryBuilder inTimelineEqualTo( - bool value) { + bool value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'inTimeline', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'inTimeline', value: value), + ); }); } QueryBuilder isAdminEqualTo(bool value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isAdmin', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isAdmin', value: value), + ); }); } QueryBuilder isPartnerSharedByEqualTo( - bool value) { + bool value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isPartnerSharedBy', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isPartnerSharedBy', value: value), + ); }); } QueryBuilder isPartnerSharedWithEqualTo( - bool value) { + bool value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isPartnerSharedWith', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isPartnerSharedWith', value: value), + ); }); } QueryBuilder isarIdEqualTo(Id value) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'isarId', value: value), + ); }); } @@ -781,11 +820,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'isarId', + value: value, + ), + ); }); } @@ -794,11 +835,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'isarId', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'isarId', + value: value, + ), + ); }); } @@ -809,23 +852,25 @@ extension UserQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'isarId', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'isarId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder memoryEnabledEqualTo( - bool value) { + bool value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'memoryEnabled', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'memoryEnabled', value: value), + ); }); } @@ -834,11 +879,13 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -848,12 +895,14 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -863,12 +912,14 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -880,14 +931,16 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'name', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'name', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -896,11 +949,13 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -909,51 +964,59 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder nameContains(String value, - {bool caseSensitive = true}) { + QueryBuilder nameContains( + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'name', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'name', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } - QueryBuilder nameMatches(String pattern, - {bool caseSensitive = true}) { + QueryBuilder nameMatches( + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'name', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'name', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder nameIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'name', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'name', value: ''), + ); }); } QueryBuilder nameIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'name', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'name', value: ''), + ); }); } @@ -962,11 +1025,13 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'profileImagePath', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.equalTo( + property: r'profileImagePath', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -976,12 +1041,14 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'profileImagePath', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'profileImagePath', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -991,12 +1058,14 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'profileImagePath', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'profileImagePath', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1008,14 +1077,16 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'profileImagePath', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'profileImagePath', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1024,11 +1095,13 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'profileImagePath', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.startsWith( + property: r'profileImagePath', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } @@ -1037,63 +1110,69 @@ extension UserQueryFilter on QueryBuilder { bool caseSensitive = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'profileImagePath', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.endsWith( + property: r'profileImagePath', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder profileImagePathContains( - String value, - {bool caseSensitive = true}) { + String value, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'profileImagePath', - value: value, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.contains( + property: r'profileImagePath', + value: value, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder profileImagePathMatches( - String pattern, - {bool caseSensitive = true}) { + String pattern, { + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'profileImagePath', - wildcard: pattern, - caseSensitive: caseSensitive, - )); + return query.addFilterCondition( + FilterCondition.matches( + property: r'profileImagePath', + wildcard: pattern, + caseSensitive: caseSensitive, + ), + ); }); } QueryBuilder profileImagePathIsEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'profileImagePath', - value: '', - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'profileImagePath', value: ''), + ); }); } QueryBuilder profileImagePathIsNotEmpty() { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'profileImagePath', - value: '', - )); + return query.addFilterCondition( + FilterCondition.greaterThan(property: r'profileImagePath', value: ''), + ); }); } QueryBuilder quotaSizeInBytesEqualTo( - int value) { + int value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'quotaSizeInBytes', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'quotaSizeInBytes', value: value), + ); }); } @@ -1102,11 +1181,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'quotaSizeInBytes', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'quotaSizeInBytes', + value: value, + ), + ); }); } @@ -1115,11 +1196,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'quotaSizeInBytes', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'quotaSizeInBytes', + value: value, + ), + ); }); } @@ -1130,23 +1213,25 @@ extension UserQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'quotaSizeInBytes', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'quotaSizeInBytes', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder quotaUsageInBytesEqualTo( - int value) { + int value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'quotaUsageInBytes', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'quotaUsageInBytes', value: value), + ); }); } @@ -1155,11 +1240,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'quotaUsageInBytes', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'quotaUsageInBytes', + value: value, + ), + ); }); } @@ -1168,11 +1255,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'quotaUsageInBytes', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'quotaUsageInBytes', + value: value, + ), + ); }); } @@ -1183,23 +1272,25 @@ extension UserQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'quotaUsageInBytes', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'quotaUsageInBytes', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } QueryBuilder updatedAtEqualTo( - DateTime value) { + DateTime value, + ) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'updatedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.equalTo(property: r'updatedAt', value: value), + ); }); } @@ -1208,11 +1299,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'updatedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.greaterThan( + include: include, + property: r'updatedAt', + value: value, + ), + ); }); } @@ -1221,11 +1314,13 @@ extension UserQueryFilter on QueryBuilder { bool include = false, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'updatedAt', - value: value, - )); + return query.addFilterCondition( + FilterCondition.lessThan( + include: include, + property: r'updatedAt', + value: value, + ), + ); }); } @@ -1236,13 +1331,15 @@ extension UserQueryFilter on QueryBuilder { bool includeUpper = true, }) { return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'updatedAt', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); + return query.addFilterCondition( + FilterCondition.between( + property: r'updatedAt', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + ), + ); }); } } @@ -1586,15 +1683,17 @@ extension UserQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctByEmail( - {bool caseSensitive = true}) { + QueryBuilder distinctByEmail({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'email', caseSensitive: caseSensitive); }); } - QueryBuilder distinctById( - {bool caseSensitive = true}) { + QueryBuilder distinctById({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'id', caseSensitive: caseSensitive); }); @@ -1630,18 +1729,22 @@ extension UserQueryWhereDistinct on QueryBuilder { }); } - QueryBuilder distinctByName( - {bool caseSensitive = true}) { + QueryBuilder distinctByName({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { return query.addDistinctBy(r'name', caseSensitive: caseSensitive); }); } - QueryBuilder distinctByProfileImagePath( - {bool caseSensitive = true}) { + QueryBuilder distinctByProfileImagePath({ + bool caseSensitive = true, + }) { return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(r'profileImagePath', - caseSensitive: caseSensitive); + return query.addDistinctBy( + r'profileImagePath', + caseSensitive: caseSensitive, + ); }); } diff --git a/mobile/lib/infrastructure/entities/user_metadata.entity.dart b/mobile/lib/infrastructure/entities/user_metadata.entity.dart index f9a411e3de..ede3de3966 100644 --- a/mobile/lib/infrastructure/entities/user_metadata.entity.dart +++ b/mobile/lib/infrastructure/entities/user_metadata.entity.dart @@ -6,8 +6,7 @@ import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; class UserMetadataEntity extends Table with DriftDefaultsMixin { const UserMetadataEntity(); - TextColumn get userId => - text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); + TextColumn get userId => text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); IntColumn get key => intEnum()(); @@ -17,7 +16,6 @@ class UserMetadataEntity extends Table with DriftDefaultsMixin { Set get primaryKey => {userId, key}; } -final JsonTypeConverter2, Uint8List, Object?> - userMetadataConverter = TypeConverter.jsonb( +final JsonTypeConverter2, Uint8List, Object?> userMetadataConverter = TypeConverter.jsonb( fromJson: (json) => json as Map, ); diff --git a/mobile/lib/infrastructure/entities/user_metadata.entity.drift.dart b/mobile/lib/infrastructure/entities/user_metadata.entity.drift.dart index a13ea5c04e..1e9dc8a890 100644 --- a/mobile/lib/infrastructure/entities/user_metadata.entity.drift.dart +++ b/mobile/lib/infrastructure/entities/user_metadata.entity.drift.dart @@ -11,51 +11,64 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' as i5; import 'package:drift/internal/modular.dart' as i6; -typedef $$UserMetadataEntityTableCreateCompanionBuilder - = i1.UserMetadataEntityCompanion Function({ - required String userId, - required i2.UserMetadataKey key, - required Map value, -}); -typedef $$UserMetadataEntityTableUpdateCompanionBuilder - = i1.UserMetadataEntityCompanion Function({ - i0.Value userId, - i0.Value key, - i0.Value> value, -}); +typedef $$UserMetadataEntityTableCreateCompanionBuilder = + i1.UserMetadataEntityCompanion Function({ + required String userId, + required i2.UserMetadataKey key, + required Map value, + }); +typedef $$UserMetadataEntityTableUpdateCompanionBuilder = + i1.UserMetadataEntityCompanion Function({ + i0.Value userId, + i0.Value key, + i0.Value> value, + }); -final class $$UserMetadataEntityTableReferences extends i0.BaseReferences< - i0.GeneratedDatabase, - i1.$UserMetadataEntityTable, - i1.UserMetadataEntityData> { +final class $$UserMetadataEntityTableReferences + extends + i0.BaseReferences< + i0.GeneratedDatabase, + i1.$UserMetadataEntityTable, + i1.UserMetadataEntityData + > { $$UserMetadataEntityTableReferences( - super.$_db, super.$_table, super.$_typedResult); + super.$_db, + super.$_table, + super.$_typedResult, + ); static i5.$UserEntityTable _userIdTable(i0.GeneratedDatabase db) => i6.ReadDatabaseContainer(db) .resultSet('user_entity') - .createAlias(i0.$_aliasNameGenerator( + .createAlias( + i0.$_aliasNameGenerator( i6.ReadDatabaseContainer(db) .resultSet( - 'user_metadata_entity') + 'user_metadata_entity', + ) .userId, - i6.ReadDatabaseContainer(db) - .resultSet('user_entity') - .id)); + i6.ReadDatabaseContainer( + db, + ).resultSet('user_entity').id, + ), + ); i5.$$UserEntityTableProcessedTableManager get userId { final $_column = $_itemColumn('user_id')!; final manager = i5 .$$UserEntityTableTableManager( + $_db, + i6.ReadDatabaseContainer( $_db, - i6.ReadDatabaseContainer($_db) - .resultSet('user_entity')) + ).resultSet('user_entity'), + ) .filter((f) => f.id.sqlEquals($_column)); final item = $_typedResult.readTableOrNull(_userIdTable($_db)); if (item == null) return manager; return i0.ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item])); + manager.$state.copyWith(prefetchedData: [item]), + ); } } @@ -69,35 +82,45 @@ class $$UserMetadataEntityTableFilterComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnWithTypeConverterFilters - get key => $composableBuilder( - column: $table.key, - builder: (column) => i0.ColumnWithTypeConverterFilters(column)); + get key => $composableBuilder( + column: $table.key, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); - i0.ColumnWithTypeConverterFilters, Map, - i3.Uint8List> - get value => $composableBuilder( - column: $table.value, - builder: (column) => i0.ColumnWithTypeConverterFilters(column)); + i0.ColumnWithTypeConverterFilters< + Map, + Map, + i3.Uint8List + > + get value => $composableBuilder( + column: $table.value, + builder: (column) => i0.ColumnWithTypeConverterFilters(column), + ); i5.$$UserEntityTableFilterComposer get userId { final i5.$$UserEntityTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.userId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableFilterComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.userId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableFilterComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -112,30 +135,39 @@ class $$UserMetadataEntityTableOrderingComposer super.$removeJoinBuilderFromRootComposer, }); i0.ColumnOrderings get key => $composableBuilder( - column: $table.key, builder: (column) => i0.ColumnOrderings(column)); + column: $table.key, + builder: (column) => i0.ColumnOrderings(column), + ); i0.ColumnOrderings get value => $composableBuilder( - column: $table.value, builder: (column) => i0.ColumnOrderings(column)); + column: $table.value, + builder: (column) => i0.ColumnOrderings(column), + ); i5.$$UserEntityTableOrderingComposer get userId { final i5.$$UserEntityTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.userId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableOrderingComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.userId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableOrderingComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } @@ -153,89 +185,106 @@ class $$UserMetadataEntityTableAnnotationComposer $composableBuilder(column: $table.key, builder: (column) => column); i0.GeneratedColumnWithTypeConverter, i3.Uint8List> - get value => - $composableBuilder(column: $table.value, builder: (column) => column); + get value => + $composableBuilder(column: $table.value, builder: (column) => column); i5.$$UserEntityTableAnnotationComposer get userId { final i5.$$UserEntityTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.userId, - referencedTable: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - getReferencedColumn: (t) => t.id, - builder: (joinBuilder, - {$addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer}) => - i5.$$UserEntityTableAnnotationComposer( - $db: $db, - $table: i6.ReadDatabaseContainer($db) - .resultSet('user_entity'), - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - )); + composer: this, + getCurrentColumn: (t) => t.userId, + referencedTable: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => i5.$$UserEntityTableAnnotationComposer( + $db: $db, + $table: i6.ReadDatabaseContainer( + $db, + ).resultSet('user_entity'), + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); return composer; } } -class $$UserMetadataEntityTableTableManager extends i0.RootTableManager< - i0.GeneratedDatabase, - i1.$UserMetadataEntityTable, - i1.UserMetadataEntityData, - i1.$$UserMetadataEntityTableFilterComposer, - i1.$$UserMetadataEntityTableOrderingComposer, - i1.$$UserMetadataEntityTableAnnotationComposer, - $$UserMetadataEntityTableCreateCompanionBuilder, - $$UserMetadataEntityTableUpdateCompanionBuilder, - (i1.UserMetadataEntityData, i1.$$UserMetadataEntityTableReferences), - i1.UserMetadataEntityData, - i0.PrefetchHooks Function({bool userId})> { +class $$UserMetadataEntityTableTableManager + extends + i0.RootTableManager< + i0.GeneratedDatabase, + i1.$UserMetadataEntityTable, + i1.UserMetadataEntityData, + i1.$$UserMetadataEntityTableFilterComposer, + i1.$$UserMetadataEntityTableOrderingComposer, + i1.$$UserMetadataEntityTableAnnotationComposer, + $$UserMetadataEntityTableCreateCompanionBuilder, + $$UserMetadataEntityTableUpdateCompanionBuilder, + (i1.UserMetadataEntityData, i1.$$UserMetadataEntityTableReferences), + i1.UserMetadataEntityData, + i0.PrefetchHooks Function({bool userId}) + > { $$UserMetadataEntityTableTableManager( - i0.GeneratedDatabase db, i1.$UserMetadataEntityTable table) - : super(i0.TableManagerState( + i0.GeneratedDatabase db, + i1.$UserMetadataEntityTable table, + ) : super( + i0.TableManagerState( db: db, table: table, createFilteringComposer: () => i1 .$$UserMetadataEntityTableFilterComposer($db: db, $table: table), createOrderingComposer: () => i1.$$UserMetadataEntityTableOrderingComposer( - $db: db, $table: table), + $db: db, + $table: table, + ), createComputedFieldComposer: () => i1.$$UserMetadataEntityTableAnnotationComposer( - $db: db, $table: table), - updateCompanionCallback: ({ - i0.Value userId = const i0.Value.absent(), - i0.Value key = const i0.Value.absent(), - i0.Value> value = const i0.Value.absent(), - }) => - i1.UserMetadataEntityCompanion( - userId: userId, - key: key, - value: value, - ), - createCompanionCallback: ({ - required String userId, - required i2.UserMetadataKey key, - required Map value, - }) => - i1.UserMetadataEntityCompanion.insert( - userId: userId, - key: key, - value: value, - ), + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + i0.Value userId = const i0.Value.absent(), + i0.Value key = const i0.Value.absent(), + i0.Value> value = const i0.Value.absent(), + }) => i1.UserMetadataEntityCompanion( + userId: userId, + key: key, + value: value, + ), + createCompanionCallback: + ({ + required String userId, + required i2.UserMetadataKey key, + required Map value, + }) => i1.UserMetadataEntityCompanion.insert( + userId: userId, + key: key, + value: value, + ), withReferenceMapper: (p0) => p0 - .map((e) => ( - e.readTable(table), - i1.$$UserMetadataEntityTableReferences(db, table, e) - )) + .map( + (e) => ( + e.readTable(table), + i1.$$UserMetadataEntityTableReferences(db, table, e), + ), + ) .toList(), prefetchHooksCallback: ({userId = false}) { return i0.PrefetchHooks( db: db, explicitlyWatchedTables: [], - addJoins: < - T extends i0.TableManagerState< + addJoins: + < + T extends i0.TableManagerState< dynamic, dynamic, dynamic, @@ -246,42 +295,50 @@ class $$UserMetadataEntityTableTableManager extends i0.RootTableManager< dynamic, dynamic, dynamic, - dynamic>>(state) { - if (userId) { - state = state.withJoin( - currentTable: table, - currentColumn: table.userId, - referencedTable: - i1.$$UserMetadataEntityTableReferences._userIdTable(db), - referencedColumn: i1.$$UserMetadataEntityTableReferences - ._userIdTable(db) - .id, - ) as T; - } + dynamic + > + >(state) { + if (userId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.userId, + referencedTable: i1 + .$$UserMetadataEntityTableReferences + ._userIdTable(db), + referencedColumn: i1 + .$$UserMetadataEntityTableReferences + ._userIdTable(db) + .id, + ) + as T; + } - return state; - }, + return state; + }, getPrefetchedDataCallback: (items) async { return []; }, ); }, - )); + ), + ); } -typedef $$UserMetadataEntityTableProcessedTableManager - = i0.ProcessedTableManager< - i0.GeneratedDatabase, - i1.$UserMetadataEntityTable, - i1.UserMetadataEntityData, - i1.$$UserMetadataEntityTableFilterComposer, - i1.$$UserMetadataEntityTableOrderingComposer, - i1.$$UserMetadataEntityTableAnnotationComposer, - $$UserMetadataEntityTableCreateCompanionBuilder, - $$UserMetadataEntityTableUpdateCompanionBuilder, - (i1.UserMetadataEntityData, i1.$$UserMetadataEntityTableReferences), - i1.UserMetadataEntityData, - i0.PrefetchHooks Function({bool userId})>; +typedef $$UserMetadataEntityTableProcessedTableManager = + i0.ProcessedTableManager< + i0.GeneratedDatabase, + i1.$UserMetadataEntityTable, + i1.UserMetadataEntityData, + i1.$$UserMetadataEntityTableFilterComposer, + i1.$$UserMetadataEntityTableOrderingComposer, + i1.$$UserMetadataEntityTableAnnotationComposer, + $$UserMetadataEntityTableCreateCompanionBuilder, + $$UserMetadataEntityTableUpdateCompanionBuilder, + (i1.UserMetadataEntityData, i1.$$UserMetadataEntityTableReferences), + i1.UserMetadataEntityData, + i0.PrefetchHooks Function({bool userId}) + >; class $UserMetadataEntityTable extends i4.UserMetadataEntity with i0.TableInfo<$UserMetadataEntityTable, i1.UserMetadataEntityData> { @@ -289,28 +346,46 @@ class $UserMetadataEntityTable extends i4.UserMetadataEntity final i0.GeneratedDatabase attachedDatabase; final String? _alias; $UserMetadataEntityTable(this.attachedDatabase, [this._alias]); - static const i0.VerificationMeta _userIdMeta = - const i0.VerificationMeta('userId'); + static const i0.VerificationMeta _userIdMeta = const i0.VerificationMeta( + 'userId', + ); @override late final i0.GeneratedColumn userId = i0.GeneratedColumn( - 'user_id', aliasedName, false, - type: i0.DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: i0.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'user_id', + aliasedName, + false, + type: i0.DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: i0.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); @override late final i0.GeneratedColumnWithTypeConverter key = - i0.GeneratedColumn('key', aliasedName, false, - type: i0.DriftSqlType.int, requiredDuringInsert: true) - .withConverter( - i1.$UserMetadataEntityTable.$converterkey); + i0.GeneratedColumn( + 'key', + aliasedName, + false, + type: i0.DriftSqlType.int, + requiredDuringInsert: true, + ).withConverter( + i1.$UserMetadataEntityTable.$converterkey, + ); @override - late final i0 - .GeneratedColumnWithTypeConverter, i3.Uint8List> - value = i0.GeneratedColumn('value', aliasedName, false, - type: i0.DriftSqlType.blob, requiredDuringInsert: true) - .withConverter>( - i1.$UserMetadataEntityTable.$convertervalue); + late final i0.GeneratedColumnWithTypeConverter< + Map, + i3.Uint8List + > + value = + i0.GeneratedColumn( + 'value', + aliasedName, + false, + type: i0.DriftSqlType.blob, + requiredDuringInsert: true, + ).withConverter>( + i1.$UserMetadataEntityTable.$convertervalue, + ); @override List get $columns => [userId, key, value]; @override @@ -320,13 +395,16 @@ class $UserMetadataEntityTable extends i4.UserMetadataEntity static const String $name = 'user_metadata_entity'; @override i0.VerificationContext validateIntegrity( - i0.Insertable instance, - {bool isInserting = false}) { + i0.Insertable instance, { + bool isInserting = false, + }) { final context = i0.VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('user_id')) { - context.handle(_userIdMeta, - userId.isAcceptableOrUnknown(data['user_id']!, _userIdMeta)); + context.handle( + _userIdMeta, + userId.isAcceptableOrUnknown(data['user_id']!, _userIdMeta), + ); } else if (isInserting) { context.missing(_userIdMeta); } @@ -336,18 +414,28 @@ class $UserMetadataEntityTable extends i4.UserMetadataEntity @override Set get $primaryKey => {userId, key}; @override - i1.UserMetadataEntityData map(Map data, - {String? tablePrefix}) { + i1.UserMetadataEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return i1.UserMetadataEntityData( - userId: attachedDatabase.typeMapping - .read(i0.DriftSqlType.string, data['${effectivePrefix}user_id'])!, - key: i1.$UserMetadataEntityTable.$converterkey.fromSql(attachedDatabase - .typeMapping - .read(i0.DriftSqlType.int, data['${effectivePrefix}key'])!), + userId: attachedDatabase.typeMapping.read( + i0.DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + key: i1.$UserMetadataEntityTable.$converterkey.fromSql( + attachedDatabase.typeMapping.read( + i0.DriftSqlType.int, + data['${effectivePrefix}key'], + )!, + ), value: i1.$UserMetadataEntityTable.$convertervalue.fromSql( - attachedDatabase.typeMapping - .read(i0.DriftSqlType.blob, data['${effectivePrefix}value'])!), + attachedDatabase.typeMapping.read( + i0.DriftSqlType.blob, + data['${effectivePrefix}value'], + )!, + ), ); } @@ -358,9 +446,10 @@ class $UserMetadataEntityTable extends i4.UserMetadataEntity static i0.JsonTypeConverter2 $converterkey = const i0.EnumIndexConverter( - i2.UserMetadataKey.values); + i2.UserMetadataKey.values, + ); static i0.JsonTypeConverter2, i3.Uint8List, Object?> - $convertervalue = i4.userMetadataConverter; + $convertervalue = i4.userMetadataConverter; @override bool get withoutRowId => true; @override @@ -372,32 +461,41 @@ class UserMetadataEntityData extends i0.DataClass final String userId; final i2.UserMetadataKey key; final Map value; - const UserMetadataEntityData( - {required this.userId, required this.key, required this.value}); + const UserMetadataEntityData({ + required this.userId, + required this.key, + required this.value, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; map['user_id'] = i0.Variable(userId); { map['key'] = i0.Variable( - i1.$UserMetadataEntityTable.$converterkey.toSql(key)); + i1.$UserMetadataEntityTable.$converterkey.toSql(key), + ); } { map['value'] = i0.Variable( - i1.$UserMetadataEntityTable.$convertervalue.toSql(value)); + i1.$UserMetadataEntityTable.$convertervalue.toSql(value), + ); } return map; } - factory UserMetadataEntityData.fromJson(Map json, - {i0.ValueSerializer? serializer}) { + factory UserMetadataEntityData.fromJson( + Map json, { + i0.ValueSerializer? serializer, + }) { serializer ??= i0.driftRuntimeOptions.defaultSerializer; return UserMetadataEntityData( userId: serializer.fromJson(json['userId']), - key: i1.$UserMetadataEntityTable.$converterkey - .fromJson(serializer.fromJson(json['key'])), - value: i1.$UserMetadataEntityTable.$convertervalue - .fromJson(serializer.fromJson(json['value'])), + key: i1.$UserMetadataEntityTable.$converterkey.fromJson( + serializer.fromJson(json['key']), + ), + value: i1.$UserMetadataEntityTable.$convertervalue.fromJson( + serializer.fromJson(json['value']), + ), ); } @override @@ -405,24 +503,27 @@ class UserMetadataEntityData extends i0.DataClass serializer ??= i0.driftRuntimeOptions.defaultSerializer; return { 'userId': serializer.toJson(userId), - 'key': serializer - .toJson(i1.$UserMetadataEntityTable.$converterkey.toJson(key)), + 'key': serializer.toJson( + i1.$UserMetadataEntityTable.$converterkey.toJson(key), + ), 'value': serializer.toJson( - i1.$UserMetadataEntityTable.$convertervalue.toJson(value)), + i1.$UserMetadataEntityTable.$convertervalue.toJson(value), + ), }; } - i1.UserMetadataEntityData copyWith( - {String? userId, - i2.UserMetadataKey? key, - Map? value}) => - i1.UserMetadataEntityData( - userId: userId ?? this.userId, - key: key ?? this.key, - value: value ?? this.value, - ); + i1.UserMetadataEntityData copyWith({ + String? userId, + i2.UserMetadataKey? key, + Map? value, + }) => i1.UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); UserMetadataEntityData copyWithCompanion( - i1.UserMetadataEntityCompanion data) { + i1.UserMetadataEntityCompanion data, + ) { return UserMetadataEntityData( userId: data.userId.present ? data.userId.value : this.userId, key: data.key.present ? data.key.value : this.key, @@ -465,9 +566,9 @@ class UserMetadataEntityCompanion required String userId, required i2.UserMetadataKey key, required Map value, - }) : userId = i0.Value(userId), - key = i0.Value(key), - value = i0.Value(value); + }) : userId = i0.Value(userId), + key = i0.Value(key), + value = i0.Value(value); static i0.Insertable custom({ i0.Expression? userId, i0.Expression? key, @@ -480,10 +581,11 @@ class UserMetadataEntityCompanion }); } - i1.UserMetadataEntityCompanion copyWith( - {i0.Value? userId, - i0.Value? key, - i0.Value>? value}) { + i1.UserMetadataEntityCompanion copyWith({ + i0.Value? userId, + i0.Value? key, + i0.Value>? value, + }) { return i1.UserMetadataEntityCompanion( userId: userId ?? this.userId, key: key ?? this.key, @@ -499,11 +601,13 @@ class UserMetadataEntityCompanion } if (key.present) { map['key'] = i0.Variable( - i1.$UserMetadataEntityTable.$converterkey.toSql(key.value)); + i1.$UserMetadataEntityTable.$converterkey.toSql(key.value), + ); } if (value.present) { map['value'] = i0.Variable( - i1.$UserMetadataEntityTable.$convertervalue.toSql(value.value)); + i1.$UserMetadataEntityTable.$convertervalue.toSql(value.value), + ); } return map; } diff --git a/mobile/lib/infrastructure/repositories/asset_media.repository.dart b/mobile/lib/infrastructure/repositories/asset_media.repository.dart index e8bf9ace43..6c81c7ff7f 100644 --- a/mobile/lib/infrastructure/repositories/asset_media.repository.dart +++ b/mobile/lib/infrastructure/repositories/asset_media.repository.dart @@ -6,21 +6,13 @@ import 'package:photo_manager/photo_manager.dart'; class AssetMediaRepository { const AssetMediaRepository(); - Future getThumbnail( - String id, { - int quality = 80, - Size size = const Size.square(256), - }) => - AssetEntity( - id: id, - // The below fields are not used in thumbnailDataWithSize but are required - // to create an AssetEntity instance. It is faster to create a dummy AssetEntity - // instance than to fetch the asset from the device first. - typeInt: AssetType.image.index, - width: size.width.toInt(), - height: size.height.toInt(), - ).thumbnailDataWithSize( - ThumbnailSize(size.width.toInt(), size.height.toInt()), - quality: quality, - ); + Future getThumbnail(String id, {int quality = 80, Size size = const Size.square(256)}) => AssetEntity( + id: id, + // The below fields are not used in thumbnailDataWithSize but are required + // to create an AssetEntity instance. It is faster to create a dummy AssetEntity + // instance than to fetch the asset from the device first. + typeInt: AssetType.image.index, + width: size.width.toInt(), + height: size.height.toInt(), + ).thumbnailDataWithSize(ThumbnailSize(size.width.toInt(), size.height.toInt()), quality: quality); } diff --git a/mobile/lib/infrastructure/repositories/backup.repository.dart b/mobile/lib/infrastructure/repositories/backup.repository.dart new file mode 100644 index 0000000000..ce38ff9311 --- /dev/null +++ b/mobile/lib/infrastructure/repositories/backup.repository.dart @@ -0,0 +1,139 @@ +import 'package:drift/drift.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/asset/base_asset.model.dart'; +import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; +import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; +import "package:immich_mobile/utils/database.utils.dart"; + +final backupRepositoryProvider = Provider( + (ref) => DriftBackupRepository(ref.watch(driftProvider)), +); + +class DriftBackupRepository extends DriftDatabaseRepository { + final Drift _db; + const DriftBackupRepository(this._db) : super(_db); + + _getExcludedSubquery() { + return _db.localAlbumAssetEntity.selectOnly() + ..addColumns([_db.localAlbumAssetEntity.assetId]) + ..join([ + innerJoin( + _db.localAlbumEntity, + _db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id), + useColumns: false, + ), + ]) + ..where(_db.localAlbumEntity.backupSelection.equalsValue(BackupSelection.excluded)); + } + + Future getTotalCount() async { + final query = _db.localAlbumAssetEntity.selectOnly(distinct: true) + ..addColumns([_db.localAlbumAssetEntity.assetId]) + ..join([ + innerJoin( + _db.localAlbumEntity, + _db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id), + useColumns: false, + ), + ]) + ..where( + _db.localAlbumEntity.backupSelection.equalsValue(BackupSelection.selected) & + _db.localAlbumAssetEntity.assetId.isNotInQuery(_getExcludedSubquery()), + ); + + return query.get().then((rows) => rows.length); + } + + Future getRemainderCount(String userId) async { + final query = _db.localAlbumAssetEntity.selectOnly(distinct: true) + ..addColumns([_db.localAlbumAssetEntity.assetId]) + ..join([ + innerJoin( + _db.localAlbumEntity, + _db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id), + useColumns: false, + ), + innerJoin( + _db.localAssetEntity, + _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), + useColumns: false, + ), + leftOuterJoin( + _db.remoteAssetEntity, + _db.localAssetEntity.checksum.equalsExp(_db.remoteAssetEntity.checksum) & + _db.remoteAssetEntity.ownerId.equals(userId), + useColumns: false, + ), + ]) + ..where( + _db.localAlbumEntity.backupSelection.equalsValue(BackupSelection.selected) & + _db.remoteAssetEntity.id.isNull() & + _db.localAlbumAssetEntity.assetId.isNotInQuery(_getExcludedSubquery()), + ); + + return query.get().then((rows) => rows.length); + } + + Future getBackupCount(String userId) async { + final query = _db.localAlbumAssetEntity.selectOnly(distinct: true) + ..addColumns([_db.localAlbumAssetEntity.assetId]) + ..join([ + innerJoin( + _db.localAlbumEntity, + _db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id), + useColumns: false, + ), + innerJoin( + _db.localAssetEntity, + _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), + useColumns: false, + ), + innerJoin( + _db.remoteAssetEntity, + _db.localAssetEntity.checksum.equalsExp(_db.remoteAssetEntity.checksum), + useColumns: false, + ), + ]) + ..where( + _db.localAlbumEntity.backupSelection.equalsValue(BackupSelection.selected) & + _db.remoteAssetEntity.id.isNotNull() & + _db.remoteAssetEntity.ownerId.equals(userId) & + _db.localAlbumAssetEntity.assetId.isNotInQuery(_getExcludedSubquery()), + ); + + return query.get().then((rows) => rows.length); + } + + Future> getCandidates(String userId) async { + final selectedAlbumIds = _db.localAlbumEntity.selectOnly(distinct: true) + ..addColumns([_db.localAlbumEntity.id]) + ..where(_db.localAlbumEntity.backupSelection.equalsValue(BackupSelection.selected)); + + final query = _db.localAssetEntity.select() + ..where( + (lae) => + existsQuery( + _db.localAlbumAssetEntity.selectOnly() + ..addColumns([_db.localAlbumAssetEntity.assetId]) + ..where( + _db.localAlbumAssetEntity.albumId.isInQuery(selectedAlbumIds) & + _db.localAlbumAssetEntity.assetId.equalsExp(lae.id), + ), + ) & + notExistsQuery( + _db.remoteAssetEntity.selectOnly() + ..addColumns([_db.remoteAssetEntity.checksum]) + ..where( + _db.remoteAssetEntity.checksum.equalsExp(lae.checksum) & + _db.remoteAssetEntity.ownerId.equals(userId) & + lae.checksum.isNotNull(), + ), + ) & + lae.id.isNotInQuery(_getExcludedSubquery()), + ) + ..orderBy([(localAsset) => OrderingTerm.desc(localAsset.createdAt)]); + + return query.map((localAsset) => localAsset.toDto()).get(); + } +} diff --git a/mobile/lib/infrastructure/repositories/db.repository.dart b/mobile/lib/infrastructure/repositories/db.repository.dart index 200ec47516..0458a5b254 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.dart @@ -4,6 +4,7 @@ import 'package:drift/drift.dart'; import 'package:drift_flutter/drift_flutter.dart'; import 'package:flutter/foundation.dart'; import 'package:immich_mobile/domain/interfaces/db.interface.dart'; +import 'package:immich_mobile/infrastructure/entities/asset_face.entity.dart'; import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.dart'; @@ -11,6 +12,7 @@ import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/memory.entity.dart'; import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/partner.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/person.entity.dart'; import 'package:immich_mobile/infrastructure/entities/remote_album.entity.dart'; import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.dart'; @@ -54,58 +56,80 @@ class IsarDatabaseRepository implements IDatabaseRepository { MemoryEntity, MemoryAssetEntity, StackEntity, + PersonEntity, + AssetFaceEntity, ], - include: { - 'package:immich_mobile/infrastructure/entities/merged_asset.drift', - }, + include: {'package:immich_mobile/infrastructure/entities/merged_asset.drift'}, ) class Drift extends $Drift implements IDatabaseRepository { Drift([QueryExecutor? executor]) - : super( - executor ?? - driftDatabase( - name: 'immich', - native: const DriftNativeOptions(shareAcrossIsolates: true), - ), - ); + : super(executor ?? driftDatabase(name: 'immich', native: const DriftNativeOptions(shareAcrossIsolates: true))); @override - int get schemaVersion => 2; + int get schemaVersion => 6; @override MigrationStrategy get migration => MigrationStrategy( - onUpgrade: (m, from, to) async { - // Run migration steps without foreign keys and re-enable them later - await customStatement('PRAGMA foreign_keys = OFF'); + onUpgrade: (m, from, to) async { + // Run migration steps without foreign keys and re-enable them later + await customStatement('PRAGMA foreign_keys = OFF'); - await m.runMigrationSteps( - from: from, - to: to, - steps: migrationSteps( - from1To2: (m, _) async { - for (final entity in allSchemaEntities) { - await m.drop(entity); - await m.create(entity); - } - }, - ), - ); - - if (kDebugMode) { - // Fail if the migration broke foreign keys - final wrongFKs = - await customSelect('PRAGMA foreign_key_check').get(); - assert(wrongFKs.isEmpty, '${wrongFKs.map((e) => e.data)}'); - } - - await customStatement('PRAGMA foreign_keys = ON;'); - }, - beforeOpen: (details) async { - await customStatement('PRAGMA foreign_keys = ON'); - await customStatement('PRAGMA synchronous = NORMAL'); - await customStatement('PRAGMA journal_mode = WAL'); - }, + await m.runMigrationSteps( + from: from, + to: to, + steps: migrationSteps( + from1To2: (m, v2) async { + for (final entity in v2.entities) { + await m.drop(entity); + await m.create(entity); + } + }, + from2To3: (m, v3) async { + // Removed foreign key constraint on stack.primaryAssetId + await m.alterTable(TableMigration(v3.stackEntity)); + }, + from3To4: (m, v4) async { + // Thumbnail path column got removed from person_entity + await m.alterTable(TableMigration(v4.personEntity)); + // asset_face_entity is added + await m.create(v4.assetFaceEntity); + }, + from4To5: (m, v5) async { + await m.alterTable( + TableMigration( + v5.userEntity, + newColumns: [v5.userEntity.hasProfileImage, v5.userEntity.profileChangedAt], + columnTransformer: {v5.userEntity.profileChangedAt: currentDateAndTime}, + ), + ); + }, + from5To6: (m, v6) async { + // Drops the (checksum, ownerId) and adds it back as (ownerId, checksum) + await customStatement('DROP INDEX IF EXISTS UQ_remote_asset_owner_checksum'); + await m.create(v6.idxRemoteAssetOwnerChecksum); + // Adds libraryId to remote_asset_entity + await m.addColumn(v6.remoteAssetEntity, v6.remoteAssetEntity.libraryId); + await m.create(v6.uQRemoteAssetsOwnerChecksum); + await m.create(v6.uQRemoteAssetsOwnerLibraryChecksum); + }, + ), ); + + if (kDebugMode) { + // Fail if the migration broke foreign keys + final wrongFKs = await customSelect('PRAGMA foreign_key_check').get(); + assert(wrongFKs.isEmpty, '${wrongFKs.map((e) => e.data)}'); + } + + await customStatement('PRAGMA foreign_keys = ON;'); + }, + beforeOpen: (details) async { + await customStatement('PRAGMA foreign_keys = ON'); + await customStatement('PRAGMA synchronous = NORMAL'); + await customStatement('PRAGMA journal_mode = WAL'); + await customStatement('PRAGMA busy_timeout = 500'); + }, + ); } class DriftDatabaseRepository implements IDatabaseRepository { @@ -113,6 +137,5 @@ class DriftDatabaseRepository implements IDatabaseRepository { const DriftDatabaseRepository(this._db); @override - Future transaction(Future Function() callback) => - _db.transaction(callback); + Future transaction(Future Function() callback) => _db.transaction(callback); } diff --git a/mobile/lib/infrastructure/repositories/db.repository.drift.dart b/mobile/lib/infrastructure/repositories/db.repository.drift.dart index 3b826c209b..2b7203eb46 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.drift.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.drift.dart @@ -5,17 +5,17 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' as i1; import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart' as i2; -import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart' - as i3; import 'package:immich_mobile/infrastructure/entities/stack.entity.drift.dart' + as i3; +import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart' as i4; -import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.drift.dart' - as i5; -import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart' - as i6; import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart' - as i7; + as i5; import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.drift.dart' + as i6; +import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.drift.dart' + as i7; +import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart' as i8; import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart' as i9; @@ -29,195 +29,246 @@ import 'package:immich_mobile/infrastructure/entities/memory.entity.drift.dart' as i13; import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.drift.dart' as i14; -import 'package:immich_mobile/infrastructure/entities/merged_asset.drift.dart' +import 'package:immich_mobile/infrastructure/entities/person.entity.drift.dart' as i15; -import 'package:drift/internal/modular.dart' as i16; +import 'package:immich_mobile/infrastructure/entities/asset_face.entity.drift.dart' + as i16; +import 'package:immich_mobile/infrastructure/entities/merged_asset.drift.dart' + as i17; +import 'package:drift/internal/modular.dart' as i18; abstract class $Drift extends i0.GeneratedDatabase { $Drift(i0.QueryExecutor e) : super(e); $DriftManager get managers => $DriftManager(this); late final i1.$UserEntityTable userEntity = i1.$UserEntityTable(this); - late final i2.$RemoteAssetEntityTable remoteAssetEntity = - i2.$RemoteAssetEntityTable(this); - late final i3.$LocalAssetEntityTable localAssetEntity = - i3.$LocalAssetEntityTable(this); - late final i4.$StackEntityTable stackEntity = i4.$StackEntityTable(this); - late final i5.$UserMetadataEntityTable userMetadataEntity = - i5.$UserMetadataEntityTable(this); - late final i6.$PartnerEntityTable partnerEntity = - i6.$PartnerEntityTable(this); - late final i7.$LocalAlbumEntityTable localAlbumEntity = - i7.$LocalAlbumEntityTable(this); - late final i8.$LocalAlbumAssetEntityTable localAlbumAssetEntity = - i8.$LocalAlbumAssetEntityTable(this); - late final i9.$RemoteExifEntityTable remoteExifEntity = - i9.$RemoteExifEntityTable(this); - late final i10.$RemoteAlbumEntityTable remoteAlbumEntity = - i10.$RemoteAlbumEntityTable(this); - late final i11.$RemoteAlbumAssetEntityTable remoteAlbumAssetEntity = - i11.$RemoteAlbumAssetEntityTable(this); - late final i12.$RemoteAlbumUserEntityTable remoteAlbumUserEntity = - i12.$RemoteAlbumUserEntityTable(this); + late final i2.$RemoteAssetEntityTable remoteAssetEntity = i2 + .$RemoteAssetEntityTable(this); + late final i3.$StackEntityTable stackEntity = i3.$StackEntityTable(this); + late final i4.$LocalAssetEntityTable localAssetEntity = i4 + .$LocalAssetEntityTable(this); + late final i5.$LocalAlbumEntityTable localAlbumEntity = i5 + .$LocalAlbumEntityTable(this); + late final i6.$LocalAlbumAssetEntityTable localAlbumAssetEntity = i6 + .$LocalAlbumAssetEntityTable(this); + late final i7.$UserMetadataEntityTable userMetadataEntity = i7 + .$UserMetadataEntityTable(this); + late final i8.$PartnerEntityTable partnerEntity = i8.$PartnerEntityTable( + this, + ); + late final i9.$RemoteExifEntityTable remoteExifEntity = i9 + .$RemoteExifEntityTable(this); + late final i10.$RemoteAlbumEntityTable remoteAlbumEntity = i10 + .$RemoteAlbumEntityTable(this); + late final i11.$RemoteAlbumAssetEntityTable remoteAlbumAssetEntity = i11 + .$RemoteAlbumAssetEntityTable(this); + late final i12.$RemoteAlbumUserEntityTable remoteAlbumUserEntity = i12 + .$RemoteAlbumUserEntityTable(this); late final i13.$MemoryEntityTable memoryEntity = i13.$MemoryEntityTable(this); - late final i14.$MemoryAssetEntityTable memoryAssetEntity = - i14.$MemoryAssetEntityTable(this); - i15.MergedAssetDrift get mergedAssetDrift => i16.ReadDatabaseContainer(this) - .accessor(i15.MergedAssetDrift.new); + late final i14.$MemoryAssetEntityTable memoryAssetEntity = i14 + .$MemoryAssetEntityTable(this); + late final i15.$PersonEntityTable personEntity = i15.$PersonEntityTable(this); + late final i16.$AssetFaceEntityTable assetFaceEntity = i16 + .$AssetFaceEntityTable(this); + i17.MergedAssetDrift get mergedAssetDrift => i18.ReadDatabaseContainer( + this, + ).accessor(i17.MergedAssetDrift.new); @override Iterable> get allTables => allSchemaEntities.whereType>(); @override List get allSchemaEntities => [ - userEntity, - remoteAssetEntity, - localAssetEntity, - stackEntity, - i3.idxLocalAssetChecksum, - i2.uQRemoteAssetOwnerChecksum, - i2.idxRemoteAssetChecksum, - userMetadataEntity, - partnerEntity, - localAlbumEntity, - localAlbumAssetEntity, - remoteExifEntity, - remoteAlbumEntity, - remoteAlbumAssetEntity, - remoteAlbumUserEntity, - memoryEntity, - memoryAssetEntity - ]; + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, + i4.idxLocalAssetChecksum, + i2.idxRemoteAssetOwnerChecksum, + i2.uQRemoteAssetsOwnerChecksum, + i2.uQRemoteAssetsOwnerLibraryChecksum, + i2.idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + ]; @override - i0.StreamQueryUpdateRules get streamUpdateRules => - const i0.StreamQueryUpdateRules( - [ - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('user_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('remote_asset_entity', kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('user_entity', - limitUpdateKind: 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('user_metadata_entity', - kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('user_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('partner_entity', kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('user_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('partner_entity', kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('local_asset_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('local_album_asset_entity', - kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('local_album_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('local_album_asset_entity', - kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('remote_asset_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - 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( - on: i0.TableUpdateQuery.onTableName('remote_asset_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('remote_album_asset_entity', - kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('remote_album_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('remote_album_asset_entity', - kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('remote_album_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('remote_album_user_entity', - kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('user_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('remote_album_user_entity', - kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('user_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('memory_entity', kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('remote_asset_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('memory_asset_entity', kind: i0.UpdateKind.delete), - ], - ), - i0.WritePropagation( - on: i0.TableUpdateQuery.onTableName('memory_entity', - limitUpdateKind: i0.UpdateKind.delete), - result: [ - i0.TableUpdate('memory_asset_entity', kind: i0.UpdateKind.delete), - ], - ), - ], - ); + i0.StreamQueryUpdateRules + get streamUpdateRules => const i0.StreamQueryUpdateRules([ + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('remote_asset_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [i0.TableUpdate('stack_entity', kind: i0.UpdateKind.delete)], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'local_asset_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('local_album_asset_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'local_album_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('local_album_asset_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('user_metadata_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [i0.TableUpdate('partner_entity', kind: i0.UpdateKind.delete)], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [i0.TableUpdate('partner_entity', kind: i0.UpdateKind.delete)], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'remote_asset_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + 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( + on: i0.TableUpdateQuery.onTableName( + 'remote_asset_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('remote_album_asset_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'remote_album_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('remote_album_asset_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'remote_album_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('remote_album_user_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('remote_album_user_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [i0.TableUpdate('memory_entity', kind: i0.UpdateKind.delete)], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'remote_asset_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('memory_asset_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'memory_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [ + i0.TableUpdate('memory_asset_entity', kind: i0.UpdateKind.delete), + ], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'user_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [i0.TableUpdate('person_entity', kind: i0.UpdateKind.delete)], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'remote_asset_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [i0.TableUpdate('asset_face_entity', kind: i0.UpdateKind.delete)], + ), + i0.WritePropagation( + on: i0.TableUpdateQuery.onTableName( + 'person_entity', + limitUpdateKind: i0.UpdateKind.delete, + ), + result: [i0.TableUpdate('asset_face_entity', kind: i0.UpdateKind.update)], + ), + ]); @override i0.DriftDatabaseOptions get options => const i0.DriftDatabaseOptions(storeDateTimeAsText: true); @@ -230,29 +281,35 @@ class $DriftManager { i1.$$UserEntityTableTableManager(_db, _db.userEntity); i2.$$RemoteAssetEntityTableTableManager get remoteAssetEntity => i2.$$RemoteAssetEntityTableTableManager(_db, _db.remoteAssetEntity); - i3.$$LocalAssetEntityTableTableManager get localAssetEntity => - i3.$$LocalAssetEntityTableTableManager(_db, _db.localAssetEntity); - i4.$$StackEntityTableTableManager get stackEntity => - i4.$$StackEntityTableTableManager(_db, _db.stackEntity); - i5.$$UserMetadataEntityTableTableManager get userMetadataEntity => - i5.$$UserMetadataEntityTableTableManager(_db, _db.userMetadataEntity); - i6.$$PartnerEntityTableTableManager get partnerEntity => - i6.$$PartnerEntityTableTableManager(_db, _db.partnerEntity); - i7.$$LocalAlbumEntityTableTableManager get localAlbumEntity => - i7.$$LocalAlbumEntityTableTableManager(_db, _db.localAlbumEntity); - i8.$$LocalAlbumAssetEntityTableTableManager get localAlbumAssetEntity => i8 + i3.$$StackEntityTableTableManager get stackEntity => + i3.$$StackEntityTableTableManager(_db, _db.stackEntity); + i4.$$LocalAssetEntityTableTableManager get localAssetEntity => + i4.$$LocalAssetEntityTableTableManager(_db, _db.localAssetEntity); + i5.$$LocalAlbumEntityTableTableManager get localAlbumEntity => + i5.$$LocalAlbumEntityTableTableManager(_db, _db.localAlbumEntity); + i6.$$LocalAlbumAssetEntityTableTableManager get localAlbumAssetEntity => i6 .$$LocalAlbumAssetEntityTableTableManager(_db, _db.localAlbumAssetEntity); + i7.$$UserMetadataEntityTableTableManager get userMetadataEntity => + i7.$$UserMetadataEntityTableTableManager(_db, _db.userMetadataEntity); + i8.$$PartnerEntityTableTableManager get partnerEntity => + i8.$$PartnerEntityTableTableManager(_db, _db.partnerEntity); i9.$$RemoteExifEntityTableTableManager get remoteExifEntity => i9.$$RemoteExifEntityTableTableManager(_db, _db.remoteExifEntity); i10.$$RemoteAlbumEntityTableTableManager get remoteAlbumEntity => i10.$$RemoteAlbumEntityTableTableManager(_db, _db.remoteAlbumEntity); i11.$$RemoteAlbumAssetEntityTableTableManager get remoteAlbumAssetEntity => i11.$$RemoteAlbumAssetEntityTableTableManager( - _db, _db.remoteAlbumAssetEntity); + _db, + _db.remoteAlbumAssetEntity, + ); i12.$$RemoteAlbumUserEntityTableTableManager get remoteAlbumUserEntity => i12 .$$RemoteAlbumUserEntityTableTableManager(_db, _db.remoteAlbumUserEntity); i13.$$MemoryEntityTableTableManager get memoryEntity => i13.$$MemoryEntityTableTableManager(_db, _db.memoryEntity); i14.$$MemoryAssetEntityTableTableManager get memoryAssetEntity => i14.$$MemoryAssetEntityTableTableManager(_db, _db.memoryAssetEntity); + i15.$$PersonEntityTableTableManager get personEntity => + i15.$$PersonEntityTableTableManager(_db, _db.personEntity); + i16.$$AssetFaceEntityTableTableManager get assetFaceEntity => + i16.$$AssetFaceEntityTableTableManager(_db, _db.assetFaceEntity); } diff --git a/mobile/lib/infrastructure/repositories/db.repository.steps.dart b/mobile/lib/infrastructure/repositories/db.repository.steps.dart index 4b27ce830c..32b309881e 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.steps.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.steps.dart @@ -26,301 +26,289 @@ final class Schema2 extends i0.VersionedSchema { remoteAlbumUserEntity, memoryEntity, memoryAssetEntity, + personEntity, ]; late final Shape0 userEntity = Shape0( - source: i0.VersionedTable( - entityName: 'user_entity', - withoutRowId: true, - isStrict: true, - tableConstraints: [ - 'PRIMARY KEY(id)', - ], - columns: [ - _column_0, - _column_1, - _column_2, - _column_3, - _column_4, - _column_5, - _column_6, - _column_7, - ], - attachedDatabase: database, - ), - alias: null); + source: i0.VersionedTable( + entityName: 'user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_2, + _column_3, + _column_4, + _column_5, + _column_6, + _column_7, + ], + attachedDatabase: database, + ), + alias: null, + ); late final Shape1 remoteAssetEntity = Shape1( - 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, - ], - attachedDatabase: database, - ), - alias: null); + 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, + ], + 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); + 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 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_24, - ], - attachedDatabase: database, - ), - alias: null); - final i1.Index idxLocalAssetChecksum = i1.Index('idx_local_asset_checksum', - 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)'); + source: i0.VersionedTable( + entityName: 'stack_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [_column_0, _column_9, _column_5, _column_15, _column_24], + attachedDatabase: database, + ), + alias: null, + ); + final i1.Index idxLocalAssetChecksum = i1.Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); final i1.Index uQRemoteAssetOwnerChecksum = i1.Index( - 'UQ_remote_asset_owner_checksum', - 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)'); - final i1.Index idxRemoteAssetChecksum = i1.Index('idx_remote_asset_checksum', - 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)'); + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', + ); + final i1.Index idxRemoteAssetChecksum = i1.Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX 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); + 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); + 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 Shape6 localAlbumEntity = Shape6( - 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_33, - ], - attachedDatabase: database, - ), - alias: null); + 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_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); + 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, + ); 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); + 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 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); + 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 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); + 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); + 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); + 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); + 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 Shape13 personEntity = Shape13( + 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_70, + _column_71, + _column_72, + _column_73, + _column_74, + ], + attachedDatabase: database, + ), + alias: null, + ); } class Shape0 extends i0.VersionedTable { @@ -344,33 +332,67 @@ class Shape0 extends i0.VersionedTable { } i1.GeneratedColumn _column_0(String aliasedName) => - i1.GeneratedColumn('id', aliasedName, false, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'id', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_1(String aliasedName) => - i1.GeneratedColumn('name', aliasedName, false, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'name', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_2(String aliasedName) => - i1.GeneratedColumn('is_admin', aliasedName, false, - type: i1.DriftSqlType.bool, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways( - 'CHECK ("is_admin" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + i1.GeneratedColumn( + 'is_admin', + aliasedName, + false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); i1.GeneratedColumn _column_3(String aliasedName) => - i1.GeneratedColumn('email', aliasedName, false, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'email', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_4(String aliasedName) => - i1.GeneratedColumn('profile_image_path', aliasedName, true, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'profile_image_path', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_5(String aliasedName) => - i1.GeneratedColumn('updated_at', aliasedName, false, - type: i1.DriftSqlType.dateTime, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + i1.GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: i1.DriftSqlType.dateTime, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); i1.GeneratedColumn _column_6(String aliasedName) => - i1.GeneratedColumn('quota_size_in_bytes', aliasedName, true, - type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'quota_size_in_bytes', + aliasedName, + true, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_7(String aliasedName) => - i1.GeneratedColumn('quota_usage_in_bytes', aliasedName, false, - type: i1.DriftSqlType.int, defaultValue: const CustomExpression('0')); + i1.GeneratedColumn( + 'quota_usage_in_bytes', + aliasedName, + false, + type: i1.DriftSqlType.int, + defaultValue: const CustomExpression('0'), + ); class Shape1 extends i0.VersionedTable { Shape1({required super.source, required super.alias}) : super.aliased(); @@ -411,53 +433,111 @@ class Shape1 extends i0.VersionedTable { } i1.GeneratedColumn _column_8(String aliasedName) => - i1.GeneratedColumn('type', aliasedName, false, - type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'type', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_9(String aliasedName) => - i1.GeneratedColumn('created_at', aliasedName, false, - type: i1.DriftSqlType.dateTime, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + i1.GeneratedColumn( + 'created_at', + aliasedName, + false, + type: i1.DriftSqlType.dateTime, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); i1.GeneratedColumn _column_10(String aliasedName) => - i1.GeneratedColumn('width', aliasedName, true, - type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'width', + aliasedName, + true, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_11(String aliasedName) => - i1.GeneratedColumn('height', aliasedName, true, - type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'height', + aliasedName, + true, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_12(String aliasedName) => - i1.GeneratedColumn('duration_in_seconds', aliasedName, true, - type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_13(String aliasedName) => - i1.GeneratedColumn('checksum', aliasedName, false, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'checksum', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_14(String aliasedName) => - i1.GeneratedColumn('is_favorite', aliasedName, false, - type: i1.DriftSqlType.bool, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways( - 'CHECK ("is_favorite" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + i1.GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); i1.GeneratedColumn _column_15(String aliasedName) => - i1.GeneratedColumn('owner_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + i1.GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); i1.GeneratedColumn _column_16(String aliasedName) => - i1.GeneratedColumn('local_date_time', aliasedName, true, - type: i1.DriftSqlType.dateTime); + i1.GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: i1.DriftSqlType.dateTime, + ); i1.GeneratedColumn _column_17(String aliasedName) => - i1.GeneratedColumn('thumb_hash', aliasedName, true, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'thumb_hash', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_18(String aliasedName) => - i1.GeneratedColumn('deleted_at', aliasedName, true, - type: i1.DriftSqlType.dateTime); + i1.GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: i1.DriftSqlType.dateTime, + ); i1.GeneratedColumn _column_19(String aliasedName) => - i1.GeneratedColumn('live_photo_video_id', aliasedName, true, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'live_photo_video_id', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_20(String aliasedName) => - i1.GeneratedColumn('visibility', aliasedName, false, - type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'visibility', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_21(String aliasedName) => - i1.GeneratedColumn('stack_id', aliasedName, true, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'stack_id', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); class Shape2 extends i0.VersionedTable { Shape2({required super.source, required super.alias}) : super.aliased(); @@ -486,11 +566,20 @@ class Shape2 extends i0.VersionedTable { } i1.GeneratedColumn _column_22(String aliasedName) => - i1.GeneratedColumn('checksum', aliasedName, true, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'checksum', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_23(String aliasedName) => - i1.GeneratedColumn('orientation', aliasedName, false, - type: i1.DriftSqlType.int, defaultValue: const CustomExpression('0')); + i1.GeneratedColumn( + 'orientation', + aliasedName, + false, + type: i1.DriftSqlType.int, + defaultValue: const CustomExpression('0'), + ); class Shape3 extends i0.VersionedTable { Shape3({required super.source, required super.alias}) : super.aliased(); @@ -507,10 +596,15 @@ class Shape3 extends i0.VersionedTable { } i1.GeneratedColumn _column_24(String aliasedName) => - i1.GeneratedColumn('primary_asset_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id)')); + i1.GeneratedColumn( + 'primary_asset_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id)', + ), + ); class Shape4 extends i0.VersionedTable { Shape4({required super.source, required super.alias}) : super.aliased(); @@ -523,16 +617,29 @@ class Shape4 extends i0.VersionedTable { } i1.GeneratedColumn _column_25(String aliasedName) => - i1.GeneratedColumn('user_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + i1.GeneratedColumn( + 'user_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); i1.GeneratedColumn _column_26(String aliasedName) => - i1.GeneratedColumn('key', aliasedName, false, - type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'key', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_27(String aliasedName) => - i1.GeneratedColumn('value', aliasedName, false, - type: i1.DriftSqlType.blob); + i1.GeneratedColumn( + 'value', + aliasedName, + false, + type: i1.DriftSqlType.blob, + ); class Shape5 extends i0.VersionedTable { Shape5({required super.source, required super.alias}) : super.aliased(); @@ -545,21 +652,36 @@ class Shape5 extends i0.VersionedTable { } i1.GeneratedColumn _column_28(String aliasedName) => - i1.GeneratedColumn('shared_by_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + i1.GeneratedColumn( + 'shared_by_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); i1.GeneratedColumn _column_29(String aliasedName) => - i1.GeneratedColumn('shared_with_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + i1.GeneratedColumn( + 'shared_with_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); i1.GeneratedColumn _column_30(String aliasedName) => - i1.GeneratedColumn('in_timeline', aliasedName, false, - type: i1.DriftSqlType.bool, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways( - 'CHECK ("in_timeline" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + i1.GeneratedColumn( + 'in_timeline', + aliasedName, + false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); class Shape6 extends i0.VersionedTable { Shape6({required super.source, required super.alias}) : super.aliased(); @@ -578,19 +700,33 @@ class Shape6 extends i0.VersionedTable { } i1.GeneratedColumn _column_31(String aliasedName) => - i1.GeneratedColumn('backup_selection', aliasedName, false, - type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'backup_selection', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_32(String aliasedName) => - i1.GeneratedColumn('is_ios_shared_album', aliasedName, false, - type: i1.DriftSqlType.bool, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways( - 'CHECK ("is_ios_shared_album" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + i1.GeneratedColumn( + 'is_ios_shared_album', + aliasedName, + false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); i1.GeneratedColumn _column_33(String aliasedName) => - i1.GeneratedColumn('marker', aliasedName, true, - type: i1.DriftSqlType.bool, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways( - 'CHECK ("marker" IN (0, 1))')); + i1.GeneratedColumn( + 'marker', + aliasedName, + true, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); class Shape7 extends i0.VersionedTable { Shape7({required super.source, required super.alias}) : super.aliased(); @@ -601,15 +737,25 @@ class Shape7 extends i0.VersionedTable { } i1.GeneratedColumn _column_34(String aliasedName) => - i1.GeneratedColumn('asset_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways( - 'REFERENCES local_asset_entity (id) ON DELETE CASCADE')); + i1.GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); i1.GeneratedColumn _column_35(String aliasedName) => - i1.GeneratedColumn('album_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways( - 'REFERENCES local_album_entity (id) ON DELETE CASCADE')); + i1.GeneratedColumn( + 'album_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); class Shape8 extends i0.VersionedTable { Shape8({required super.source, required super.alias}) : super.aliased(); @@ -660,67 +806,148 @@ class Shape8 extends i0.VersionedTable { } i1.GeneratedColumn _column_36(String aliasedName) => - i1.GeneratedColumn('asset_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); + i1.GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); i1.GeneratedColumn _column_37(String aliasedName) => - i1.GeneratedColumn('city', aliasedName, true, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'city', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_38(String aliasedName) => - i1.GeneratedColumn('state', aliasedName, true, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'state', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_39(String aliasedName) => - i1.GeneratedColumn('country', aliasedName, true, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'country', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_40(String aliasedName) => - i1.GeneratedColumn('date_time_original', aliasedName, true, - type: i1.DriftSqlType.dateTime); + i1.GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: i1.DriftSqlType.dateTime, + ); i1.GeneratedColumn _column_41(String aliasedName) => - i1.GeneratedColumn('description', aliasedName, true, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'description', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_42(String aliasedName) => - i1.GeneratedColumn('exposure_time', aliasedName, true, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'exposure_time', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_43(String aliasedName) => - i1.GeneratedColumn('f_number', aliasedName, true, - type: i1.DriftSqlType.double); + i1.GeneratedColumn( + 'f_number', + aliasedName, + true, + type: i1.DriftSqlType.double, + ); i1.GeneratedColumn _column_44(String aliasedName) => - i1.GeneratedColumn('file_size', aliasedName, true, - type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'file_size', + aliasedName, + true, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_45(String aliasedName) => - i1.GeneratedColumn('focal_length', aliasedName, true, - type: i1.DriftSqlType.double); + i1.GeneratedColumn( + 'focal_length', + aliasedName, + true, + type: i1.DriftSqlType.double, + ); i1.GeneratedColumn _column_46(String aliasedName) => - i1.GeneratedColumn('latitude', aliasedName, true, - type: i1.DriftSqlType.double); + i1.GeneratedColumn( + 'latitude', + aliasedName, + true, + type: i1.DriftSqlType.double, + ); i1.GeneratedColumn _column_47(String aliasedName) => - i1.GeneratedColumn('longitude', aliasedName, true, - type: i1.DriftSqlType.double); + i1.GeneratedColumn( + 'longitude', + aliasedName, + true, + type: i1.DriftSqlType.double, + ); i1.GeneratedColumn _column_48(String aliasedName) => - i1.GeneratedColumn('iso', aliasedName, true, - type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'iso', + aliasedName, + true, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_49(String aliasedName) => - i1.GeneratedColumn('make', aliasedName, true, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'make', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_50(String aliasedName) => - i1.GeneratedColumn('model', aliasedName, true, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'model', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_51(String aliasedName) => - i1.GeneratedColumn('lens', aliasedName, true, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'lens', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_52(String aliasedName) => - i1.GeneratedColumn('orientation', aliasedName, true, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'orientation', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_53(String aliasedName) => - i1.GeneratedColumn('time_zone', aliasedName, true, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'time_zone', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_54(String aliasedName) => - i1.GeneratedColumn('rating', aliasedName, true, - type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'rating', + aliasedName, + true, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_55(String aliasedName) => - i1.GeneratedColumn('projection_type', aliasedName, true, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'projection_type', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); class Shape9 extends i0.VersionedTable { Shape9({required super.source, required super.alias}) : super.aliased(); @@ -745,28 +972,51 @@ class Shape9 extends i0.VersionedTable { } i1.GeneratedColumn _column_56(String aliasedName) => - i1.GeneratedColumn('description', aliasedName, false, - type: i1.DriftSqlType.string, - defaultValue: const CustomExpression('\'\'')); + i1.GeneratedColumn( + 'description', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultValue: const CustomExpression('\'\''), + ); i1.GeneratedColumn _column_57(String aliasedName) => - i1.GeneratedColumn('thumbnail_asset_id', aliasedName, true, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL')); + i1.GeneratedColumn( + 'thumbnail_asset_id', + aliasedName, + true, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); i1.GeneratedColumn _column_58(String aliasedName) => - i1.GeneratedColumn('is_activity_enabled', aliasedName, false, - type: i1.DriftSqlType.bool, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways( - 'CHECK ("is_activity_enabled" IN (0, 1))'), - defaultValue: const CustomExpression('1')); + i1.GeneratedColumn( + 'is_activity_enabled', + aliasedName, + false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const CustomExpression('1'), + ); i1.GeneratedColumn _column_59(String aliasedName) => - i1.GeneratedColumn('order', aliasedName, false, - type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'order', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); i1.GeneratedColumn _column_60(String aliasedName) => - i1.GeneratedColumn('album_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_album_entity (id) ON DELETE CASCADE')); + i1.GeneratedColumn( + 'album_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); class Shape10 extends i0.VersionedTable { Shape10({required super.source, required super.alias}) : super.aliased(); @@ -779,8 +1029,12 @@ class Shape10 extends i0.VersionedTable { } i1.GeneratedColumn _column_61(String aliasedName) => - i1.GeneratedColumn('role', aliasedName, false, - type: i1.DriftSqlType.int); + i1.GeneratedColumn( + 'role', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); class Shape11 extends i0.VersionedTable { Shape11({required super.source, required super.alias}) : super.aliased(); @@ -811,26 +1065,51 @@ class Shape11 extends i0.VersionedTable { } i1.GeneratedColumn _column_62(String aliasedName) => - i1.GeneratedColumn('data', aliasedName, false, - type: i1.DriftSqlType.string); + i1.GeneratedColumn( + 'data', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); i1.GeneratedColumn _column_63(String aliasedName) => - i1.GeneratedColumn('is_saved', aliasedName, false, - type: i1.DriftSqlType.bool, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways( - 'CHECK ("is_saved" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + i1.GeneratedColumn( + 'is_saved', + aliasedName, + false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); i1.GeneratedColumn _column_64(String aliasedName) => - i1.GeneratedColumn('memory_at', aliasedName, false, - type: i1.DriftSqlType.dateTime); + i1.GeneratedColumn( + 'memory_at', + aliasedName, + false, + type: i1.DriftSqlType.dateTime, + ); i1.GeneratedColumn _column_65(String aliasedName) => - i1.GeneratedColumn('seen_at', aliasedName, true, - type: i1.DriftSqlType.dateTime); + i1.GeneratedColumn( + 'seen_at', + aliasedName, + true, + type: i1.DriftSqlType.dateTime, + ); i1.GeneratedColumn _column_66(String aliasedName) => - i1.GeneratedColumn('show_at', aliasedName, true, - type: i1.DriftSqlType.dateTime); + i1.GeneratedColumn( + 'show_at', + aliasedName, + true, + type: i1.DriftSqlType.dateTime, + ); i1.GeneratedColumn _column_67(String aliasedName) => - i1.GeneratedColumn('hide_at', aliasedName, true, - type: i1.DriftSqlType.dateTime); + i1.GeneratedColumn( + 'hide_at', + aliasedName, + true, + type: i1.DriftSqlType.dateTime, + ); class Shape12 extends i0.VersionedTable { Shape12({required super.source, required super.alias}) : super.aliased(); @@ -841,12 +1120,1597 @@ class Shape12 extends i0.VersionedTable { } i1.GeneratedColumn _column_68(String aliasedName) => - i1.GeneratedColumn('memory_id', aliasedName, false, - type: i1.DriftSqlType.string, - defaultConstraints: i1.GeneratedColumn.constraintIsAlways( - 'REFERENCES memory_entity (id) ON DELETE CASCADE')); + i1.GeneratedColumn( + 'memory_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); + +class Shape13 extends i0.VersionedTable { + Shape13({required super.source, required super.alias}) : super.aliased(); + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get createdAt => + columnsByName['created_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get updatedAt => + columnsByName['updated_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get ownerId => + columnsByName['owner_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get name => + columnsByName['name']! as i1.GeneratedColumn; + i1.GeneratedColumn get faceAssetId => + columnsByName['face_asset_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get thumbnailPath => + columnsByName['thumbnail_path']! as i1.GeneratedColumn; + i1.GeneratedColumn get isFavorite => + columnsByName['is_favorite']! as i1.GeneratedColumn; + i1.GeneratedColumn get isHidden => + columnsByName['is_hidden']! as i1.GeneratedColumn; + i1.GeneratedColumn get color => + columnsByName['color']! as i1.GeneratedColumn; + i1.GeneratedColumn get birthDate => + columnsByName['birth_date']! as i1.GeneratedColumn; +} + +i1.GeneratedColumn _column_69(String aliasedName) => + i1.GeneratedColumn( + 'face_asset_id', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); +i1.GeneratedColumn _column_70(String aliasedName) => + i1.GeneratedColumn( + 'thumbnail_path', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); +i1.GeneratedColumn _column_71(String aliasedName) => + i1.GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); +i1.GeneratedColumn _column_72(String aliasedName) => + i1.GeneratedColumn( + 'is_hidden', + aliasedName, + false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); +i1.GeneratedColumn _column_73(String aliasedName) => + i1.GeneratedColumn( + 'color', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); +i1.GeneratedColumn _column_74(String aliasedName) => + i1.GeneratedColumn( + 'birth_date', + aliasedName, + true, + type: i1.DriftSqlType.dateTime, + ); + +final class Schema3 extends i0.VersionedSchema { + Schema3({required super.database}) : super(version: 3); + @override + late final List entities = [ + userEntity, + remoteAssetEntity, + localAssetEntity, + stackEntity, + idxLocalAssetChecksum, + uQRemoteAssetOwnerChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + localAlbumEntity, + localAlbumAssetEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + ]; + late final Shape0 userEntity = Shape0( + source: i0.VersionedTable( + entityName: 'user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_2, + _column_3, + _column_4, + _column_5, + _column_6, + _column_7, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape1 remoteAssetEntity = Shape1( + 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, + ], + 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 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, + ); + final i1.Index idxLocalAssetChecksum = i1.Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + final i1.Index uQRemoteAssetOwnerChecksum = i1.Index( + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', + ); + final i1.Index idxRemoteAssetChecksum = i1.Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX 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 Shape6 localAlbumEntity = Shape6( + 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_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, + ); + 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 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 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 Shape13 personEntity = Shape13( + 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_70, + _column_71, + _column_72, + _column_73, + _column_74, + ], + attachedDatabase: database, + ), + alias: null, + ); +} + +i1.GeneratedColumn _column_75(String aliasedName) => + i1.GeneratedColumn( + 'primary_asset_id', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); + +final class Schema4 extends i0.VersionedSchema { + Schema4({required super.database}) : super(version: 4); + @override + late final List entities = [ + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAssetChecksum, + uQRemoteAssetOwnerChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + ]; + late final Shape0 userEntity = Shape0( + source: i0.VersionedTable( + entityName: 'user_entity', + withoutRowId: true, + isStrict: true, + tableConstraints: ['PRIMARY KEY(id)'], + columns: [ + _column_0, + _column_1, + _column_2, + _column_3, + _column_4, + _column_5, + _column_6, + _column_7, + ], + attachedDatabase: database, + ), + alias: null, + ); + late final Shape1 remoteAssetEntity = Shape1( + 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, + ], + 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 Shape6 localAlbumEntity = Shape6( + 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_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 idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + final i1.Index uQRemoteAssetOwnerChecksum = i1.Index( + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', + ); + final i1.Index idxRemoteAssetChecksum = i1.Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX 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 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 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, + ); +} + +class Shape14 extends i0.VersionedTable { + Shape14({required super.source, required super.alias}) : super.aliased(); + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get createdAt => + columnsByName['created_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get updatedAt => + columnsByName['updated_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get ownerId => + columnsByName['owner_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get name => + columnsByName['name']! as i1.GeneratedColumn; + i1.GeneratedColumn get faceAssetId => + columnsByName['face_asset_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get isFavorite => + columnsByName['is_favorite']! as i1.GeneratedColumn; + i1.GeneratedColumn get isHidden => + columnsByName['is_hidden']! as i1.GeneratedColumn; + i1.GeneratedColumn get color => + columnsByName['color']! as i1.GeneratedColumn; + i1.GeneratedColumn get birthDate => + columnsByName['birth_date']! as i1.GeneratedColumn; +} + +class Shape15 extends i0.VersionedTable { + Shape15({required super.source, required super.alias}) : super.aliased(); + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get assetId => + columnsByName['asset_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get personId => + columnsByName['person_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get imageWidth => + columnsByName['image_width']! as i1.GeneratedColumn; + i1.GeneratedColumn get imageHeight => + columnsByName['image_height']! as i1.GeneratedColumn; + i1.GeneratedColumn get boundingBoxX1 => + columnsByName['bounding_box_x1']! as i1.GeneratedColumn; + i1.GeneratedColumn get boundingBoxY1 => + columnsByName['bounding_box_y1']! as i1.GeneratedColumn; + i1.GeneratedColumn get boundingBoxX2 => + columnsByName['bounding_box_x2']! as i1.GeneratedColumn; + i1.GeneratedColumn get boundingBoxY2 => + columnsByName['bounding_box_y2']! as i1.GeneratedColumn; + i1.GeneratedColumn get sourceType => + columnsByName['source_type']! as i1.GeneratedColumn; +} + +i1.GeneratedColumn _column_76(String aliasedName) => + i1.GeneratedColumn( + 'person_id', + aliasedName, + true, + type: i1.DriftSqlType.string, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'REFERENCES person_entity (id) ON DELETE SET NULL', + ), + ); +i1.GeneratedColumn _column_77(String aliasedName) => + i1.GeneratedColumn( + 'image_width', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); +i1.GeneratedColumn _column_78(String aliasedName) => + i1.GeneratedColumn( + 'image_height', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); +i1.GeneratedColumn _column_79(String aliasedName) => + i1.GeneratedColumn( + 'bounding_box_x1', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); +i1.GeneratedColumn _column_80(String aliasedName) => + i1.GeneratedColumn( + 'bounding_box_y1', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); +i1.GeneratedColumn _column_81(String aliasedName) => + i1.GeneratedColumn( + 'bounding_box_x2', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); +i1.GeneratedColumn _column_82(String aliasedName) => + i1.GeneratedColumn( + 'bounding_box_y2', + aliasedName, + false, + type: i1.DriftSqlType.int, + ); +i1.GeneratedColumn _column_83(String aliasedName) => + i1.GeneratedColumn( + 'source_type', + aliasedName, + false, + type: i1.DriftSqlType.string, + ); + +final class Schema5 extends i0.VersionedSchema { + Schema5({required super.database}) : super(version: 5); + @override + late final List entities = [ + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAssetChecksum, + uQRemoteAssetOwnerChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + ]; + 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 Shape1 remoteAssetEntity = Shape1( + 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, + ], + 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 Shape6 localAlbumEntity = Shape6( + 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_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 idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + final i1.Index uQRemoteAssetOwnerChecksum = i1.Index( + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', + ); + final i1.Index idxRemoteAssetChecksum = i1.Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX 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 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 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, + ); +} + +class Shape16 extends i0.VersionedTable { + Shape16({required super.source, required super.alias}) : super.aliased(); + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get name => + columnsByName['name']! as i1.GeneratedColumn; + i1.GeneratedColumn get isAdmin => + columnsByName['is_admin']! as i1.GeneratedColumn; + i1.GeneratedColumn get email => + columnsByName['email']! as i1.GeneratedColumn; + i1.GeneratedColumn get hasProfileImage => + columnsByName['has_profile_image']! as i1.GeneratedColumn; + i1.GeneratedColumn get profileChangedAt => + columnsByName['profile_changed_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get updatedAt => + columnsByName['updated_at']! as i1.GeneratedColumn; +} + +i1.GeneratedColumn _column_84(String aliasedName) => + i1.GeneratedColumn( + 'has_profile_image', + aliasedName, + false, + type: i1.DriftSqlType.bool, + defaultConstraints: i1.GeneratedColumn.constraintIsAlways( + 'CHECK ("has_profile_image" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); +i1.GeneratedColumn _column_85(String aliasedName) => + i1.GeneratedColumn( + 'profile_changed_at', + aliasedName, + false, + type: i1.DriftSqlType.dateTime, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + +final class Schema6 extends i0.VersionedSchema { + Schema6({required super.database}) : super(version: 6); + @override + late final List entities = [ + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAssetChecksum, + idxRemoteAssetOwnerChecksum, + uQRemoteAssetsOwnerChecksum, + uQRemoteAssetsOwnerLibraryChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + ]; + 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 Shape6 localAlbumEntity = Shape6( + 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_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 idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + final i1.Index idxRemoteAssetOwnerChecksum = i1.Index( + 'idx_remote_asset_owner_checksum', + 'CREATE INDEX 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 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 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 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, + ); +} + +class Shape17 extends i0.VersionedTable { + Shape17({required super.source, required super.alias}) : super.aliased(); + i1.GeneratedColumn get name => + columnsByName['name']! as i1.GeneratedColumn; + i1.GeneratedColumn get type => + columnsByName['type']! as i1.GeneratedColumn; + i1.GeneratedColumn get createdAt => + columnsByName['created_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get updatedAt => + columnsByName['updated_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get width => + columnsByName['width']! as i1.GeneratedColumn; + i1.GeneratedColumn get height => + columnsByName['height']! as i1.GeneratedColumn; + i1.GeneratedColumn get durationInSeconds => + columnsByName['duration_in_seconds']! as i1.GeneratedColumn; + i1.GeneratedColumn get id => + columnsByName['id']! as i1.GeneratedColumn; + i1.GeneratedColumn get checksum => + columnsByName['checksum']! as i1.GeneratedColumn; + i1.GeneratedColumn get isFavorite => + columnsByName['is_favorite']! as i1.GeneratedColumn; + i1.GeneratedColumn get ownerId => + columnsByName['owner_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get localDateTime => + columnsByName['local_date_time']! as i1.GeneratedColumn; + i1.GeneratedColumn get thumbHash => + columnsByName['thumb_hash']! as i1.GeneratedColumn; + i1.GeneratedColumn get deletedAt => + columnsByName['deleted_at']! as i1.GeneratedColumn; + i1.GeneratedColumn get livePhotoVideoId => + columnsByName['live_photo_video_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get visibility => + columnsByName['visibility']! as i1.GeneratedColumn; + i1.GeneratedColumn get stackId => + columnsByName['stack_id']! as i1.GeneratedColumn; + i1.GeneratedColumn get libraryId => + columnsByName['library_id']! as i1.GeneratedColumn; +} + +i1.GeneratedColumn _column_86(String aliasedName) => + i1.GeneratedColumn( + 'library_id', + aliasedName, + true, + type: i1.DriftSqlType.string, + ); i0.MigrationStepWithVersion migrationSteps({ required Future Function(i1.Migrator m, Schema2 schema) from1To2, + required Future Function(i1.Migrator m, Schema3 schema) from2To3, + required Future Function(i1.Migrator m, Schema4 schema) from3To4, + required Future Function(i1.Migrator m, Schema5 schema) from4To5, + required Future Function(i1.Migrator m, Schema6 schema) from5To6, }) { return (currentVersion, database) async { switch (currentVersion) { @@ -855,6 +2719,26 @@ i0.MigrationStepWithVersion migrationSteps({ final migrator = i1.Migrator(database, schema); await from1To2(migrator, schema); return 2; + case 2: + final schema = Schema3(database: database); + final migrator = i1.Migrator(database, schema); + await from2To3(migrator, schema); + return 3; + case 3: + final schema = Schema4(database: database); + final migrator = i1.Migrator(database, schema); + await from3To4(migrator, schema); + return 4; + case 4: + final schema = Schema5(database: database); + final migrator = i1.Migrator(database, schema); + await from4To5(migrator, schema); + return 5; + case 5: + final schema = Schema6(database: database); + final migrator = i1.Migrator(database, schema); + await from5To6(migrator, schema); + return 6; default: throw ArgumentError.value('Unknown migration from $currentVersion'); } @@ -863,8 +2747,16 @@ i0.MigrationStepWithVersion migrationSteps({ i1.OnUpgrade stepByStep({ required Future Function(i1.Migrator m, Schema2 schema) from1To2, -}) => - i0.VersionedSchema.stepByStepHelper( - step: migrationSteps( - from1To2: from1To2, - )); + required Future Function(i1.Migrator m, Schema3 schema) from2To3, + required Future Function(i1.Migrator m, Schema4 schema) from3To4, + required Future Function(i1.Migrator m, Schema5 schema) from4To5, + required Future Function(i1.Migrator m, Schema6 schema) from5To6, +}) => i0.VersionedSchema.stepByStepHelper( + step: migrationSteps( + from1To2: from1To2, + from2To3: from2To3, + from3To4: from3To4, + from4To5: from4To5, + from5To6: from5To6, + ), +); diff --git a/mobile/lib/infrastructure/repositories/device_asset.repository.dart b/mobile/lib/infrastructure/repositories/device_asset.repository.dart index 4e72d4a7f6..73ee148ab3 100644 --- a/mobile/lib/infrastructure/repositories/device_asset.repository.dart +++ b/mobile/lib/infrastructure/repositories/device_asset.repository.dart @@ -24,8 +24,7 @@ class IsarDeviceAssetRepository extends IsarDatabaseRepository { Future updateAll(List assetHash) { return transaction(() async { - await _db.deviceAssetEntitys - .putAll(assetHash.map(DeviceAssetEntity.fromDto).toList()); + await _db.deviceAssetEntitys.putAll(assetHash.map(DeviceAssetEntity.fromDto).toList()); return true; }); } diff --git a/mobile/lib/infrastructure/repositories/exif.repository.dart b/mobile/lib/infrastructure/repositories/exif.repository.dart index 0012e329ca..0ede30680e 100644 --- a/mobile/lib/infrastructure/repositories/exif.repository.dart +++ b/mobile/lib/infrastructure/repositories/exif.repository.dart @@ -1,6 +1,5 @@ import 'package:immich_mobile/domain/models/exif.model.dart'; -import 'package:immich_mobile/infrastructure/entities/exif.entity.dart' - as entity; +import 'package:immich_mobile/infrastructure/entities/exif.entity.dart' as entity; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; import 'package:isar/isar.dart'; @@ -34,9 +33,7 @@ class IsarExifRepository extends IsarDatabaseRepository { Future> updateAll(List exifInfos) { return transaction(() async { - await _db.exifInfos.putAll( - exifInfos.map(entity.ExifInfo.fromDto).toList(), - ); + await _db.exifInfos.putAll(exifInfos.map(entity.ExifInfo.fromDto).toList()); return exifInfos; }); } diff --git a/mobile/lib/infrastructure/repositories/local_album.repository.dart b/mobile/lib/infrastructure/repositories/local_album.repository.dart index 44ebe7f7ca..869d8f0dc8 100644 --- a/mobile/lib/infrastructure/repositories/local_album.repository.dart +++ b/mobile/lib/infrastructure/repositories/local_album.repository.dart @@ -5,16 +5,17 @@ import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.d import 'package:immich_mobile/infrastructure/entities/local_album_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/utils/database.utils.dart'; import 'package:platform/platform.dart'; -enum SortLocalAlbumsBy { id, backupSelection, isIosSharedAlbum } +enum SortLocalAlbumsBy { id, backupSelection, isIosSharedAlbum, name, assetCount } class DriftLocalAlbumRepository extends DriftDatabaseRepository { final Drift _db; final Platform _platform; const DriftLocalAlbumRepository(this._db, {Platform? platform}) - : _platform = platform ?? const LocalPlatform(), - super(_db); + : _platform = platform ?? const LocalPlatform(), + super(_db); Future> getAll({Set sortBy = const {}}) { final assetCount = _db.localAlbumAssetEntity.assetId.count(); @@ -33,47 +34,32 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { if (sortBy.isNotEmpty) { final orderings = []; for (final sort in sortBy) { - orderings.add( - switch (sort) { - SortLocalAlbumsBy.id => OrderingTerm.asc(_db.localAlbumEntity.id), - SortLocalAlbumsBy.backupSelection => - OrderingTerm.asc(_db.localAlbumEntity.backupSelection), - SortLocalAlbumsBy.isIosSharedAlbum => - OrderingTerm.asc(_db.localAlbumEntity.isIosSharedAlbum), - }, - ); + orderings.add(switch (sort) { + SortLocalAlbumsBy.id => OrderingTerm.asc(_db.localAlbumEntity.id), + SortLocalAlbumsBy.backupSelection => OrderingTerm.asc(_db.localAlbumEntity.backupSelection), + SortLocalAlbumsBy.isIosSharedAlbum => OrderingTerm.asc(_db.localAlbumEntity.isIosSharedAlbum), + SortLocalAlbumsBy.name => OrderingTerm.asc(_db.localAlbumEntity.name), + SortLocalAlbumsBy.assetCount => OrderingTerm.desc(assetCount), + }); } query.orderBy(orderings); } - 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 delete(String albumId) => transaction(() async { - // 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 - // That is not the case on Android since asset <-> album has one:one mapping - final assetsToDelete = _platform.isIOS - ? await _getUniqueAssetsInAlbum(albumId) - : await getAssetIds(albumId); - await _deleteAssets(assetsToDelete); + // 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 + // That is not the case on Android since asset <-> album has one:one mapping + final assetsToDelete = _platform.isIOS ? await _getUniqueAssetsInAlbum(albumId) : await getAssetIds(albumId); + await _deleteAssets(assetsToDelete); - // All the other assets that are still associated will be unlinked automatically on-cascade - await _db.managers.localAlbumEntity - .filter((a) => a.id.equals(albumId)) - .delete(); - }); + // All the other assets that are still associated will be unlinked automatically on-cascade + await _db.managers.localAlbumEntity.filter((a) => a.id.equals(albumId)).delete(); + }); - Future syncDeletes( - String albumId, - Iterable assetIdsToKeep, - ) async { + Future syncDeletes(String albumId, Iterable assetIdsToKeep) async { if (assetIdsToKeep.isEmpty) { return Future.value(); } @@ -82,16 +68,9 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { deleteSmt.where((localAsset) { final subQuery = _db.localAlbumAssetEntity.selectOnly() ..addColumns([_db.localAlbumAssetEntity.assetId]) - ..join([ - innerJoin( - _db.localAlbumEntity, - _db.localAlbumAssetEntity.albumId - .equalsExp(_db.localAlbumEntity.id), - ), - ]); + ..join([innerJoin(_db.localAlbumEntity, _db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id))]); subQuery.where( - _db.localAlbumEntity.id.equals(albumId) & - _db.localAlbumAssetEntity.assetId.isNotIn(assetIdsToKeep), + _db.localAlbumEntity.id.equals(albumId) & _db.localAlbumAssetEntity.assetId.isNotIn(assetIdsToKeep), ); return localAsset.id.isInQuery(subQuery); }); @@ -112,17 +91,11 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { ); return _db.transaction(() async { - await _db.localAlbumEntity - .insertOne(companion, onConflict: DoUpdate((_) => companion)); + await _db.localAlbumEntity.insertOne(companion, onConflict: DoUpdate((_) => companion)); if (toUpsert.isNotEmpty) { await _upsertAssets(toUpsert); await _db.localAlbumAssetEntity.insertAll( - toUpsert.map( - (a) => LocalAlbumAssetEntityCompanion.insert( - assetId: a.id, - albumId: localAlbum.id, - ), - ), + toUpsert.map((a) => LocalAlbumAssetEntityCompanion.insert(assetId: a.id, albumId: localAlbum.id)), mode: InsertMode.insertOrIgnore, ); } @@ -132,9 +105,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { Future updateAll(Iterable albums) { return _db.transaction(() async { - await _db.localAlbumEntity - .update() - .write(const LocalAlbumEntityCompanion(marker_: Value(true))); + await _db.localAlbumEntity.update().write(const LocalAlbumEntityCompanion(marker_: Value(true))); await _db.batch((batch) { for (final album in albums) { @@ -150,7 +121,15 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { batch.insert( _db.localAlbumEntity, companion, - onConflict: DoUpdate((_) => companion), + onConflict: DoUpdate( + (old) => LocalAlbumEntityCompanion( + id: companion.id, + name: companion.name, + updatedAt: companion.updatedAt, + isIosSharedAlbum: companion.isIosSharedAlbum, + marker_: companion.marker_, + ), + ), ); } }); @@ -164,11 +143,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { final subQuery = _db.localAlbumAssetEntity.selectOnly() ..addColumns([_db.localAlbumAssetEntity.assetId]) ..join([ - innerJoin( - _db.localAlbumEntity, - _db.localAlbumAssetEntity.albumId - .equalsExp(_db.localAlbumEntity.id), - ), + innerJoin(_db.localAlbumEntity, _db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id)), ]); subQuery.where(_db.localAlbumEntity.marker_.isNotNull()); return localAsset.id.isInQuery(subQuery); @@ -181,28 +156,20 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { } Future> getAssets(String albumId) { - final query = _db.localAlbumAssetEntity.select().join( - [ - innerJoin( - _db.localAssetEntity, - _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), - ), - ], - ) - ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) - ..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)]); - return query - .map((row) => row.readTable(_db.localAssetEntity).toDto()) - .get(); + final query = + _db.localAlbumAssetEntity.select().join([ + innerJoin(_db.localAssetEntity, _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id)), + ]) + ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) + ..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)]); + return query.map((row) => row.readTable(_db.localAssetEntity).toDto()).get(); } Future> getAssetIds(String albumId) { final query = _db.localAlbumAssetEntity.selectOnly() ..addColumns([_db.localAlbumAssetEntity.assetId]) ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)); - return query - .map((row) => row.read(_db.localAlbumAssetEntity.assetId)!) - .get(); + return query.map((row) => row.read(_db.localAlbumAssetEntity.assetId)!).get(); } Future processDelta({ @@ -222,9 +189,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { assetAlbums.cast>().forEach((assetId, albumIds) { batch.deleteWhere( _db.localAlbumAssetEntity, - (f) => - f.albumId.isNotIn(albumIds.cast().nonNulls) & - f.assetId.equals(assetId), + (f) => f.albumId.isNotIn(albumIds.cast().nonNulls) & f.assetId.equals(assetId), ); }); }); @@ -233,11 +198,8 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { batch.insertAll( _db.localAlbumAssetEntity, albumIds.cast().nonNulls.map( - (albumId) => LocalAlbumAssetEntityCompanion.insert( - assetId: assetId, - albumId: albumId, - ), - ), + (albumId) => LocalAlbumAssetEntityCompanion.insert(assetId: assetId, albumId: albumId), + ), onConflict: DoNothing(), ); }); @@ -246,23 +208,14 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { } Future> getAssetsToHash(String albumId) { - final query = _db.localAlbumAssetEntity.select().join( - [ - innerJoin( - _db.localAssetEntity, - _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), - ), - ], - ) - ..where( - _db.localAlbumAssetEntity.albumId.equals(albumId) & - _db.localAssetEntity.checksum.isNull(), - ) - ..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)]); + final query = + _db.localAlbumAssetEntity.select().join([ + innerJoin(_db.localAssetEntity, _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id)), + ]) + ..where(_db.localAlbumAssetEntity.albumId.equals(albumId) & _db.localAssetEntity.checksum.isNull()) + ..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)]); - return query - .map((row) => row.readTable(_db.localAssetEntity).toDto()) - .get(); + return query.map((row) => row.readTable(_db.localAssetEntity).toDto()).get(); } Future _upsertAssets(Iterable localAssets) { @@ -283,14 +236,12 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { id: asset.id, orientation: Value(asset.orientation), checksum: const Value(null), + isFavorite: Value(asset.isFavorite), ); batch.insert<$LocalAssetEntityTable, LocalAssetEntityData>( _db.localAssetEntity, companion, - onConflict: DoUpdate( - (_) => companion, - where: (old) => old.updatedAt.isNotValue(asset.updatedAt), - ), + onConflict: DoUpdate((_) => companion, where: (old) => old.updatedAt.isNotValue(asset.updatedAt)), ); } }); @@ -346,8 +297,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { ..addColumns([assetId]) ..groupBy( [assetId], - having: _db.localAlbumAssetEntity.albumId.count().equals(1) & - _db.localAlbumAssetEntity.albumId.equals(albumId), + having: _db.localAlbumAssetEntity.albumId.count().equals(1) & _db.localAlbumAssetEntity.albumId.equals(albumId), ); return query.map((row) => row.read(assetId)!).get(); @@ -364,47 +314,20 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository { } Future getThumbnail(String albumId) async { - final query = _db.localAlbumAssetEntity.select().join([ - innerJoin( - _db.localAssetEntity, - _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), - ), - ]) - ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) - ..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)]) - ..limit(1); + final query = + _db.localAlbumAssetEntity.select().join([ + innerJoin(_db.localAssetEntity, _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id)), + ]) + ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) + ..orderBy([OrderingTerm.asc(_db.localAssetEntity.id)]) + ..limit(1); - final results = await query - .map((row) => row.readTable(_db.localAssetEntity).toDto()) - .get(); + final results = await query.map((row) => row.readTable(_db.localAssetEntity).toDto()).get(); return results.isNotEmpty ? results.first : null; } -} -extension on LocalAlbumEntityData { - LocalAlbum toDto({int assetCount = 0}) { - return LocalAlbum( - id: id, - name: name, - updatedAt: updatedAt, - assetCount: assetCount, - backupSelection: backupSelection, - ); - } -} - -extension on LocalAssetEntityData { - LocalAsset toDto() { - return LocalAsset( - id: id, - name: name, - checksum: checksum, - type: type, - createdAt: createdAt, - updatedAt: updatedAt, - durationInSeconds: durationInSeconds, - isFavorite: isFavorite, - ); + Future getCount() { + return _db.managers.localAlbumEntity.count(); } } diff --git a/mobile/lib/infrastructure/repositories/local_asset.repository.dart b/mobile/lib/infrastructure/repositories/local_asset.repository.dart index 31a11f7047..58adac30db 100644 --- a/mobile/lib/infrastructure/repositories/local_asset.repository.dart +++ b/mobile/lib/infrastructure/repositories/local_asset.repository.dart @@ -10,22 +10,17 @@ class DriftLocalAssetRepository extends DriftDatabaseRepository { const DriftLocalAssetRepository(this._db) : super(_db); Stream watchAsset(String id) { - final query = _db.localAssetEntity - .select() - .addColumns([_db.remoteAssetEntity.id]).join([ + final query = _db.localAssetEntity.select().addColumns([_db.remoteAssetEntity.id]).join([ leftOuterJoin( _db.remoteAssetEntity, _db.localAssetEntity.checksum.equalsExp(_db.remoteAssetEntity.checksum), useColumns: false, ), - ]) - ..where(_db.localAssetEntity.id.equals(id)); + ])..where(_db.localAssetEntity.id.equals(id)); return query.map((row) { final asset = row.readTable(_db.localAssetEntity).toDto(); - return asset.copyWith( - remoteId: row.read(_db.remoteAssetEntity.id), - ); + return asset.copyWith(remoteId: row.read(_db.remoteAssetEntity.id)); }).watchSingleOrNull(); } @@ -56,4 +51,18 @@ class DriftLocalAssetRepository extends DriftDatabaseRepository { } }); } + + Future getById(String id) { + final query = _db.localAssetEntity.select()..where((lae) => lae.id.equals(id)); + + return query.map((row) => row.toDto()).getSingleOrNull(); + } + + Future getCount() { + return _db.managers.localAssetEntity.count(); + } + + Future getHashedCount() { + return _db.managers.localAssetEntity.filter((e) => e.checksum.isNull().not()).count(); + } } diff --git a/mobile/lib/infrastructure/repositories/log.repository.dart b/mobile/lib/infrastructure/repositories/log.repository.dart index 717900910a..eefe2b0ab0 100644 --- a/mobile/lib/infrastructure/repositories/log.repository.dart +++ b/mobile/lib/infrastructure/repositories/log.repository.dart @@ -13,23 +13,25 @@ class IsarLogRepository extends IsarDatabaseRepository { } Future> getAll() async { - final logs = - await _db.loggerMessages.where().sortByCreatedAtDesc().findAll(); + final logs = await _db.loggerMessages.where().sortByCreatedAtDesc().findAll(); return logs.map((l) => l.toDto()).toList(); } Future insert(LogMessage log) async { final logEntity = LoggerMessage.fromDto(log); - await transaction(() async { - await _db.loggerMessages.put(logEntity); - }); + + try { + await transaction(() => _db.loggerMessages.put(logEntity)); + } catch (e) { + return false; + } + return true; } Future insertAll(Iterable logs) async { await transaction(() async { - final logEntities = - logs.map((log) => LoggerMessage.fromDto(log)).toList(); + final logEntities = logs.map((log) => LoggerMessage.fromDto(log)).toList(); await _db.loggerMessages.putAll(logEntities); }); return true; diff --git a/mobile/lib/infrastructure/repositories/memory.repository.dart b/mobile/lib/infrastructure/repositories/memory.repository.dart index ff5f75c2ac..2a52faf2dd 100644 --- a/mobile/lib/infrastructure/repositories/memory.repository.dart +++ b/mobile/lib/infrastructure/repositories/memory.repository.dart @@ -13,31 +13,21 @@ class DriftMemoryRepository extends DriftDatabaseRepository { final now = DateTime.now(); final localUtc = DateTime.utc(now.year, now.month, now.day, 0, 0, 0); - final query = _db.select(_db.memoryEntity).join([ - leftOuterJoin( - _db.memoryAssetEntity, - _db.memoryAssetEntity.memoryId.equalsExp(_db.memoryEntity.id), - ), - leftOuterJoin( - _db.remoteAssetEntity, - _db.remoteAssetEntity.id.equalsExp(_db.memoryAssetEntity.assetId) & - _db.remoteAssetEntity.deletedAt.isNull() & - _db.remoteAssetEntity.visibility - .equalsValue(AssetVisibility.timeline), - ), - ]) - ..where(_db.memoryEntity.ownerId.equals(ownerId)) - ..where(_db.memoryEntity.deletedAt.isNull()) - ..where( - _db.memoryEntity.showAt.isSmallerOrEqualValue(localUtc), - ) - ..where( - _db.memoryEntity.hideAt.isBiggerOrEqualValue(localUtc), - ) - ..orderBy([ - OrderingTerm.desc(_db.memoryEntity.memoryAt), - OrderingTerm.asc(_db.remoteAssetEntity.createdAt), - ]); + final query = + _db.select(_db.memoryEntity).join([ + leftOuterJoin(_db.memoryAssetEntity, _db.memoryAssetEntity.memoryId.equalsExp(_db.memoryEntity.id)), + leftOuterJoin( + _db.remoteAssetEntity, + _db.remoteAssetEntity.id.equalsExp(_db.memoryAssetEntity.assetId) & + _db.remoteAssetEntity.deletedAt.isNull() & + _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline), + ), + ]) + ..where(_db.memoryEntity.ownerId.equals(ownerId)) + ..where(_db.memoryEntity.deletedAt.isNull()) + ..where(_db.memoryEntity.showAt.isSmallerOrEqualValue(localUtc)) + ..where(_db.memoryEntity.hideAt.isBiggerOrEqualValue(localUtc)) + ..orderBy([OrderingTerm.desc(_db.memoryEntity.memoryAt), OrderingTerm.asc(_db.remoteAssetEntity.createdAt)]); final rows = await query.get(); @@ -58,6 +48,42 @@ class DriftMemoryRepository extends DriftDatabaseRepository { return memoriesMap.values.toList(); } + + Future get(String memoryId) async { + final query = + _db.select(_db.memoryEntity).join([ + leftOuterJoin(_db.memoryAssetEntity, _db.memoryAssetEntity.memoryId.equalsExp(_db.memoryEntity.id)), + leftOuterJoin( + _db.remoteAssetEntity, + _db.remoteAssetEntity.id.equalsExp(_db.memoryAssetEntity.assetId) & + _db.remoteAssetEntity.deletedAt.isNull() & + _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline), + ), + ]) + ..where(_db.memoryEntity.id.equals(memoryId)) + ..where(_db.memoryEntity.deletedAt.isNull()) + ..orderBy([OrderingTerm.desc(_db.memoryEntity.memoryAt), OrderingTerm.asc(_db.remoteAssetEntity.createdAt)]); + + final rows = await query.get(); + + if (rows.isEmpty) { + return null; + } + + final memory = rows.first.readTable(_db.memoryEntity); + final assets = []; + + for (final row in rows) { + final asset = row.readTable(_db.remoteAssetEntity); + assets.add(asset.toDto()); + } + + return memory.toDto().copyWith(assets: assets); + } + + Future getCount() { + return _db.managers.memoryEntity.count(); + } } extension on MemoryEntityData { diff --git a/mobile/lib/infrastructure/repositories/partner.repository.dart b/mobile/lib/infrastructure/repositories/partner.repository.dart new file mode 100644 index 0000000000..b12061ad24 --- /dev/null +++ b/mobile/lib/infrastructure/repositories/partner.repository.dart @@ -0,0 +1,106 @@ +import 'package:drift/drift.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; +import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; + +class DriftPartnerRepository extends DriftDatabaseRepository { + final Drift _db; + const DriftPartnerRepository(this._db) : super(_db); + + Future> getPartners(String userId) { + final query = _db.select(_db.partnerEntity).join([ + innerJoin(_db.userEntity, _db.userEntity.id.equalsExp(_db.partnerEntity.sharedById)), + ])..where(_db.partnerEntity.sharedWithId.equals(userId)); + + return query.map((row) { + final user = row.readTable(_db.userEntity); + final partner = row.readTable(_db.partnerEntity); + return PartnerUserDto(id: user.id, email: user.email, name: user.name, inTimeline: partner.inTimeline); + }).get(); + } + + // Get users who we can share our library with + Future> getAvailablePartners(String currentUserId) { + final query = _db.select(_db.userEntity)..where((row) => row.id.equals(currentUserId).not()); + + return query.map((user) { + return PartnerUserDto(id: user.id, email: user.email, name: user.name, inTimeline: false); + }).get(); + } + + // Get users who are sharing their photos WITH the current user + Future> getSharedWith(String partnerId) { + final query = _db.select(_db.partnerEntity).join([ + innerJoin(_db.userEntity, _db.userEntity.id.equalsExp(_db.partnerEntity.sharedById)), + ])..where(_db.partnerEntity.sharedWithId.equals(partnerId)); + + return query.map((row) { + final user = row.readTable(_db.userEntity); + final partner = row.readTable(_db.partnerEntity); + return PartnerUserDto(id: user.id, email: user.email, name: user.name, inTimeline: partner.inTimeline); + }).get(); + } + + // Get users who the current user is sharing their photos TO + Future> getSharedBy(String userId) { + final query = _db.select(_db.partnerEntity).join([ + innerJoin(_db.userEntity, _db.userEntity.id.equalsExp(_db.partnerEntity.sharedWithId)), + ])..where(_db.partnerEntity.sharedById.equals(userId)); + + return query.map((row) { + final user = row.readTable(_db.userEntity); + final partner = row.readTable(_db.partnerEntity); + return PartnerUserDto(id: user.id, email: user.email, name: user.name, inTimeline: partner.inTimeline); + }).get(); + } + + Future> getAllPartnerIds(String userId) async { + // Get users who are sharing with me (sharedWithId = userId) + final sharingWithMeQuery = _db.select(_db.partnerEntity)..where((tbl) => tbl.sharedWithId.equals(userId)); + final sharingWithMe = await sharingWithMeQuery.map((row) => row.sharedById).get(); + + // Get users who I am sharing with (sharedById = userId) + final sharingWithThemQuery = _db.select(_db.partnerEntity)..where((tbl) => tbl.sharedById.equals(userId)); + final sharingWithThem = await sharingWithThemQuery.map((row) => row.sharedWithId).get(); + + // Combine both lists and remove duplicates + final allPartnerIds = {...sharingWithMe, ...sharingWithThem}.toList(); + return allPartnerIds; + } + + Future getPartner(String partnerId, String userId) { + final query = _db.select(_db.partnerEntity).join([ + innerJoin(_db.userEntity, _db.userEntity.id.equalsExp(_db.partnerEntity.sharedById)), + ])..where(_db.partnerEntity.sharedById.equals(partnerId) & _db.partnerEntity.sharedWithId.equals(userId)); + + return query.map((row) { + final user = row.readTable(_db.userEntity); + final partner = row.readTable(_db.partnerEntity); + return PartnerUserDto(id: user.id, email: user.email, name: user.name, inTimeline: partner.inTimeline); + }).getSingleOrNull(); + } + + Future toggleShowInTimeline(PartnerUserDto partner, String userId) { + return _db.partnerEntity.update().replace( + PartnerEntityCompanion( + sharedById: Value(partner.id), + sharedWithId: Value(userId), + inTimeline: Value(!partner.inTimeline), + ), + ); + } + + Future create(String partnerId, String userId) { + final entity = PartnerEntityCompanion( + sharedById: Value(userId), + sharedWithId: Value(partnerId), + inTimeline: const Value(false), + ); + + return _db.partnerEntity.insertOne(entity); + } + + Future delete(String partnerId, String userId) { + return _db.partnerEntity.deleteWhere((t) => t.sharedById.equals(userId) & t.sharedWithId.equals(partnerId)); + } +} diff --git a/mobile/lib/infrastructure/repositories/people.repository.dart b/mobile/lib/infrastructure/repositories/people.repository.dart new file mode 100644 index 0000000000..9c6ed74636 --- /dev/null +++ b/mobile/lib/infrastructure/repositories/people.repository.dart @@ -0,0 +1,67 @@ +import 'package:drift/drift.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; +import 'package:immich_mobile/infrastructure/entities/person.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; + +class DriftPeopleRepository extends DriftDatabaseRepository { + final Drift _db; + const DriftPeopleRepository(this._db) : super(_db); + + Future> getAssetPeople(String assetId) async { + final query = _db.select(_db.assetFaceEntity).join([ + innerJoin(_db.personEntity, _db.personEntity.id.equalsExp(_db.assetFaceEntity.personId)), + ])..where(_db.assetFaceEntity.assetId.equals(assetId) & _db.personEntity.isHidden.equals(false)); + + return query.map((row) { + final person = row.readTable(_db.personEntity); + return person.toDto(); + }).get(); + } + + Future> getAllPeople() async { + final query = + _db.select(_db.personEntity).join([ + leftOuterJoin(_db.assetFaceEntity, _db.assetFaceEntity.personId.equalsExp(_db.personEntity.id)), + ]) + ..where(_db.personEntity.isHidden.equals(false)) + ..groupBy([_db.personEntity.id]) + ..orderBy([ + OrderingTerm(expression: _db.personEntity.name.equals('').not(), mode: OrderingMode.desc), + OrderingTerm(expression: _db.assetFaceEntity.id.count(), mode: OrderingMode.desc), + ]); + + return query.map((row) { + final person = row.readTable(_db.personEntity); + return person.toDto(); + }).get(); + } + + Future updateName(String personId, String name) { + final query = _db.update(_db.personEntity)..where((row) => row.id.equals(personId)); + + return query.write(PersonEntityCompanion(name: Value(name), updatedAt: Value(DateTime.now()))); + } + + Future updateBirthday(String personId, DateTime birthday) { + final query = _db.update(_db.personEntity)..where((row) => row.id.equals(personId)); + + return query.write(PersonEntityCompanion(birthDate: Value(birthday), updatedAt: Value(DateTime.now()))); + } +} + +extension on PersonEntityData { + DriftPerson toDto() { + return DriftPerson( + id: id, + createdAt: createdAt, + updatedAt: updatedAt, + ownerId: ownerId, + name: name, + faceAssetId: faceAssetId, + isFavorite: isFavorite, + isHidden: isHidden, + color: color, + birthDate: birthDate, + ); + } +} diff --git a/mobile/lib/infrastructure/repositories/remote_album.repository.dart b/mobile/lib/infrastructure/repositories/remote_album.repository.dart index c3c4570559..6bc6a7066d 100644 --- a/mobile/lib/infrastructure/repositories/remote_album.repository.dart +++ b/mobile/lib/infrastructure/repositories/remote_album.repository.dart @@ -16,9 +16,7 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { final Drift _db; const DriftRemoteAlbumRepository(this._db) : super(_db); - Future> getAll({ - Set sortBy = const {SortRemoteAlbumsBy.updatedAt}, - }) { + Future> getAll({Set sortBy = const {SortRemoteAlbumsBy.updatedAt}}) { final assetCount = _db.remoteAlbumAssetEntity.assetId.count(); final query = _db.remoteAlbumEntity.select().join([ @@ -32,11 +30,7 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId), useColumns: false, ), - leftOuterJoin( - _db.userEntity, - _db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId), - useColumns: false, - ), + leftOuterJoin(_db.userEntity, _db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId), useColumns: false), ]); query ..where(_db.remoteAssetEntity.deletedAt.isNull()) @@ -47,31 +41,59 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { if (sortBy.isNotEmpty) { final orderings = []; for (final sort in sortBy) { - orderings.add( - switch (sort) { - SortRemoteAlbumsBy.id => OrderingTerm.asc(_db.remoteAlbumEntity.id), - SortRemoteAlbumsBy.updatedAt => - OrderingTerm.desc(_db.remoteAlbumEntity.updatedAt), - }, - ); + orderings.add(switch (sort) { + SortRemoteAlbumsBy.id => OrderingTerm.asc(_db.remoteAlbumEntity.id), + SortRemoteAlbumsBy.updatedAt => OrderingTerm.desc(_db.remoteAlbumEntity.updatedAt), + }); } query.orderBy(orderings); } return query .map( - (row) => row.readTable(_db.remoteAlbumEntity).toDto( - assetCount: row.read(assetCount) ?? 0, - ownerName: row.read(_db.userEntity.name)!, - ), + (row) => row + .readTable(_db.remoteAlbumEntity) + .toDto(assetCount: row.read(assetCount) ?? 0, ownerName: row.read(_db.userEntity.name)!), ) .get(); } - Future create( - RemoteAlbum album, - List assetIds, - ) async { + Future get(String albumId) { + final assetCount = _db.remoteAlbumAssetEntity.assetId.count(); + + final query = + _db.remoteAlbumEntity.select().join([ + leftOuterJoin( + _db.remoteAlbumAssetEntity, + _db.remoteAlbumAssetEntity.albumId.equalsExp(_db.remoteAlbumEntity.id), + useColumns: false, + ), + leftOuterJoin( + _db.remoteAssetEntity, + _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId), + useColumns: false, + ), + leftOuterJoin( + _db.userEntity, + _db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId), + useColumns: false, + ), + ]) + ..where(_db.remoteAlbumEntity.id.equals(albumId) & _db.remoteAssetEntity.deletedAt.isNull()) + ..addColumns([assetCount]) + ..addColumns([_db.userEntity.name]) + ..groupBy([_db.remoteAlbumEntity.id]); + + return query + .map( + (row) => row + .readTable(_db.remoteAlbumEntity) + .toDto(assetCount: row.read(assetCount) ?? 0, ownerName: row.read(_db.userEntity.name)!), + ) + .getSingleOrNull(); + } + + Future create(RemoteAlbum album, List assetIds) async { await _db.transaction(() async { final entity = RemoteAlbumEntityCompanion( id: Value(album.id), @@ -89,17 +111,11 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { if (assetIds.isNotEmpty) { final albumAssets = assetIds.map( - (assetId) => RemoteAlbumAssetEntityCompanion( - albumId: Value(album.id), - assetId: Value(assetId), - ), + (assetId) => RemoteAlbumAssetEntityCompanion(albumId: Value(album.id), assetId: Value(assetId)), ); await _db.batch((batch) { - batch.insertAll( - _db.remoteAlbumAssetEntity, - albumAssets, - ); + batch.insertAll(_db.remoteAlbumAssetEntity, albumAssets); }); } }); @@ -107,39 +123,30 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { Future update(RemoteAlbum album) async { await _db.remoteAlbumEntity.update().replace( - RemoteAlbumEntityCompanion( - id: Value(album.id), - name: Value(album.name), - ownerId: Value(album.ownerId), - createdAt: Value(album.createdAt), - updatedAt: Value(album.updatedAt), - description: Value(album.description), - thumbnailAssetId: Value(album.thumbnailAssetId), - isActivityEnabled: Value(album.isActivityEnabled), - order: Value(album.order), - ), - ); + RemoteAlbumEntityCompanion( + id: Value(album.id), + name: Value(album.name), + ownerId: Value(album.ownerId), + createdAt: Value(album.createdAt), + updatedAt: Value(album.updatedAt), + description: Value(album.description), + thumbnailAssetId: Value(album.thumbnailAssetId), + isActivityEnabled: Value(album.isActivityEnabled), + order: Value(album.order), + ), + ); } Future removeAssets(String albumId, List assetIds) { - return _db.remoteAlbumAssetEntity.deleteWhere( - (tbl) => tbl.albumId.equals(albumId) & tbl.assetId.isIn(assetIds), - ); + return _db.remoteAlbumAssetEntity.deleteWhere((tbl) => tbl.albumId.equals(albumId) & tbl.assetId.isIn(assetIds)); } FutureOr<(DateTime, DateTime)> getDateRange(String albumId) { final query = _db.remoteAlbumAssetEntity.selectOnly() ..where(_db.remoteAlbumAssetEntity.albumId.equals(albumId)) - ..addColumns([ - _db.remoteAssetEntity.createdAt.min(), - _db.remoteAssetEntity.createdAt.max(), - ]) + ..addColumns([_db.remoteAssetEntity.createdAt.min(), _db.remoteAssetEntity.createdAt.max()]) ..join([ - innerJoin( - _db.remoteAssetEntity, - _db.remoteAssetEntity.id - .equalsExp(_db.remoteAlbumAssetEntity.assetId), - ), + innerJoin(_db.remoteAssetEntity, _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId)), ]); return query.map((row) { @@ -150,9 +157,9 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { } Future> getSharedUsers(String albumId) async { - final albumUserRows = await (_db.select(_db.remoteAlbumUserEntity) - ..where((row) => row.albumId.equals(albumId))) - .get(); + final albumUserRows = await (_db.select( + _db.remoteAlbumUserEntity, + )..where((row) => row.albumId.equals(albumId))).get(); if (albumUserRows.isEmpty) { return []; @@ -166,17 +173,14 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { id: user.id, email: user.email, name: user.name, - profileImagePath: user.profileImagePath?.isEmpty == true - ? null - : user.profileImagePath, isAdmin: user.isAdmin, updatedAt: user.updatedAt, - quotaSizeInBytes: user.quotaSizeInBytes ?? 0, - quotaUsageInBytes: user.quotaUsageInBytes, memoryEnabled: true, inTimeline: false, isPartnerSharedBy: false, isPartnerSharedWith: false, + profileChangedAt: user.profileChangedAt, + hasProfileImage: user.hasProfileImage, ), ) .get(); @@ -184,31 +188,19 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { Future> getAssets(String albumId) { final query = _db.remoteAlbumAssetEntity.select().join([ - innerJoin( - _db.remoteAssetEntity, - _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId), - ), - ]) - ..where(_db.remoteAlbumAssetEntity.albumId.equals(albumId)); + innerJoin(_db.remoteAssetEntity, _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId)), + ])..where(_db.remoteAlbumAssetEntity.albumId.equals(albumId)); - return query - .map((row) => row.readTable(_db.remoteAssetEntity).toDto()) - .get(); + return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get(); } Future addAssets(String albumId, List assetIds) async { final albumAssets = assetIds.map( - (assetId) => RemoteAlbumAssetEntityCompanion( - albumId: Value(albumId), - assetId: Value(assetId), - ), + (assetId) => RemoteAlbumAssetEntityCompanion(albumId: Value(albumId), assetId: Value(assetId)), ); await _db.batch((batch) { - batch.insertAll( - _db.remoteAlbumAssetEntity, - albumAssets, - ); + batch.insertAll(_db.remoteAlbumAssetEntity, albumAssets); }); return assetIds.length; @@ -224,50 +216,48 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository { ); return _db.batch((batch) { - batch.insertAll( - _db.remoteAlbumUserEntity, - albumUsers, - ); + batch.insertAll(_db.remoteAlbumUserEntity, albumUsers); }); } Future deleteAlbum(String albumId) async { return _db.transaction(() async { - await _db.remoteAlbumEntity.deleteWhere( - (table) => table.id.equals(albumId), - ); + await _db.remoteAlbumEntity.deleteWhere((table) => table.id.equals(albumId)); }); } Stream watchAlbum(String albumId) { - final query = _db.remoteAlbumEntity.select().join([ - leftOuterJoin( - _db.remoteAlbumAssetEntity, - _db.remoteAlbumAssetEntity.albumId.equalsExp(_db.remoteAlbumEntity.id), - useColumns: false, - ), - leftOuterJoin( - _db.remoteAssetEntity, - _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId), - useColumns: false, - ), - leftOuterJoin( - _db.userEntity, - _db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId), - useColumns: false, - ), - ]) - ..where(_db.remoteAlbumEntity.id.equals(albumId)) - ..addColumns([_db.userEntity.name]) - ..groupBy([_db.remoteAlbumEntity.id]); + final query = + _db.remoteAlbumEntity.select().join([ + leftOuterJoin( + _db.remoteAlbumAssetEntity, + _db.remoteAlbumAssetEntity.albumId.equalsExp(_db.remoteAlbumEntity.id), + useColumns: false, + ), + leftOuterJoin( + _db.remoteAssetEntity, + _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId), + useColumns: false, + ), + leftOuterJoin( + _db.userEntity, + _db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId), + useColumns: false, + ), + ]) + ..where(_db.remoteAlbumEntity.id.equals(albumId)) + ..addColumns([_db.userEntity.name]) + ..groupBy([_db.remoteAlbumEntity.id]); return query.map((row) { - final album = row.readTable(_db.remoteAlbumEntity).toDto( - ownerName: row.read(_db.userEntity.name)!, - ); + final album = row.readTable(_db.remoteAlbumEntity).toDto(ownerName: row.read(_db.userEntity.name)!); return album; }).watchSingleOrNull(); } + + Future getCount() { + return _db.managers.remoteAlbumEntity.count(); + } } extension on RemoteAlbumEntityData { diff --git a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart index 28c229b46b..44d7cfb6bb 100644 --- a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart +++ b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart @@ -2,8 +2,7 @@ import 'package:drift/drift.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/exif.model.dart'; import 'package:immich_mobile/domain/models/stack.model.dart'; -import 'package:immich_mobile/infrastructure/entities/exif.entity.dart' - hide ExifInfo; +import 'package:immich_mobile/infrastructure/entities/exif.entity.dart' hide ExifInfo; import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart'; @@ -22,8 +21,7 @@ class RemoteAssetRepository extends DriftDatabaseRepository { (row) => _db.remoteAssetEntity.ownerId.equals(userId) & _db.remoteAssetEntity.deletedAt.isNull() & - _db.remoteAssetEntity.visibility - .equalsValue(AssetVisibility.timeline), + _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline), ) ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]) ..limit(10); @@ -31,49 +29,47 @@ class RemoteAssetRepository extends DriftDatabaseRepository { return query.map((row) => row.toDto()).get(); } - Stream watchAsset(String id) { - final stackCountRef = _db.stackEntity.id.count(); - - final query = _db.remoteAssetEntity.select().addColumns([ - _db.localAssetEntity.id, - _db.stackEntity.primaryAssetId, - stackCountRef, - ]).join([ - leftOuterJoin( - _db.localAssetEntity, - _db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum), - useColumns: false, - ), - leftOuterJoin( - _db.stackEntity, - _db.stackEntity.primaryAssetId.equalsExp(_db.remoteAssetEntity.id), - useColumns: false, - ), - leftOuterJoin( - _db.remoteAssetEntity.createAlias('stacked_assets'), - _db.stackEntity.id.equalsExp( - _db.remoteAssetEntity.createAlias('stacked_assets').stackId, - ), - useColumns: false, - ), - ]) - ..where(_db.remoteAssetEntity.id.equals(id)) - ..groupBy([ - _db.remoteAssetEntity.id, - _db.localAssetEntity.id, - _db.stackEntity.primaryAssetId, - ]); + SingleOrNullSelectable _assetSelectable(String id) { + final query = + _db.remoteAssetEntity.select().addColumns([_db.localAssetEntity.id]).join([ + leftOuterJoin( + _db.localAssetEntity, + _db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum), + useColumns: false, + ), + ]) + ..where(_db.remoteAssetEntity.id.equals(id)) + ..limit(1); return query.map((row) { final asset = row.readTable(_db.remoteAssetEntity).toDto(); - final primaryAssetId = row.read(_db.stackEntity.primaryAssetId); - final stackCount = - primaryAssetId == id ? (row.read(stackCountRef) ?? 0) : 0; + return asset.copyWith(localId: row.read(_db.localAssetEntity.id)); + }); + } - return asset.copyWith( - localId: row.read(_db.localAssetEntity.id), - stackCount: stackCount, - ); + Stream watch(String id) { + return _assetSelectable(id).watchSingleOrNull(); + } + + Future get(String id) { + return _assetSelectable(id).getSingleOrNull(); + } + + Stream watchAsset(String id) { + final query = + _db.remoteAssetEntity.select().addColumns([_db.localAssetEntity.id]).join([ + leftOuterJoin( + _db.localAssetEntity, + _db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum), + useColumns: false, + ), + ]) + ..where(_db.remoteAssetEntity.id.equals(id)) + ..limit(1); + + return query.map((row) { + final asset = row.readTable(_db.remoteAssetEntity).toDto(); + return asset.copyWith(localId: row.read(_db.localAssetEntity.id)); }).watchSingleOrNull(); } @@ -83,10 +79,7 @@ class RemoteAssetRepository extends DriftDatabaseRepository { } final query = _db.remoteAssetEntity.select() - ..where( - (row) => - row.stackId.equals(asset.stackId!) & row.id.equals(asset.id).not(), - ) + ..where((row) => row.stackId.equals(asset.stackId!) & row.id.equals(asset.id).not()) ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]); return query.map((row) => row.toDto()).get(); @@ -101,32 +94,26 @@ class RemoteAssetRepository extends DriftDatabaseRepository { Future> getPlaces() { final asset = Subquery( - _db.remoteAssetEntity.select() - ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]), + _db.remoteAssetEntity.select()..orderBy([(row) => OrderingTerm.desc(row.createdAt)]), "asset", ); - final query = asset.selectOnly().join([ - innerJoin( - _db.remoteExifEntity, - _db.remoteExifEntity.assetId - .equalsExp(asset.ref(_db.remoteAssetEntity.id)), - useColumns: false, - ), - ]) - ..addColumns([ - _db.remoteExifEntity.city, - _db.remoteExifEntity.assetId, - ]) - ..where( - _db.remoteExifEntity.city.isNotNull() & - asset.ref(_db.remoteAssetEntity.deletedAt).isNull() & - asset - .ref(_db.remoteAssetEntity.visibility) - .equals(AssetVisibility.timeline.index), - ) - ..groupBy([_db.remoteExifEntity.city]) - ..orderBy([OrderingTerm.asc(_db.remoteExifEntity.city)]); + final query = + asset.selectOnly().join([ + innerJoin( + _db.remoteExifEntity, + _db.remoteExifEntity.assetId.equalsExp(asset.ref(_db.remoteAssetEntity.id)), + useColumns: false, + ), + ]) + ..addColumns([_db.remoteExifEntity.city, _db.remoteExifEntity.assetId]) + ..where( + _db.remoteExifEntity.city.isNotNull() & + asset.ref(_db.remoteAssetEntity.deletedAt).isNull() & + asset.ref(_db.remoteAssetEntity.visibility).equals(AssetVisibility.timeline.index), + ) + ..groupBy([_db.remoteExifEntity.city]) + ..orderBy([OrderingTerm.asc(_db.remoteExifEntity.city)]); return query.map((row) { final assetId = row.read(_db.remoteExifEntity.assetId); @@ -171,6 +158,18 @@ class RemoteAssetRepository extends DriftDatabaseRepository { }); } + Future restoreTrash(List ids) { + return _db.batch((batch) async { + for (final id in ids) { + batch.update( + _db.remoteAssetEntity, + const RemoteAssetEntityCompanion(deletedAt: Value(null)), + where: (e) => e.id.equals(id), + ); + } + }); + } + Future delete(List ids) { return _db.remoteAssetEntity.deleteWhere((row) => row.id.isIn(ids)); } @@ -180,43 +179,48 @@ class RemoteAssetRepository extends DriftDatabaseRepository { for (final id in ids) { batch.update( _db.remoteExifEntity, - RemoteExifEntityCompanion( - latitude: Value(location.latitude), - longitude: Value(location.longitude), - ), + RemoteExifEntityCompanion(latitude: Value(location.latitude), longitude: Value(location.longitude)), where: (e) => e.assetId.equals(id), ); } }); } + Future updateDateTime(List ids, DateTime dateTime) { + return _db.batch((batch) async { + for (final id in ids) { + batch.update( + _db.remoteExifEntity, + RemoteExifEntityCompanion(dateTimeOriginal: Value(dateTime)), + where: (e) => e.assetId.equals(id), + ); + batch.update( + _db.remoteAssetEntity, + RemoteAssetEntityCompanion(createdAt: Value(dateTime)), + where: (e) => e.id.equals(id), + ); + } + }); + } + Future stack(String userId, StackResponse stack) { return _db.transaction(() async { final stackIds = await _db.managers.stackEntity - .filter((row) => row.primaryAssetId.id.isIn(stack.assetIds)) + .filter((row) => row.primaryAssetId.isIn(stack.assetIds)) .map((row) => row.id) .get(); await _db.stackEntity.deleteWhere((row) => row.id.isIn(stackIds)); await _db.batch((batch) { - final companion = StackEntityCompanion( - ownerId: Value(userId), - primaryAssetId: Value(stack.primaryAssetId), - ); + final companion = StackEntityCompanion(ownerId: Value(userId), primaryAssetId: Value(stack.primaryAssetId)); - batch.insert( - _db.stackEntity, - companion.copyWith(id: Value(stack.id)), - onConflict: DoUpdate((_) => companion), - ); + batch.insert(_db.stackEntity, companion.copyWith(id: Value(stack.id)), onConflict: DoUpdate((_) => companion)); for (final assetId in stack.assetIds) { batch.update( _db.remoteAssetEntity, - RemoteAssetEntityCompanion( - stackId: Value(stack.id), - ), + RemoteAssetEntityCompanion(stackId: Value(stack.id)), where: (e) => e.id.equals(assetId), ); } @@ -238,4 +242,14 @@ class RemoteAssetRepository extends DriftDatabaseRepository { }); }); } + + Future updateDescription(String assetId, String description) async { + await (_db.remoteExifEntity.update()..where((row) => row.assetId.equals(assetId))).write( + RemoteExifEntityCompanion(description: Value(description)), + ); + } + + Future getCount() { + return _db.managers.remoteAssetEntity.count(); + } } diff --git a/mobile/lib/infrastructure/repositories/search_api.repository.dart b/mobile/lib/infrastructure/repositories/search_api.repository.dart index 55604b885c..129746120b 100644 --- a/mobile/lib/infrastructure/repositories/search_api.repository.dart +++ b/mobile/lib/infrastructure/repositories/search_api.repository.dart @@ -1,5 +1,4 @@ -import 'package:immich_mobile/domain/models/asset/base_asset.model.dart' - hide AssetVisibility; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart' hide AssetVisibility; import 'package:immich_mobile/infrastructure/repositories/api.repository.dart'; import 'package:immich_mobile/models/search/search_filter.model.dart'; import 'package:openapi/api.dart'; @@ -28,9 +27,7 @@ class SearchApiRepository extends ApiRepository { model: filter.camera.model, takenAfter: filter.date.takenAfter, takenBefore: filter.date.takenBefore, - visibility: filter.display.isArchive - ? AssetVisibility.archive - : AssetVisibility.timeline, + visibility: filter.display.isArchive ? AssetVisibility.archive : AssetVisibility.timeline, isFavorite: filter.display.isFavorite ? true : null, isNotInAlbum: filter.display.isNotInAlbum ? true : null, personIds: filter.people.map((e) => e.id).toList(), @@ -43,23 +40,16 @@ class SearchApiRepository extends ApiRepository { return _api.searchAssets( MetadataSearchDto( - originalFileName: filter.filename != null && filter.filename!.isNotEmpty - ? filter.filename - : null, + originalFileName: filter.filename != null && filter.filename!.isNotEmpty ? filter.filename : null, country: filter.location.country, - description: - filter.description != null && filter.description!.isNotEmpty - ? filter.description - : null, + description: filter.description != null && filter.description!.isNotEmpty ? filter.description : null, state: filter.location.state, city: filter.location.city, make: filter.camera.make, model: filter.camera.model, takenAfter: filter.date.takenAfter, takenBefore: filter.date.takenBefore, - visibility: filter.display.isArchive - ? AssetVisibility.archive - : AssetVisibility.timeline, + visibility: filter.display.isArchive ? AssetVisibility.archive : AssetVisibility.timeline, isFavorite: filter.display.isFavorite ? true : null, isNotInAlbum: filter.display.isNotInAlbum ? true : null, personIds: filter.people.map((e) => e.id).toList(), @@ -76,12 +66,5 @@ class SearchApiRepository extends ApiRepository { String? state, String? make, String? model, - }) => - _api.getSearchSuggestions( - type, - country: country, - state: state, - make: make, - model: model, - ); + }) => _api.getSearchSuggestions(type, country: country, state: state, make: make, model: model); } diff --git a/mobile/lib/infrastructure/repositories/stack.repository.dart b/mobile/lib/infrastructure/repositories/stack.repository.dart index 7f97f3d9ae..28f7496f97 100644 --- a/mobile/lib/infrastructure/repositories/stack.repository.dart +++ b/mobile/lib/infrastructure/repositories/stack.repository.dart @@ -8,8 +8,7 @@ class DriftStackRepository extends DriftDatabaseRepository { const DriftStackRepository(this._db) : super(_db); Future> getAll(String userId) { - final query = _db.stackEntity.select() - ..where((e) => e.ownerId.equals(userId)); + final query = _db.stackEntity.select()..where((e) => e.ownerId.equals(userId)); return query.map((stack) { return stack.toDto(); @@ -19,12 +18,6 @@ class DriftStackRepository extends DriftDatabaseRepository { extension on StackEntityData { Stack toDto() { - return Stack( - id: id, - createdAt: createdAt, - updatedAt: updatedAt, - ownerId: ownerId, - primaryAssetId: primaryAssetId, - ); + return Stack(id: id, createdAt: createdAt, updatedAt: updatedAt, ownerId: ownerId, primaryAssetId: primaryAssetId); } } diff --git a/mobile/lib/infrastructure/repositories/storage.repository.dart b/mobile/lib/infrastructure/repositories/storage.repository.dart index 5b511709cd..18302aeb7d 100644 --- a/mobile/lib/infrastructure/repositories/storage.repository.dart +++ b/mobile/lib/infrastructure/repositories/storage.repository.dart @@ -1,5 +1,6 @@ import 'dart:io'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:logging/logging.dart'; import 'package:photo_manager/photo_manager.dart'; @@ -7,8 +8,9 @@ class StorageRepository { const StorageRepository(); Future getFileForAsset(String assetId) async { - final log = Logger('StorageRepository'); File? file; + final log = Logger('StorageRepository'); + try { final entity = await AssetEntity.fromId(assetId); file = await entity?.originFile; @@ -20,4 +22,58 @@ class StorageRepository { } return file; } + + Future getMotionFileForAsset(LocalAsset asset) async { + File? file; + final log = Logger('StorageRepository'); + + try { + final entity = await AssetEntity.fromId(asset.id); + file = await entity?.originFileWithSubtype; + if (file == null) { + log.warning( + "Cannot get motion file for asset ${asset.id}, name: ${asset.name}, created on: ${asset.createdAt}", + ); + } + } catch (error, stackTrace) { + log.warning( + "Error getting motion file for asset ${asset.id}, name: ${asset.name}, created on: ${asset.createdAt}", + error, + stackTrace, + ); + } + return file; + } + + Future getAssetEntityForAsset(LocalAsset asset) async { + final log = Logger('StorageRepository'); + + AssetEntity? entity; + + try { + entity = await AssetEntity.fromId(asset.id); + if (entity == null) { + log.warning( + "Cannot get AssetEntity for asset ${asset.id}, name: ${asset.name}, created on: ${asset.createdAt}", + ); + } + } catch (error, stackTrace) { + log.warning( + "Error getting AssetEntity for asset ${asset.id}, name: ${asset.name}, created on: ${asset.createdAt}", + error, + stackTrace, + ); + } + return entity; + } + + Future clearCache() async { + final log = Logger('StorageRepository'); + + try { + await PhotoManager.clearFileCache(); + } catch (error, stackTrace) { + log.warning("Error clearing cache", error, stackTrace); + } + } } diff --git a/mobile/lib/infrastructure/repositories/store.repository.dart b/mobile/lib/infrastructure/repositories/store.repository.dart index 400c8ccf7d..6467767aa2 100644 --- a/mobile/lib/infrastructure/repositories/store.repository.dart +++ b/mobile/lib/infrastructure/repositories/store.repository.dart @@ -23,11 +23,7 @@ class IsarStoreRepository extends IsarDatabaseRepository { .filter() .anyOf(validStoreKeys, (query, id) => query.idEqualTo(id)) .watch(fireImmediately: true) - .asyncExpand( - (entities) => Stream.fromFutures( - entities.map((e) async => _toUpdateEvent(e)), - ), - ); + .asyncExpand((entities) => Stream.fromFutures(entities.map((e) async => _toUpdateEvent(e)))); } Future delete(StoreKey key) async { @@ -63,25 +59,22 @@ class IsarStoreRepository extends IsarDatabaseRepository { } Future> _toUpdateEvent(StoreValue entity) async { - final key = StoreKey.values.firstWhere((e) => e.id == entity.id) - as StoreKey; + final key = StoreKey.values.firstWhere((e) => e.id == entity.id) as StoreKey; final value = await _toValue(key, entity); return StoreDto(key, value); } Future _toValue(StoreKey key, StoreValue entity) async => switch (key.type) { - const (int) => entity.intValue, - const (String) => entity.strValue, - const (bool) => entity.intValue == 1, - const (DateTime) => entity.intValue == null - ? null - : DateTime.fromMillisecondsSinceEpoch(entity.intValue!), - const (UserDto) => entity.strValue == null - ? null - : await IsarUserRepository(_db).getByUserId(entity.strValue!), - _ => null, - } as T?; + const (int) => entity.intValue, + const (String) => entity.strValue, + const (bool) => entity.intValue == 1, + const (DateTime) => entity.intValue == null ? null : DateTime.fromMillisecondsSinceEpoch(entity.intValue!), + const (UserDto) => + entity.strValue == null ? null : await IsarUserRepository(_db).getByUserId(entity.strValue!), + _ => null, + } + as T?; Future _fromValue(StoreKey key, T value) async { final (int? intValue, String? strValue) = switch (key.type) { @@ -89,22 +82,14 @@ class IsarStoreRepository extends IsarDatabaseRepository { const (String) => (null, value as String), const (bool) => ((value as bool) ? 1 : 0, null), const (DateTime) => ((value as DateTime).millisecondsSinceEpoch, null), - const (UserDto) => ( - null, - (await IsarUserRepository(_db).update(value as UserDto)).id, - ), - _ => throw UnsupportedError( - "Unsupported primitive type: ${key.type} for key: ${key.name}", - ), + const (UserDto) => (null, (await IsarUserRepository(_db).update(value as UserDto)).id), + _ => throw UnsupportedError("Unsupported primitive type: ${key.type} for key: ${key.name}"), }; return StoreValue(key.id, intValue: intValue, strValue: strValue); } Future>> getAll() async { - final entities = await _db.storeValues - .filter() - .anyOf(validStoreKeys, (query, id) => query.idEqualTo(id)) - .findAll(); + final entities = await _db.storeValues.filter().anyOf(validStoreKeys, (query, id) => query.idEqualTo(id)).findAll(); return Future.wait(entities.map((e) => _toUpdateEvent(e)).toList()); } } diff --git a/mobile/lib/infrastructure/repositories/sync_api.repository.dart b/mobile/lib/infrastructure/repositories/sync_api.repository.dart index d1ecbd580c..78b2a9d957 100644 --- a/mobile/lib/infrastructure/repositories/sync_api.repository.dart +++ b/mobile/lib/infrastructure/repositories/sync_api.repository.dart @@ -27,10 +27,7 @@ class SyncApiRepository { final client = httpClient ?? http.Client(); final endpoint = "${_api.apiClient.basePath}/sync/stream"; - final headers = { - 'Content-Type': 'application/json', - 'Accept': 'application/jsonlines+json', - }; + final headers = {'Content-Type': 'application/json', 'Accept': 'application/jsonlines+json'}; final headerParams = {}; await _api.applyToParams([], headerParams); @@ -57,6 +54,8 @@ class SyncApiRepository { SyncRequestType.stacksV1, SyncRequestType.partnerStacksV1, SyncRequestType.userMetadataV1, + SyncRequestType.peopleV1, + SyncRequestType.assetFacesV1, ], ).toJson(), ); @@ -76,10 +75,7 @@ class SyncApiRepository { if (response.statusCode != 200) { final errorBody = await response.stream.bytesToString(); - throw ApiException( - response.statusCode, - 'Failed to get sync stream: $errorBody', - ); + throw ApiException(response.statusCode, 'Failed to get sync stream: $errorBody'); } await for (final chunk in response.stream.transform(utf8.decoder)) { @@ -110,8 +106,7 @@ class SyncApiRepository { client.close(); } stopwatch.stop(); - _logger - .info("Remote Sync completed in ${stopwatch.elapsed.inMilliseconds}ms"); + _logger.info("Remote Sync completed in ${stopwatch.elapsed.inMilliseconds}ms"); DLog.log("Remote Sync completed in ${stopwatch.elapsed.inMilliseconds}ms"); } @@ -173,6 +168,10 @@ const _kResponseMap = { SyncEntityType.partnerStackDeleteV1: SyncStackDeleteV1.fromJson, SyncEntityType.userMetadataV1: SyncUserMetadataV1.fromJson, SyncEntityType.userMetadataDeleteV1: SyncUserMetadataDeleteV1.fromJson, + SyncEntityType.personV1: SyncPersonV1.fromJson, + SyncEntityType.personDeleteV1: SyncPersonDeleteV1.fromJson, + SyncEntityType.assetFaceV1: SyncAssetFaceV1.fromJson, + SyncEntityType.assetFaceDeleteV1: SyncAssetFaceDeleteV1.fromJson, }; class _SyncAckV1 { diff --git a/mobile/lib/infrastructure/repositories/sync_stream.repository.dart b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart index d28c68ed78..52ffaabca9 100644 --- a/mobile/lib/infrastructure/repositories/sync_stream.repository.dart +++ b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart @@ -5,10 +5,12 @@ import 'package:immich_mobile/domain/models/album/album.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/memory.model.dart'; import 'package:immich_mobile/domain/models/user_metadata.model.dart'; +import 'package:immich_mobile/infrastructure/entities/asset_face.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/memory.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/entities/person.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.drift.dart'; @@ -18,8 +20,8 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.drift.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; import 'package:logging/logging.dart'; -import 'package:openapi/api.dart' as api show AssetVisibility, AlbumUserRole; -import 'package:openapi/api.dart' hide AssetVisibility, AlbumUserRole; +import 'package:openapi/api.dart' as api show AssetVisibility, AlbumUserRole, UserMetadataKey; +import 'package:openapi/api.dart' hide AssetVisibility, AlbumUserRole, UserMetadataKey; class SyncStreamRepository extends DriftDatabaseRepository { final Logger _logger = Logger('DriftSyncStreamRepository'); @@ -29,8 +31,7 @@ class SyncStreamRepository extends DriftDatabaseRepository { Future deleteUsersV1(Iterable data) async { try { - await _db.userEntity - .deleteWhere((row) => row.id.isIn(data.map((e) => e.userId))); + await _db.userEntity.deleteWhere((row) => row.id.isIn(data.map((e) => e.userId))); } catch (error, stack) { _logger.severe('Error: SyncUserDeleteV1', error, stack); rethrow; @@ -44,13 +45,11 @@ class SyncStreamRepository extends DriftDatabaseRepository { final companion = UserEntityCompanion( name: Value(user.name), email: Value(user.email), + hasProfileImage: Value(user.hasProfileImage), + profileChangedAt: Value(user.profileChangedAt), ); - batch.insert( - _db.userEntity, - companion.copyWith(id: Value(user.id)), - onConflict: DoUpdate((_) => companion), - ); + batch.insert(_db.userEntity, companion.copyWith(id: Value(user.id)), onConflict: DoUpdate((_) => companion)); } }); } catch (error, stack) { @@ -65,10 +64,7 @@ class SyncStreamRepository extends DriftDatabaseRepository { for (final partner in data) { batch.delete( _db.partnerEntity, - PartnerEntityCompanion( - sharedById: Value(partner.sharedById), - sharedWithId: Value(partner.sharedWithId), - ), + PartnerEntityCompanion(sharedById: Value(partner.sharedById), sharedWithId: Value(partner.sharedWithId)), ); } }); @@ -82,15 +78,11 @@ class SyncStreamRepository extends DriftDatabaseRepository { try { await _db.batch((batch) { for (final partner in data) { - final companion = - PartnerEntityCompanion(inTimeline: Value(partner.inTimeline)); + final companion = PartnerEntityCompanion(inTimeline: Value(partner.inTimeline)); batch.insert( _db.partnerEntity, - companion.copyWith( - sharedById: Value(partner.sharedById), - sharedWithId: Value(partner.sharedWithId), - ), + companion.copyWith(sharedById: Value(partner.sharedById), sharedWithId: Value(partner.sharedWithId)), onConflict: DoUpdate((_) => companion), ); } @@ -101,24 +93,16 @@ class SyncStreamRepository extends DriftDatabaseRepository { } } - Future deleteAssetsV1( - Iterable data, { - String debugLabel = 'user', - }) async { + Future deleteAssetsV1(Iterable data, {String debugLabel = 'user'}) async { try { - await _db.remoteAssetEntity.deleteWhere( - (row) => row.id.isIn(data.map((e) => e.assetId)), - ); + await _db.remoteAssetEntity.deleteWhere((row) => row.id.isIn(data.map((e) => e.assetId))); } catch (error, stack) { _logger.severe('Error: deleteAssetsV1 - $debugLabel', error, stack); rethrow; } } - Future updateAssetsV1( - Iterable data, { - String debugLabel = 'user', - }) async { + Future updateAssetsV1(Iterable data, {String debugLabel = 'user'}) async { try { await _db.batch((batch) { for (final asset in data) { @@ -127,8 +111,7 @@ class SyncStreamRepository extends DriftDatabaseRepository { type: Value(asset.type.toAssetType()), createdAt: Value.absentIfNull(asset.fileCreatedAt), updatedAt: Value.absentIfNull(asset.fileModifiedAt), - durationInSeconds: - Value(asset.duration?.toDuration()?.inSeconds ?? 0), + durationInSeconds: Value(asset.duration?.toDuration()?.inSeconds ?? 0), checksum: Value(asset.checksum), isFavorite: Value(asset.isFavorite), ownerId: Value(asset.ownerId), @@ -138,6 +121,7 @@ class SyncStreamRepository extends DriftDatabaseRepository { visibility: Value(asset.visibility.toAssetVisibility()), livePhotoVideoId: Value(asset.livePhotoVideoId), stackId: Value(asset.stackId), + libraryId: Value(asset.libraryId), ); batch.insert( @@ -153,10 +137,7 @@ class SyncStreamRepository extends DriftDatabaseRepository { } } - Future updateAssetsExifV1( - Iterable data, { - String debugLabel = 'user', - }) async { + Future updateAssetsExifV1(Iterable data, {String debugLabel = 'user'}) async { try { await _db.batch((batch) { for (final exif in data) { @@ -192,20 +173,14 @@ class SyncStreamRepository extends DriftDatabaseRepository { } }); } catch (error, stack) { - _logger.severe( - 'Error: updateAssetsExifV1 - $debugLabel', - error, - stack, - ); + _logger.severe('Error: updateAssetsExifV1 - $debugLabel', error, stack); rethrow; } } Future deleteAlbumsV1(Iterable data) async { try { - await _db.remoteAlbumEntity.deleteWhere( - (row) => row.id.isIn(data.map((e) => e.albumId)), - ); + await _db.remoteAlbumEntity.deleteWhere((row) => row.id.isIn(data.map((e) => e.albumId))); } catch (error, stack) { _logger.severe('Error: deleteAlbumsV1', error, stack); rethrow; @@ -246,10 +221,7 @@ class SyncStreamRepository extends DriftDatabaseRepository { for (final album in data) { batch.delete( _db.remoteAlbumUserEntity, - RemoteAlbumUserEntityCompanion( - albumId: Value(album.albumId), - userId: Value(album.userId), - ), + RemoteAlbumUserEntityCompanion(albumId: Value(album.albumId), userId: Value(album.userId)), ); } }); @@ -259,49 +231,32 @@ class SyncStreamRepository extends DriftDatabaseRepository { } } - Future updateAlbumUsersV1( - Iterable data, { - String debugLabel = 'user', - }) async { + Future updateAlbumUsersV1(Iterable data, {String debugLabel = 'user'}) async { try { await _db.batch((batch) { for (final album in data) { - final companion = RemoteAlbumUserEntityCompanion( - role: Value(album.role.toAlbumUserRole()), - ); + final companion = RemoteAlbumUserEntityCompanion(role: Value(album.role.toAlbumUserRole())); batch.insert( _db.remoteAlbumUserEntity, - companion.copyWith( - albumId: Value(album.albumId), - userId: Value(album.userId), - ), + companion.copyWith(albumId: Value(album.albumId), userId: Value(album.userId)), onConflict: DoUpdate((_) => companion), ); } }); } catch (error, stack) { - _logger.severe( - 'Error: updateAlbumUsersV1 - $debugLabel', - error, - stack, - ); + _logger.severe('Error: updateAlbumUsersV1 - $debugLabel', error, stack); rethrow; } } - Future deleteAlbumToAssetsV1( - Iterable data, - ) async { + Future deleteAlbumToAssetsV1(Iterable data) async { try { await _db.batch((batch) { for (final album in data) { batch.delete( _db.remoteAlbumAssetEntity, - RemoteAlbumAssetEntityCompanion( - albumId: Value(album.albumId), - assetId: Value(album.assetId), - ), + RemoteAlbumAssetEntityCompanion(albumId: Value(album.albumId), assetId: Value(album.assetId)), ); } }); @@ -311,10 +266,7 @@ class SyncStreamRepository extends DriftDatabaseRepository { } } - Future updateAlbumToAssetsV1( - Iterable data, { - String debugLabel = 'user', - }) async { + Future updateAlbumToAssetsV1(Iterable data, {String debugLabel = 'user'}) async { try { await _db.batch((batch) { for (final album in data) { @@ -323,19 +275,11 @@ class SyncStreamRepository extends DriftDatabaseRepository { assetId: Value(album.assetId), ); - batch.insert( - _db.remoteAlbumAssetEntity, - companion, - onConflict: DoNothing(), - ); + batch.insert(_db.remoteAlbumAssetEntity, companion, onConflict: DoNothing()); } }); } catch (error, stack) { - _logger.severe( - 'Error: updateAlbumToAssetsV1 - $debugLabel', - error, - stack, - ); + _logger.severe('Error: updateAlbumToAssetsV1 - $debugLabel', error, stack); rethrow; } } @@ -372,9 +316,7 @@ class SyncStreamRepository extends DriftDatabaseRepository { Future deleteMemoriesV1(Iterable data) async { try { - await _db.memoryEntity.deleteWhere( - (row) => row.id.isIn(data.map((e) => e.memoryId)), - ); + await _db.memoryEntity.deleteWhere((row) => row.id.isIn(data.map((e) => e.memoryId))); } catch (error, stack) { _logger.severe('Error: deleteMemoriesV1', error, stack); rethrow; @@ -385,16 +327,9 @@ class SyncStreamRepository extends DriftDatabaseRepository { try { await _db.batch((batch) { for (final asset in data) { - final companion = MemoryAssetEntityCompanion( - memoryId: Value(asset.memoryId), - assetId: Value(asset.assetId), - ); + final companion = MemoryAssetEntityCompanion(memoryId: Value(asset.memoryId), assetId: Value(asset.assetId)); - batch.insert( - _db.memoryAssetEntity, - companion, - onConflict: DoNothing(), - ); + batch.insert(_db.memoryAssetEntity, companion, onConflict: DoNothing()); } }); } catch (error, stack) { @@ -403,18 +338,13 @@ class SyncStreamRepository extends DriftDatabaseRepository { } } - Future deleteMemoryAssetsV1( - Iterable data, - ) async { + Future deleteMemoryAssetsV1(Iterable data) async { try { await _db.batch((batch) { for (final asset in data) { batch.delete( _db.memoryAssetEntity, - MemoryAssetEntityCompanion( - memoryId: Value(asset.memoryId), - assetId: Value(asset.assetId), - ), + MemoryAssetEntityCompanion(memoryId: Value(asset.memoryId), assetId: Value(asset.assetId)), ); } }); @@ -424,10 +354,7 @@ class SyncStreamRepository extends DriftDatabaseRepository { } } - Future updateStacksV1( - Iterable data, { - String debugLabel = 'user', - }) async { + Future updateStacksV1(Iterable data, {String debugLabel = 'user'}) async { try { await _db.batch((batch) { for (final stack in data) { @@ -451,36 +378,24 @@ class SyncStreamRepository extends DriftDatabaseRepository { } } - Future deleteStacksV1( - Iterable data, { - String debugLabel = 'user', - }) async { + Future deleteStacksV1(Iterable data, {String debugLabel = 'user'}) async { try { - await _db.stackEntity.deleteWhere( - (row) => row.id.isIn(data.map((e) => e.stackId)), - ); + await _db.stackEntity.deleteWhere((row) => row.id.isIn(data.map((e) => e.stackId))); } catch (error, stack) { _logger.severe('Error: deleteStacksV1 - $debugLabel', error, stack); rethrow; } } - Future updateUserMetadatasV1( - Iterable data, - ) async { + Future updateUserMetadatasV1(Iterable data) async { try { await _db.batch((batch) { for (final userMetadata in data) { - final companion = UserMetadataEntityCompanion( - value: Value(userMetadata.value as Map), - ); + final companion = UserMetadataEntityCompanion(value: Value(userMetadata.value as Map)); batch.insert( _db.userMetadataEntity, - companion.copyWith( - userId: Value(userMetadata.userId), - key: Value(userMetadata.key.toUserMetadataKey()), - ), + companion.copyWith(userId: Value(userMetadata.userId), key: Value(userMetadata.key.toUserMetadataKey())), onConflict: DoUpdate((_) => companion), ); } @@ -491,9 +406,7 @@ class SyncStreamRepository extends DriftDatabaseRepository { } } - Future deleteUserMetadatasV1( - Iterable data, - ) async { + Future deleteUserMetadatasV1(Iterable data) async { try { await _db.batch((batch) { for (final userMetadata in data) { @@ -511,66 +424,148 @@ class SyncStreamRepository extends DriftDatabaseRepository { rethrow; } } + + Future updatePeopleV1(Iterable data) async { + try { + await _db.batch((batch) { + for (final person in data) { + final companion = PersonEntityCompanion( + createdAt: Value(person.createdAt), + updatedAt: Value(person.updatedAt), + ownerId: Value(person.ownerId), + name: Value(person.name), + faceAssetId: Value(person.faceAssetId), + isFavorite: Value(person.isFavorite), + isHidden: Value(person.isHidden), + color: Value(person.color), + birthDate: Value(person.birthDate), + ); + + batch.insert( + _db.personEntity, + companion.copyWith(id: Value(person.id)), + onConflict: DoUpdate((_) => companion), + ); + } + }); + } catch (error, stack) { + _logger.severe('Error: updatePeopleV1', error, stack); + rethrow; + } + } + + Future deletePeopleV1(Iterable data) async { + try { + await _db.batch((batch) { + for (final person in data) { + batch.deleteWhere(_db.personEntity, (row) => row.id.equals(person.personId)); + } + }); + } catch (error, stack) { + _logger.severe('Error: deletePeopleV1', error, stack); + rethrow; + } + } + + Future updateAssetFacesV1(Iterable data) async { + try { + await _db.batch((batch) { + for (final assetFace in data) { + final companion = AssetFaceEntityCompanion( + assetId: Value(assetFace.assetId), + personId: Value(assetFace.personId), + imageWidth: Value(assetFace.imageWidth), + imageHeight: Value(assetFace.imageHeight), + boundingBoxX1: Value(assetFace.boundingBoxX1), + boundingBoxY1: Value(assetFace.boundingBoxY1), + boundingBoxX2: Value(assetFace.boundingBoxX2), + boundingBoxY2: Value(assetFace.boundingBoxY2), + sourceType: Value(assetFace.sourceType), + ); + + batch.insert( + _db.assetFaceEntity, + companion.copyWith(id: Value(assetFace.id)), + onConflict: DoUpdate((_) => companion), + ); + } + }); + } catch (error, stack) { + _logger.severe('Error: updateAssetFacesV1', error, stack); + rethrow; + } + } + + Future deleteAssetFacesV1(Iterable data) async { + try { + await _db.batch((batch) { + for (final assetFace in data) { + batch.deleteWhere(_db.assetFaceEntity, (row) => row.id.equals(assetFace.assetFaceId)); + } + }); + } catch (error, stack) { + _logger.severe('Error: deleteAssetFacesV1', error, stack); + rethrow; + } + } } extension on AssetTypeEnum { AssetType toAssetType() => switch (this) { - AssetTypeEnum.IMAGE => AssetType.image, - AssetTypeEnum.VIDEO => AssetType.video, - AssetTypeEnum.AUDIO => AssetType.audio, - AssetTypeEnum.OTHER => AssetType.other, - _ => throw Exception('Unknown AssetType value: $this'), - }; + AssetTypeEnum.IMAGE => AssetType.image, + AssetTypeEnum.VIDEO => AssetType.video, + AssetTypeEnum.AUDIO => AssetType.audio, + AssetTypeEnum.OTHER => AssetType.other, + _ => throw Exception('Unknown AssetType value: $this'), + }; } extension on AssetOrder { AlbumAssetOrder toAlbumAssetOrder() => switch (this) { - AssetOrder.asc => AlbumAssetOrder.asc, - AssetOrder.desc => AlbumAssetOrder.desc, - _ => throw Exception('Unknown AssetOrder value: $this'), - }; + AssetOrder.asc => AlbumAssetOrder.asc, + AssetOrder.desc => AlbumAssetOrder.desc, + _ => throw Exception('Unknown AssetOrder value: $this'), + }; } extension on MemoryType { MemoryTypeEnum toMemoryType() => switch (this) { - MemoryType.onThisDay => MemoryTypeEnum.onThisDay, - _ => throw Exception('Unknown MemoryType value: $this'), - }; + MemoryType.onThisDay => MemoryTypeEnum.onThisDay, + _ => throw Exception('Unknown MemoryType value: $this'), + }; } extension on api.AlbumUserRole { AlbumUserRole toAlbumUserRole() => switch (this) { - api.AlbumUserRole.editor => AlbumUserRole.editor, - api.AlbumUserRole.viewer => AlbumUserRole.viewer, - _ => throw Exception('Unknown AlbumUserRole value: $this'), - }; + api.AlbumUserRole.editor => AlbumUserRole.editor, + api.AlbumUserRole.viewer => AlbumUserRole.viewer, + _ => throw Exception('Unknown AlbumUserRole value: $this'), + }; } extension on api.AssetVisibility { AssetVisibility toAssetVisibility() => switch (this) { - api.AssetVisibility.timeline => AssetVisibility.timeline, - api.AssetVisibility.hidden => AssetVisibility.hidden, - api.AssetVisibility.archive => AssetVisibility.archive, - api.AssetVisibility.locked => AssetVisibility.locked, - _ => throw Exception('Unknown AssetVisibility value: $this'), - }; + api.AssetVisibility.timeline => AssetVisibility.timeline, + api.AssetVisibility.hidden => AssetVisibility.hidden, + api.AssetVisibility.archive => AssetVisibility.archive, + api.AssetVisibility.locked => AssetVisibility.locked, + _ => throw Exception('Unknown AssetVisibility value: $this'), + }; } -extension on String { +extension on api.UserMetadataKey { UserMetadataKey toUserMetadataKey() => switch (this) { - "onboarding" => UserMetadataKey.onboarding, - "preferences" => UserMetadataKey.preferences, - "license" => UserMetadataKey.license, - _ => throw Exception('Unknown UserMetadataKey value: $this'), - }; + api.UserMetadataKey.onboarding => UserMetadataKey.onboarding, + api.UserMetadataKey.preferences => UserMetadataKey.preferences, + api.UserMetadataKey.license => UserMetadataKey.license, + _ => throw Exception('Unknown UserMetadataKey value: $this'), + }; } extension on String { Duration? toDuration() { try { - final parts = split(':') - .map((e) => double.parse(e).toInt()) - .toList(growable: false); + final parts = split(':').map((e) => double.parse(e).toInt()).toList(growable: false); return Duration(hours: parts[0], minutes: parts[1], seconds: parts[2]); } catch (_) { diff --git a/mobile/lib/infrastructure/repositories/timeline.repository.dart b/mobile/lib/infrastructure/repositories/timeline.repository.dart index 0c3eee59af..663db9b82f 100644 --- a/mobile/lib/infrastructure/repositories/timeline.repository.dart +++ b/mobile/lib/infrastructure/repositories/timeline.repository.dart @@ -21,10 +21,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository { Stream> watchTimelineUserIds(String userId) { final query = _db.partnerEntity.selectOnly() ..addColumns([_db.partnerEntity.sharedById]) - ..where( - _db.partnerEntity.inTimeline.equals(true) & - _db.partnerEntity.sharedWithId.equals(userId), - ); + ..where(_db.partnerEntity.inTimeline.equals(true) & _db.partnerEntity.sharedWithId.equals(userId)); return query .map((row) => row.read(_db.partnerEntity.sharedById)!) @@ -34,29 +31,17 @@ class DriftTimelineRepository extends DriftDatabaseRepository { } TimelineQuery main(List userIds, GroupAssetsBy groupBy) => ( - bucketSource: () => _watchMainBucket( - userIds, - groupBy: groupBy, - ), - assetSource: (offset, count) => _getMainBucketAssets( - userIds, - offset: offset, - count: count, - ), - ); + bucketSource: () => _watchMainBucket(userIds, groupBy: groupBy), + assetSource: (offset, count) => _getMainBucketAssets(userIds, offset: offset, count: count), + ); - Stream> _watchMainBucket( - List userIds, { - GroupAssetsBy groupBy = GroupAssetsBy.day, - }) { + Stream> _watchMainBucket(List userIds, {GroupAssetsBy groupBy = GroupAssetsBy.day}) { if (groupBy == GroupAssetsBy.none) { - throw UnsupportedError( - "GroupAssetsBy.none is not supported for watchMainBucket", - ); + throw UnsupportedError("GroupAssetsBy.none is not supported for watchMainBucket"); } return _db.mergedAssetDrift - .mergedBucket(userIds, groupBy: groupBy.index) + .mergedBucket(userIds: userIds, groupBy: groupBy.index) .map((row) { final date = row.bucketDate.dateFmt(groupBy); return TimeBucket(date: date, assetCount: row.assetCount); @@ -65,13 +50,9 @@ class DriftTimelineRepository extends DriftDatabaseRepository { .throttle(const Duration(seconds: 3), trailing: true); } - Future> _getMainBucketAssets( - List userIds, { - required int offset, - required int count, - }) { + Future> _getMainBucketAssets(List userIds, {required int offset, required int count}) { return _db.mergedAssetDrift - .mergedAsset(userIds, limit: Limit(count, offset)) + .mergedAsset(userIds: userIds, limit: (_) => Limit(count, offset)) .map( (row) => row.remoteId != null && row.ownerId != null ? RemoteAsset( @@ -90,7 +71,6 @@ class DriftTimelineRepository extends DriftDatabaseRepository { durationInSeconds: row.durationInSeconds, livePhotoVideoId: row.livePhotoVideoId, stackId: row.stackId, - stackCount: row.stackCount, ) : LocalAsset( id: row.localId!, @@ -111,21 +91,11 @@ class DriftTimelineRepository extends DriftDatabaseRepository { } TimelineQuery localAlbum(String albumId, GroupAssetsBy groupBy) => ( - bucketSource: () => _watchLocalAlbumBucket( - albumId, - groupBy: groupBy, - ), - assetSource: (offset, count) => _getLocalAlbumBucketAssets( - albumId, - offset: offset, - count: count, - ), - ); + bucketSource: () => _watchLocalAlbumBucket(albumId, groupBy: groupBy), + assetSource: (offset, count) => _getLocalAlbumBucketAssets(albumId, offset: offset, count: count), + ); - Stream> _watchLocalAlbumBucket( - String albumId, { - GroupAssetsBy groupBy = GroupAssetsBy.day, - }) { + Stream> _watchLocalAlbumBucket(String albumId, {GroupAssetsBy groupBy = GroupAssetsBy.day}) { if (groupBy == GroupAssetsBy.none) { return _db.localAlbumAssetEntity .count(where: (row) => row.albumId.equals(albumId)) @@ -136,17 +106,23 @@ class DriftTimelineRepository extends DriftDatabaseRepository { final assetCountExp = _db.localAssetEntity.id.count(); final dateExp = _db.localAssetEntity.createdAt.dateFmt(groupBy); - final query = _db.localAssetEntity.selectOnly().join([ - innerJoin( - _db.localAlbumAssetEntity, - _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), - useColumns: false, - ), - ]) - ..addColumns([assetCountExp, dateExp]) - ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) - ..groupBy([dateExp]) - ..orderBy([OrderingTerm.desc(dateExp)]); + final query = + _db.localAssetEntity.selectOnly().join([ + innerJoin( + _db.localAlbumAssetEntity, + _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), + useColumns: false, + ), + leftOuterJoin( + _db.remoteAssetEntity, + _db.localAssetEntity.checksum.equalsExp(_db.remoteAssetEntity.checksum), + useColumns: false, + ), + ]) + ..addColumns([assetCountExp, dateExp]) + ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) + ..groupBy([dateExp]) + ..orderBy([OrderingTerm.desc(dateExp)]); return query.map((row) { final timeline = row.read(dateExp)!.dateFmt(groupBy); @@ -155,55 +131,37 @@ class DriftTimelineRepository extends DriftDatabaseRepository { }).watch(); } - Future> _getLocalAlbumBucketAssets( - String albumId, { - required int offset, - required int count, - }) { - final query = _db.localAssetEntity.select().join( - [ - innerJoin( - _db.localAlbumAssetEntity, - _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), - useColumns: false, - ), - leftOuterJoin( - _db.remoteAssetEntity, - _db.localAssetEntity.checksum - .equalsExp(_db.remoteAssetEntity.checksum), - useColumns: false, - ), - ], - ) - ..addColumns([_db.remoteAssetEntity.id]) - ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) - ..orderBy([OrderingTerm.desc(_db.localAssetEntity.createdAt)]) - ..limit(count, offset: offset); + Future> _getLocalAlbumBucketAssets(String albumId, {required int offset, required int count}) { + final query = + _db.localAssetEntity.select().join([ + innerJoin( + _db.localAlbumAssetEntity, + _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id), + useColumns: false, + ), + leftOuterJoin( + _db.remoteAssetEntity, + _db.localAssetEntity.checksum.equalsExp(_db.remoteAssetEntity.checksum), + useColumns: false, + ), + ]) + ..addColumns([_db.remoteAssetEntity.id]) + ..where(_db.localAlbumAssetEntity.albumId.equals(albumId)) + ..orderBy([OrderingTerm.desc(_db.localAssetEntity.createdAt)]) + ..limit(count, offset: offset); return query.map((row) { final asset = row.readTable(_db.localAssetEntity).toDto(); - return asset.copyWith( - remoteId: row.read(_db.remoteAssetEntity.id), - ); + return asset.copyWith(remoteId: row.read(_db.remoteAssetEntity.id)); }).get(); } TimelineQuery remoteAlbum(String albumId, GroupAssetsBy groupBy) => ( - bucketSource: () => _watchRemoteAlbumBucket( - albumId, - groupBy: groupBy, - ), - assetSource: (offset, count) => _getRemoteAlbumBucketAssets( - albumId, - offset: offset, - count: count, - ), - ); + bucketSource: () => _watchRemoteAlbumBucket(albumId, groupBy: groupBy), + assetSource: (offset, count) => _getRemoteAlbumBucketAssets(albumId, offset: offset, count: count), + ); - Stream> _watchRemoteAlbumBucket( - String albumId, { - GroupAssetsBy groupBy = GroupAssetsBy.day, - }) { + Stream> _watchRemoteAlbumBucket(String albumId, {GroupAssetsBy groupBy = GroupAssetsBy.day}) { if (groupBy == GroupAssetsBy.none) { return _db.remoteAlbumAssetEntity .count(where: (row) => row.albumId.equals(albumId)) @@ -211,64 +169,54 @@ class DriftTimelineRepository extends DriftDatabaseRepository { .watch() .map((results) => results.isNotEmpty ? results.first : []) .handleError((error) { - return []; - }); + return []; + }); } - return (_db.remoteAlbumEntity.select() - ..where((row) => row.id.equals(albumId))) + return (_db.remoteAlbumEntity.select()..where((row) => row.id.equals(albumId))) .watch() .switchMap((albums) { - if (albums.isEmpty) { - return Stream.value([]); - } + if (albums.isEmpty) { + return Stream.value([]); + } - final album = albums.first; - final isAscending = album.order == AlbumAssetOrder.asc; - final assetCountExp = _db.remoteAssetEntity.id.count(); - final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy); + final album = albums.first; + final isAscending = album.order == AlbumAssetOrder.asc; + final assetCountExp = _db.remoteAssetEntity.id.count(); + final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy); - final query = _db.remoteAssetEntity.selectOnly() - ..addColumns([assetCountExp, dateExp]) - ..join([ - innerJoin( - _db.remoteAlbumAssetEntity, - _db.remoteAlbumAssetEntity.assetId - .equalsExp(_db.remoteAssetEntity.id), - useColumns: false, - ), - ]) - ..where( - _db.remoteAssetEntity.deletedAt.isNull() & - _db.remoteAlbumAssetEntity.albumId.equals(albumId), - ) - ..groupBy([dateExp]); + final query = _db.remoteAssetEntity.selectOnly() + ..addColumns([assetCountExp, dateExp]) + ..join([ + innerJoin( + _db.remoteAlbumAssetEntity, + _db.remoteAlbumAssetEntity.assetId.equalsExp(_db.remoteAssetEntity.id), + useColumns: false, + ), + ]) + ..where(_db.remoteAssetEntity.deletedAt.isNull() & _db.remoteAlbumAssetEntity.albumId.equals(albumId)) + ..groupBy([dateExp]); - if (isAscending) { - query.orderBy([OrderingTerm.asc(dateExp)]); - } else { - query.orderBy([OrderingTerm.desc(dateExp)]); - } + if (isAscending) { + query.orderBy([OrderingTerm.asc(dateExp)]); + } else { + query.orderBy([OrderingTerm.desc(dateExp)]); + } - return query.map((row) { - final timeline = row.read(dateExp)!.dateFmt(groupBy); - final assetCount = row.read(assetCountExp)!; - return TimeBucket(date: timeline, assetCount: assetCount); - }).watch(); - }).handleError((error) { - // If there's an error (e.g., album was deleted), return empty buckets - return []; - }); + return query.map((row) { + final timeline = row.read(dateExp)!.dateFmt(groupBy); + final assetCount = row.read(assetCountExp)!; + return TimeBucket(date: timeline, assetCount: assetCount); + }).watch(); + }) + .handleError((error) { + // If there's an error (e.g., album was deleted), return empty buckets + return []; + }); } - Future> _getRemoteAlbumBucketAssets( - String albumId, { - required int offset, - required int count, - }) async { - final albumData = await (_db.remoteAlbumEntity.select() - ..where((row) => row.id.equals(albumId))) - .getSingleOrNull(); + Future> _getRemoteAlbumBucketAssets(String albumId, {required int offset, required int count}) async { + final albumData = await (_db.remoteAlbumEntity.select()..where((row) => row.id.equals(albumId))).getSingleOrNull(); // If album doesn't exist (was deleted), return empty list if (albumData == null) { @@ -277,19 +225,13 @@ class DriftTimelineRepository extends DriftDatabaseRepository { final isAscending = albumData.order == AlbumAssetOrder.asc; - final query = _db.remoteAssetEntity.select().join( - [ - innerJoin( - _db.remoteAlbumAssetEntity, - _db.remoteAlbumAssetEntity.assetId - .equalsExp(_db.remoteAssetEntity.id), - useColumns: false, - ), - ], - )..where( - _db.remoteAssetEntity.deletedAt.isNull() & - _db.remoteAlbumAssetEntity.albumId.equals(albumId), - ); + final query = _db.remoteAssetEntity.select().join([ + innerJoin( + _db.remoteAlbumAssetEntity, + _db.remoteAlbumAssetEntity.assetId.equalsExp(_db.remoteAssetEntity.id), + useColumns: false, + ), + ])..where(_db.remoteAssetEntity.deletedAt.isNull() & _db.remoteAlbumAssetEntity.albumId.equals(albumId)); if (isAscending) { query.orderBy([OrderingTerm.asc(_db.remoteAssetEntity.createdAt)]); @@ -299,90 +241,66 @@ class DriftTimelineRepository extends DriftDatabaseRepository { query.limit(count, offset: offset); - return query - .map((row) => row.readTable(_db.remoteAssetEntity).toDto()) - .get(); + return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get(); } TimelineQuery fromAssets(List assets) => ( - bucketSource: () => Stream.value(_generateBuckets(assets.length)), - assetSource: (offset, count) => - Future.value(assets.skip(offset).take(count).toList()), - ); + bucketSource: () => Stream.value(_generateBuckets(assets.length)), + assetSource: (offset, count) => Future.value(assets.skip(offset).take(count).toList()), + ); - TimelineQuery remote(String ownerId, GroupAssetsBy groupBy) => - _remoteQueryBuilder( - filter: (row) => - row.deletedAt.isNull() & - row.visibility.equalsValue(AssetVisibility.timeline) & - row.ownerId.equals(ownerId), - groupBy: groupBy, - ); + TimelineQuery remote(String ownerId, GroupAssetsBy groupBy) => _remoteQueryBuilder( + filter: (row) => + row.deletedAt.isNull() & row.visibility.equalsValue(AssetVisibility.timeline) & row.ownerId.equals(ownerId), + groupBy: groupBy, + ); - TimelineQuery favorite(String userId, GroupAssetsBy groupBy) => - _remoteQueryBuilder( - filter: (row) => - row.deletedAt.isNull() & - row.isFavorite.equals(true) & - row.ownerId.equals(userId), - groupBy: groupBy, - ); + TimelineQuery favorite(String userId, GroupAssetsBy groupBy) => _remoteQueryBuilder( + filter: (row) => row.deletedAt.isNull() & row.isFavorite.equals(true) & row.ownerId.equals(userId), + groupBy: groupBy, + ); - TimelineQuery trash(String userId, GroupAssetsBy groupBy) => - _remoteQueryBuilder( - filter: (row) => row.deletedAt.isNotNull() & row.ownerId.equals(userId), - groupBy: groupBy, - ); + TimelineQuery trash(String userId, GroupAssetsBy groupBy) => _remoteQueryBuilder( + filter: (row) => row.deletedAt.isNotNull() & row.ownerId.equals(userId), + groupBy: groupBy, + joinLocal: true, + ); - TimelineQuery archived(String userId, GroupAssetsBy groupBy) => - _remoteQueryBuilder( - filter: (row) => - row.deletedAt.isNull() & - row.ownerId.equals(userId) & - row.visibility.equalsValue(AssetVisibility.archive), - groupBy: groupBy, - ); + TimelineQuery archived(String userId, GroupAssetsBy groupBy) => _remoteQueryBuilder( + filter: (row) => + row.deletedAt.isNull() & row.ownerId.equals(userId) & row.visibility.equalsValue(AssetVisibility.archive), + groupBy: groupBy, + ); - TimelineQuery locked(String userId, GroupAssetsBy groupBy) => - _remoteQueryBuilder( - filter: (row) => - row.deletedAt.isNull() & - row.visibility.equalsValue(AssetVisibility.locked) & - row.ownerId.equals(userId), - groupBy: groupBy, - ); + TimelineQuery locked(String userId, GroupAssetsBy groupBy) => _remoteQueryBuilder( + filter: (row) => + row.deletedAt.isNull() & row.visibility.equalsValue(AssetVisibility.locked) & row.ownerId.equals(userId), + groupBy: groupBy, + ); - TimelineQuery video(String userId, GroupAssetsBy groupBy) => - _remoteQueryBuilder( - filter: (row) => - row.deletedAt.isNull() & - row.type.equalsValue(AssetType.video) & - row.visibility.equalsValue(AssetVisibility.timeline) & - row.ownerId.equals(userId), - groupBy: groupBy, - ); + TimelineQuery video(String userId, GroupAssetsBy groupBy) => _remoteQueryBuilder( + filter: (row) => + row.deletedAt.isNull() & + row.type.equalsValue(AssetType.video) & + row.visibility.equalsValue(AssetVisibility.timeline) & + row.ownerId.equals(userId), + groupBy: groupBy, + ); TimelineQuery place(String place, GroupAssetsBy groupBy) => ( - bucketSource: () => _watchPlaceBucket( - place, - groupBy: groupBy, - ), - assetSource: (offset, count) => _getPlaceBucketAssets( - place, - offset: offset, - count: count, - ), - ); + bucketSource: () => _watchPlaceBucket(place, groupBy: groupBy), + assetSource: (offset, count) => _getPlaceBucketAssets(place, offset: offset, count: count), + ); - Stream> _watchPlaceBucket( - String place, { - GroupAssetsBy groupBy = GroupAssetsBy.day, - }) { + TimelineQuery person(String userId, String personId, GroupAssetsBy groupBy) => ( + bucketSource: () => _watchPersonBucket(userId, personId, groupBy: groupBy), + assetSource: (offset, count) => _getPersonBucketAssets(userId, personId, offset: offset, count: count), + ); + + Stream> _watchPlaceBucket(String place, {GroupAssetsBy groupBy = GroupAssetsBy.day}) { if (groupBy == GroupAssetsBy.none) { // TODO: implement GroupAssetBy for place - throw UnsupportedError( - "GroupAssetsBy.none is not supported for watchPlaceBucket", - ); + throw UnsupportedError("GroupAssetsBy.none is not supported for watchPlaceBucket"); } final assetCountExp = _db.remoteAssetEntity.id.count(); @@ -400,8 +318,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository { ..where( _db.remoteExifEntity.city.equals(place) & _db.remoteAssetEntity.deletedAt.isNull() & - _db.remoteAssetEntity.visibility - .equalsValue(AssetVisibility.timeline), + _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline), ) ..groupBy([dateExp]) ..orderBy([OrderingTerm.desc(dateExp)]); @@ -413,41 +330,112 @@ class DriftTimelineRepository extends DriftDatabaseRepository { }).watch(); } - Future> _getPlaceBucketAssets( - String place, { + Future> _getPlaceBucketAssets(String place, {required int offset, required int count}) { + final query = + _db.remoteAssetEntity.select().join([ + innerJoin( + _db.remoteExifEntity, + _db.remoteExifEntity.assetId.equalsExp(_db.remoteAssetEntity.id), + useColumns: false, + ), + ]) + ..where( + _db.remoteAssetEntity.deletedAt.isNull() & + _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) & + _db.remoteExifEntity.city.equals(place), + ) + ..orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)]) + ..limit(count, offset: offset); + return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get(); + } + + Stream> _watchPersonBucket(String userId, String personId, {GroupAssetsBy groupBy = GroupAssetsBy.day}) { + if (groupBy == GroupAssetsBy.none) { + final query = _db.remoteAssetEntity.selectOnly() + ..addColumns([_db.remoteAssetEntity.id.count()]) + ..join([ + innerJoin( + _db.assetFaceEntity, + _db.assetFaceEntity.assetId.equalsExp(_db.remoteAssetEntity.id), + useColumns: false, + ), + ]) + ..where( + _db.remoteAssetEntity.deletedAt.isNull() & + _db.remoteAssetEntity.ownerId.equals(userId) & + _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) & + _db.assetFaceEntity.personId.equals(personId), + ); + + return query.map((row) { + final count = row.read(_db.remoteAssetEntity.id.count())!; + return _generateBuckets(count); + }).watchSingle(); + } + + final assetCountExp = _db.remoteAssetEntity.id.count(); + final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy); + + final query = _db.remoteAssetEntity.selectOnly() + ..addColumns([assetCountExp, dateExp]) + ..join([ + innerJoin( + _db.assetFaceEntity, + _db.assetFaceEntity.assetId.equalsExp(_db.remoteAssetEntity.id), + useColumns: false, + ), + ]) + ..where( + _db.remoteAssetEntity.deletedAt.isNull() & + _db.remoteAssetEntity.ownerId.equals(userId) & + _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) & + _db.assetFaceEntity.personId.equals(personId), + ) + ..groupBy([dateExp]) + ..orderBy([OrderingTerm.desc(dateExp)]); + + return query.map((row) { + final timeline = row.read(dateExp)!.dateFmt(groupBy); + final assetCount = row.read(assetCountExp)!; + return TimeBucket(date: timeline, assetCount: assetCount); + }).watch(); + } + + Future> _getPersonBucketAssets( + String userId, + String personId, { required int offset, required int count, }) { - final query = _db.remoteAssetEntity.select().join( - [ - innerJoin( - _db.remoteExifEntity, - _db.remoteExifEntity.assetId.equalsExp(_db.remoteAssetEntity.id), - useColumns: false, - ), - ], - ) - ..where( - _db.remoteAssetEntity.deletedAt.isNull() & - _db.remoteAssetEntity.visibility - .equalsValue(AssetVisibility.timeline) & - _db.remoteExifEntity.city.equals(place), - ) - ..orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)]) - ..limit(count, offset: offset); - return query - .map((row) => row.readTable(_db.remoteAssetEntity).toDto()) - .get(); + final query = + _db.remoteAssetEntity.select().join([ + innerJoin( + _db.assetFaceEntity, + _db.assetFaceEntity.assetId.equalsExp(_db.remoteAssetEntity.id), + useColumns: false, + ), + ]) + ..where( + _db.remoteAssetEntity.deletedAt.isNull() & + _db.remoteAssetEntity.ownerId.equals(userId) & + _db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) & + _db.assetFaceEntity.personId.equals(personId), + ) + ..orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)]) + ..limit(count, offset: offset); + + return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get(); } TimelineQuery _remoteQueryBuilder({ required Expression Function($RemoteAssetEntityTable row) filter, GroupAssetsBy groupBy = GroupAssetsBy.day, + bool joinLocal = false, }) { return ( bucketSource: () => _watchRemoteBucket(filter: filter, groupBy: groupBy), assetSource: (offset, count) => - _getRemoteAssets(filter: filter, offset: offset, count: count), + _getRemoteAssets(filter: filter, offset: offset, count: count, joinLocal: joinLocal), ); } @@ -480,13 +468,35 @@ class DriftTimelineRepository extends DriftDatabaseRepository { required Expression Function($RemoteAssetEntityTable row) filter, required int offset, required int count, + bool joinLocal = false, }) { - final query = _db.remoteAssetEntity.select() - ..where(filter) - ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]) - ..limit(count, offset: offset); + if (joinLocal) { + final query = + _db.remoteAssetEntity.select().join([ + leftOuterJoin( + _db.localAssetEntity, + _db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum), + useColumns: false, + ), + ]) + ..addColumns([_db.localAssetEntity.id]) + ..where(filter(_db.remoteAssetEntity)) + ..orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)]) + ..limit(count, offset: offset); - return query.map((row) => row.toDto()).get(); + return query.map((row) { + final asset = row.readTable(_db.remoteAssetEntity).toDto(); + final localId = row.read(_db.localAssetEntity.id); + return asset.copyWith(localId: localId); + }).get(); + } else { + final query = _db.remoteAssetEntity.select() + ..where(filter) + ..orderBy([(row) => OrderingTerm.desc(row.createdAt)]) + ..limit(count, offset: offset); + + return query.map((row) => row.toDto()).get(); + } } } @@ -507,11 +517,9 @@ extension on Expression { // to create the correct time bucket final localTimeExp = modify(const DateTimeModifier.localTime()); return switch (groupBy) { - GroupAssetsBy.day => localTimeExp.date, + GroupAssetsBy.day || GroupAssetsBy.auto => localTimeExp.date, GroupAssetsBy.month => localTimeExp.strftime("%Y-%m"), - GroupAssetsBy.none => throw ArgumentError( - "GroupAssetsBy.none is not supported for date formatting", - ), + GroupAssetsBy.none => throw ArgumentError("GroupAssetsBy.none is not supported for date formatting"), }; } } @@ -519,11 +527,9 @@ extension on Expression { extension on String { DateTime dateFmt(GroupAssetsBy groupBy) { final format = switch (groupBy) { - GroupAssetsBy.day => "y-M-d", + GroupAssetsBy.day || GroupAssetsBy.auto => "y-M-d", GroupAssetsBy.month => "y-M", - GroupAssetsBy.none => throw ArgumentError( - "GroupAssetsBy.none is not supported for date formatting", - ), + GroupAssetsBy.none => throw ArgumentError("GroupAssetsBy.none is not supported for date formatting"), }; try { return DateFormat(format).parse(this); diff --git a/mobile/lib/infrastructure/repositories/user.repository.dart b/mobile/lib/infrastructure/repositories/user.repository.dart index 4ccfccbdcc..2c6d721396 100644 --- a/mobile/lib/infrastructure/repositories/user.repository.dart +++ b/mobile/lib/infrastructure/repositories/user.repository.dart @@ -1,7 +1,6 @@ import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; -import 'package:immich_mobile/infrastructure/entities/user.entity.dart' - as entity; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart' as entity; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; import 'package:isar/isar.dart'; diff --git a/mobile/lib/infrastructure/repositories/user_api.repository.dart b/mobile/lib/infrastructure/repositories/user_api.repository.dart index e990b9d8d1..d21a1b71a6 100644 --- a/mobile/lib/infrastructure/repositories/user_api.repository.dart +++ b/mobile/lib/infrastructure/repositories/user_api.repository.dart @@ -11,22 +11,14 @@ class UserApiRepository extends ApiRepository { const UserApiRepository(this._api); Future getMyUser() async { - final (adminDto, preferenceDto) = - await (_api.getMyUser(), _api.getMyPreferences()).wait; + final (adminDto, preferenceDto) = await (_api.getMyUser(), _api.getMyPreferences()).wait; if (adminDto == null) return null; return UserConverter.fromAdminDto(adminDto, preferenceDto); } - Future createProfileImage({ - required String name, - required Uint8List data, - }) async { - final res = await checkNull( - _api.createProfileImage( - MultipartFile.fromBytes('file', data, filename: name), - ), - ); + Future createProfileImage({required String name, required Uint8List data}) async { + final res = await checkNull(_api.createProfileImage(MultipartFile.fromBytes('file', data, filename: name))); return res.profileImagePath; } diff --git a/mobile/lib/infrastructure/repositories/user_metadata.repository.dart b/mobile/lib/infrastructure/repositories/user_metadata.repository.dart index 19364afb43..7205c7f73a 100644 --- a/mobile/lib/infrastructure/repositories/user_metadata.repository.dart +++ b/mobile/lib/infrastructure/repositories/user_metadata.repository.dart @@ -8,8 +8,7 @@ class DriftUserMetadataRepository extends DriftDatabaseRepository { const DriftUserMetadataRepository(this._db) : super(_db); Future> getUserMetadata(String userId) { - final query = _db.userMetadataEntity.select() - ..where((e) => e.userId.equals(userId)); + final query = _db.userMetadataEntity.select()..where((e) => e.userId.equals(userId)); return query.map((userMetadata) { return userMetadata.toDto(); @@ -19,20 +18,8 @@ class DriftUserMetadataRepository extends DriftDatabaseRepository { extension on UserMetadataEntityData { UserMetadata toDto() => switch (key) { - UserMetadataKey.onboarding => UserMetadata( - userId: userId, - key: key, - onboarding: Onboarding.fromMap(value), - ), - UserMetadataKey.preferences => UserMetadata( - userId: userId, - key: key, - preferences: Preferences.fromMap(value), - ), - UserMetadataKey.license => UserMetadata( - userId: userId, - key: key, - license: License.fromMap(value), - ), - }; + UserMetadataKey.onboarding => UserMetadata(userId: userId, key: key, onboarding: Onboarding.fromMap(value)), + UserMetadataKey.preferences => UserMetadata(userId: userId, key: key, preferences: Preferences.fromMap(value)), + UserMetadataKey.license => UserMetadata(userId: userId, key: key, license: License.fromMap(value)), + }; } diff --git a/mobile/lib/infrastructure/utils/user.converter.dart b/mobile/lib/infrastructure/utils/user.converter.dart index eb7b24737e..dc107e6fb2 100644 --- a/mobile/lib/infrastructure/utils/user.converter.dart +++ b/mobile/lib/infrastructure/utils/user.converter.dart @@ -6,63 +6,58 @@ import 'package:openapi/api.dart'; abstract final class UserConverter { /// Base user dto used where the complete user object is not required static UserDto fromSimpleUserDto(UserResponseDto dto) => UserDto( - id: dto.id, - email: dto.email, - name: dto.name, - isAdmin: false, - updatedAt: DateTime.now(), - profileImagePath: dto.profileImagePath, - avatarColor: dto.avatarColor.toAvatarColor(), - ); + id: dto.id, + email: dto.email, + name: dto.name, + isAdmin: false, + updatedAt: DateTime.now(), + hasProfileImage: dto.profileImagePath.isNotEmpty, + profileChangedAt: dto.profileChangedAt, + avatarColor: dto.avatarColor.toAvatarColor(), + ); - static UserDto fromAdminDto( - UserAdminResponseDto adminDto, [ - UserPreferencesResponseDto? preferenceDto, - ]) => - UserDto( - id: adminDto.id, - email: adminDto.email, - name: adminDto.name, - isAdmin: adminDto.isAdmin, - updatedAt: adminDto.updatedAt, - profileImagePath: adminDto.profileImagePath, - avatarColor: adminDto.avatarColor.toAvatarColor(), - memoryEnabled: preferenceDto?.memories.enabled ?? true, - inTimeline: false, - isPartnerSharedBy: false, - isPartnerSharedWith: false, - quotaUsageInBytes: adminDto.quotaUsageInBytes ?? 0, - quotaSizeInBytes: adminDto.quotaSizeInBytes ?? 0, - ); + static UserDto fromAdminDto(UserAdminResponseDto adminDto, [UserPreferencesResponseDto? preferenceDto]) => UserDto( + id: adminDto.id, + email: adminDto.email, + name: adminDto.name, + isAdmin: adminDto.isAdmin, + updatedAt: adminDto.updatedAt, + avatarColor: adminDto.avatarColor.toAvatarColor(), + memoryEnabled: preferenceDto?.memories.enabled ?? true, + inTimeline: false, + isPartnerSharedBy: false, + isPartnerSharedWith: false, + profileChangedAt: adminDto.profileChangedAt, + hasProfileImage: adminDto.profileImagePath.isNotEmpty, + ); static UserDto fromPartnerDto(PartnerResponseDto dto) => UserDto( - id: dto.id, - email: dto.email, - name: dto.name, - isAdmin: false, - updatedAt: DateTime.now(), - profileImagePath: dto.profileImagePath, - avatarColor: dto.avatarColor.toAvatarColor(), - memoryEnabled: false, - inTimeline: dto.inTimeline ?? false, - isPartnerSharedBy: false, - isPartnerSharedWith: false, - quotaUsageInBytes: 0, - quotaSizeInBytes: 0, - ); + id: dto.id, + email: dto.email, + name: dto.name, + isAdmin: false, + updatedAt: DateTime.now(), + avatarColor: dto.avatarColor.toAvatarColor(), + memoryEnabled: false, + inTimeline: dto.inTimeline ?? false, + isPartnerSharedBy: false, + isPartnerSharedWith: false, + profileChangedAt: dto.profileChangedAt, + hasProfileImage: dto.profileImagePath.isNotEmpty, + ); } extension on UserAvatarColor { AvatarColor toAvatarColor() => switch (this) { - UserAvatarColor.red => AvatarColor.red, - UserAvatarColor.green => AvatarColor.green, - UserAvatarColor.blue => AvatarColor.blue, - UserAvatarColor.purple => AvatarColor.purple, - UserAvatarColor.orange => AvatarColor.orange, - UserAvatarColor.pink => AvatarColor.pink, - UserAvatarColor.amber => AvatarColor.amber, - UserAvatarColor.yellow => AvatarColor.yellow, - UserAvatarColor.gray => AvatarColor.gray, - UserAvatarColor.primary || _ => AvatarColor.primary, - }; + UserAvatarColor.red => AvatarColor.red, + UserAvatarColor.green => AvatarColor.green, + UserAvatarColor.blue => AvatarColor.blue, + UserAvatarColor.purple => AvatarColor.purple, + UserAvatarColor.orange => AvatarColor.orange, + UserAvatarColor.pink => AvatarColor.pink, + UserAvatarColor.amber => AvatarColor.amber, + UserAvatarColor.yellow => AvatarColor.yellow, + UserAvatarColor.gray => AvatarColor.gray, + UserAvatarColor.primary || _ => AvatarColor.primary, + }; } diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index f67767f767..d1f415a304 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -10,6 +10,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_displaymode/flutter_displaymode.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/constants/locales.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/generated/codegen_loader.g.dart'; @@ -29,7 +30,6 @@ import 'package:immich_mobile/theme/dynamic_theme.dart'; import 'package:immich_mobile/theme/theme_data.dart'; import 'package:immich_mobile/utils/bootstrap.dart'; import 'package:immich_mobile/utils/cache/widgets_binding.dart'; -import 'package:immich_mobile/utils/download.dart'; import 'package:immich_mobile/utils/http_ssl_options.dart'; import 'package:immich_mobile/utils/licenses.dart'; import 'package:immich_mobile/utils/migration.dart'; @@ -50,10 +50,7 @@ void main() async { runApp( ProviderScope( - overrides: [ - dbProvider.overrideWithValue(db), - isarProvider.overrideWithValue(db), - ], + overrides: [dbProvider.overrideWithValue(db), isarProvider.overrideWithValue(db)], child: const MainWidget(), ), ); @@ -93,23 +90,21 @@ Future initApp() async { initializeTimeZones(); - await FileDownloader().trackTasksInGroup( - downloadGroupLivePhoto, - markDownloadedComplete: false, + // Initialize the file downloader + await FileDownloader().configure( + // maxConcurrent: 6, maxConcurrentByHost(server):6, maxConcurrentByGroup: 3 + globalConfig: (Config.holdingQueue, (6, 6, 3)), ); + await FileDownloader().trackTasksInGroup(kDownloadGroupLivePhoto, markDownloadedComplete: false); + await FileDownloader().trackTasks(); - LicenseRegistry.addLicense( - () async* { - for (final license in nonPubLicenses.entries) { - yield LicenseEntryWithLineBreaks( - [license.key], - license.value, - ); - } - }, - ); + LicenseRegistry.addLicense(() async* { + for (final license in nonPubLicenses.entries) { + yield LicenseEntryWithLineBreaks([license.key], license.value); + } + }); } class ImmichApp extends ConsumerStatefulWidget { @@ -119,8 +114,7 @@ class ImmichApp extends ConsumerStatefulWidget { ImmichAppState createState() => ImmichAppState(); } -class ImmichAppState extends ConsumerState - with WidgetsBindingObserver { +class ImmichAppState extends ConsumerState with WidgetsBindingObserver { @override void didChangeAppLifecycleState(AppLifecycleState state) { switch (state) { @@ -154,16 +148,12 @@ class ImmichAppState extends ConsumerState SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); // Sets the navigation bar color - SystemUiOverlayStyle overlayStyle = const SystemUiOverlayStyle( - systemNavigationBarColor: Colors.transparent, - ); + SystemUiOverlayStyle overlayStyle = const SystemUiOverlayStyle(systemNavigationBarColor: Colors.transparent); if (Platform.isAndroid) { // Android 8 does not support transparent app bars final info = await DeviceInfoPlugin().androidInfo; if (info.version.sdkInt <= 26) { - overlayStyle = context.isDarkTheme - ? SystemUiOverlayStyle.dark - : SystemUiOverlayStyle.light; + overlayStyle = context.isDarkTheme ? SystemUiOverlayStyle.dark : SystemUiOverlayStyle.light; } } SystemChrome.setSystemUIOverlayStyle(overlayStyle); @@ -171,15 +161,24 @@ class ImmichAppState extends ConsumerState } void _configureFileDownloaderNotifications() { - FileDownloader().configureNotification( - running: TaskNotification( - 'downloading_media'.tr(), - '${'file_name'.tr()}: {filename}', - ), - complete: TaskNotification( - 'download_finished'.tr(), - '${'file_name'.tr()}: {filename}', - ), + FileDownloader().configureNotificationForGroup( + kDownloadGroupImage, + running: TaskNotification('downloading_media'.tr(), '${'file_name'.tr()}: {filename}'), + complete: TaskNotification('download_finished'.tr(), '${'file_name'.tr()}: {filename}'), + progressBar: true, + ); + + FileDownloader().configureNotificationForGroup( + kDownloadGroupVideo, + running: TaskNotification('downloading_media'.tr(), '${'file_name'.tr()}: {filename}'), + complete: TaskNotification('download_finished'.tr(), '${'file_name'.tr()}: {filename}'), + progressBar: true, + ); + + FileDownloader().configureNotificationForGroup( + kManualUploadGroup, + running: TaskNotification('uploading_media'.tr(), '${'file_name'.tr()}: {displayName}'), + complete: TaskNotification('upload_finished'.tr(), '${'file_name'.tr()}: {displayName}'), progressBar: true, ); } @@ -188,23 +187,16 @@ class ImmichAppState extends ConsumerState final deepLinkHandler = ref.read(deepLinkServiceProvider); final currentRouteName = ref.read(currentRouteNameProvider.notifier).state; - final isColdStart = - currentRouteName == null || currentRouteName == SplashScreenRoute.name; + final isColdStart = currentRouteName == null || currentRouteName == SplashScreenRoute.name; if (deepLink.uri.scheme == "immich") { - final proposedRoute = await deepLinkHandler.handleScheme( - deepLink, - isColdStart, - ); + final proposedRoute = await deepLinkHandler.handleScheme(deepLink, isColdStart); return proposedRoute; } if (deepLink.uri.host == "my.immich.app") { - final proposedRoute = await deepLinkHandler.handleMyImmichApp( - deepLink, - isColdStart, - ); + final proposedRoute = await deepLinkHandler.handleMyImmichApp(deepLink, isColdStart); return proposedRoute; } @@ -245,9 +237,7 @@ class ImmichAppState extends ConsumerState final immichTheme = ref.watch(immichThemeProvider); return ProviderScope( - overrides: [ - localeProvider.overrideWithValue(context.locale), - ], + overrides: [localeProvider.overrideWithValue(context.locale)], child: MaterialApp.router( title: 'Immich', debugShowCheckedModeBanner: true, @@ -255,18 +245,11 @@ class ImmichAppState extends ConsumerState supportedLocales: context.supportedLocales, locale: context.locale, themeMode: ref.watch(immichThemeModeProvider), - darkTheme: getThemeData( - colorScheme: immichTheme.dark, - locale: context.locale, - ), - theme: getThemeData( - colorScheme: immichTheme.light, - locale: context.locale, - ), + darkTheme: getThemeData(colorScheme: immichTheme.dark, locale: context.locale), + theme: getThemeData(colorScheme: immichTheme.light, locale: context.locale), routerConfig: router.config( deepLinkBuilder: _deepLinkBuilder, - navigatorObservers: () => - [AppNavigationObserver(ref: ref), HeroController()], + navigatorObservers: () => [AppNavigationObserver(ref: ref), HeroController()], ), ), ); diff --git a/mobile/lib/models/activities/activity.model.dart b/mobile/lib/models/activities/activity.model.dart index 17f70d5d62..38c2bef77a 100644 --- a/mobile/lib/models/activities/activity.model.dart +++ b/mobile/lib/models/activities/activity.model.dart @@ -56,12 +56,7 @@ class Activity { @override int get hashCode { - return id.hashCode ^ - assetId.hashCode ^ - comment.hashCode ^ - createdAt.hashCode ^ - type.hashCode ^ - user.hashCode; + return id.hashCode ^ assetId.hashCode ^ comment.hashCode ^ createdAt.hashCode ^ type.hashCode ^ user.hashCode; } } diff --git a/mobile/lib/models/albums/album_add_asset_response.model.dart b/mobile/lib/models/albums/album_add_asset_response.model.dart index 26168c957c..38dd989af5 100644 --- a/mobile/lib/models/albums/album_add_asset_response.model.dart +++ b/mobile/lib/models/albums/album_add_asset_response.model.dart @@ -7,15 +7,9 @@ class AlbumAddAssetsResponse { List alreadyInAlbum; int successfullyAdded; - AlbumAddAssetsResponse({ - required this.alreadyInAlbum, - required this.successfullyAdded, - }); + AlbumAddAssetsResponse({required this.alreadyInAlbum, required this.successfullyAdded}); - AlbumAddAssetsResponse copyWith({ - List? alreadyInAlbum, - int? successfullyAdded, - }) { + AlbumAddAssetsResponse copyWith({List? alreadyInAlbum, int? successfullyAdded}) { return AlbumAddAssetsResponse( alreadyInAlbum: alreadyInAlbum ?? this.alreadyInAlbum, successfullyAdded: successfullyAdded ?? this.successfullyAdded, @@ -23,25 +17,20 @@ class AlbumAddAssetsResponse { } Map toMap() { - return { - 'alreadyInAlbum': alreadyInAlbum, - 'successfullyAdded': successfullyAdded, - }; + return {'alreadyInAlbum': alreadyInAlbum, 'successfullyAdded': successfullyAdded}; } String toJson() => json.encode(toMap()); @override - String toString() => - 'AddAssetsResponse(alreadyInAlbum: $alreadyInAlbum, successfullyAdded: $successfullyAdded)'; + String toString() => 'AddAssetsResponse(alreadyInAlbum: $alreadyInAlbum, successfullyAdded: $successfullyAdded)'; @override bool operator ==(covariant AlbumAddAssetsResponse other) { if (identical(this, other)) return true; final listEquals = const DeepCollectionEquality().equals; - return listEquals(other.alreadyInAlbum, alreadyInAlbum) && - other.successfullyAdded == successfullyAdded; + return listEquals(other.alreadyInAlbum, alreadyInAlbum) && other.successfullyAdded == successfullyAdded; } @override diff --git a/mobile/lib/models/albums/album_search.model.dart b/mobile/lib/models/albums/album_search.model.dart index ac4eedbff1..4f69e7e2e1 100644 --- a/mobile/lib/models/albums/album_search.model.dart +++ b/mobile/lib/models/albums/album_search.model.dart @@ -1,5 +1 @@ -enum QuickFilterMode { - all, - sharedWithMe, - myAlbums, -} +enum QuickFilterMode { all, sharedWithMe, myAlbums } diff --git a/mobile/lib/models/albums/album_viewer_page_state.model.dart b/mobile/lib/models/albums/album_viewer_page_state.model.dart index 9fd5da1b28..70427899ae 100644 --- a/mobile/lib/models/albums/album_viewer_page_state.model.dart +++ b/mobile/lib/models/albums/album_viewer_page_state.model.dart @@ -11,11 +11,7 @@ class AlbumViewerPageState { required this.editDescriptionText, }); - AlbumViewerPageState copyWith({ - bool? isEditAlbum, - String? editTitleText, - String? editDescriptionText, - }) { + AlbumViewerPageState copyWith({bool? isEditAlbum, String? editTitleText, String? editDescriptionText}) { return AlbumViewerPageState( isEditAlbum: isEditAlbum ?? this.isEditAlbum, editTitleText: editTitleText ?? this.editTitleText, @@ -43,8 +39,7 @@ class AlbumViewerPageState { String toJson() => json.encode(toMap()); - factory AlbumViewerPageState.fromJson(String source) => - AlbumViewerPageState.fromMap(json.decode(source)); + factory AlbumViewerPageState.fromJson(String source) => AlbumViewerPageState.fromMap(json.decode(source)); @override String toString() => @@ -61,8 +56,5 @@ class AlbumViewerPageState { } @override - int get hashCode => - isEditAlbum.hashCode ^ - editTitleText.hashCode ^ - editDescriptionText.hashCode; + int get hashCode => isEditAlbum.hashCode ^ editTitleText.hashCode ^ editDescriptionText.hashCode; } diff --git a/mobile/lib/models/albums/asset_selection_page_result.model.dart b/mobile/lib/models/albums/asset_selection_page_result.model.dart index 2a4daba64a..cc750f397f 100644 --- a/mobile/lib/models/albums/asset_selection_page_result.model.dart +++ b/mobile/lib/models/albums/asset_selection_page_result.model.dart @@ -4,16 +4,13 @@ import 'package:immich_mobile/entities/asset.entity.dart'; class AssetSelectionPageResult { final Set selectedAssets; - const AssetSelectionPageResult({ - required this.selectedAssets, - }); + const AssetSelectionPageResult({required this.selectedAssets}); @override bool operator ==(Object other) { if (identical(this, other)) return true; final setEquals = const DeepCollectionEquality().equals; - return other is AssetSelectionPageResult && - setEquals(other.selectedAssets, selectedAssets); + return other is AssetSelectionPageResult && setEquals(other.selectedAssets, selectedAssets); } @override diff --git a/mobile/lib/models/asset_selection_state.dart b/mobile/lib/models/asset_selection_state.dart index b080dca003..aded3064ce 100644 --- a/mobile/lib/models/asset_selection_state.dart +++ b/mobile/lib/models/asset_selection_state.dart @@ -13,12 +13,7 @@ class AssetSelectionState { this.selectedCount = 0, }); - AssetSelectionState copyWith({ - bool? hasRemote, - bool? hasLocal, - bool? hasMerged, - int? selectedCount, - }) { + AssetSelectionState copyWith({bool? hasRemote, bool? hasLocal, bool? hasMerged, int? selectedCount}) { return AssetSelectionState( hasRemote: hasRemote ?? this.hasRemote, hasLocal: hasLocal ?? this.hasLocal, @@ -28,10 +23,10 @@ class AssetSelectionState { } AssetSelectionState.fromSelection(Set selection) - : hasLocal = selection.any((e) => e.storage == AssetState.local), - hasMerged = selection.any((e) => e.storage == AssetState.merged), - hasRemote = selection.any((e) => e.storage == AssetState.remote), - selectedCount = selection.length; + : hasLocal = selection.any((e) => e.storage == AssetState.local), + hasMerged = selection.any((e) => e.storage == AssetState.merged), + hasRemote = selection.any((e) => e.storage == AssetState.remote), + selectedCount = selection.length; @override String toString() => @@ -48,9 +43,5 @@ class AssetSelectionState { } @override - int get hashCode => - hasRemote.hashCode ^ - hasLocal.hashCode ^ - hasMerged.hashCode ^ - selectedCount.hashCode; + int get hashCode => hasRemote.hashCode ^ hasLocal.hashCode ^ hasMerged.hashCode ^ selectedCount.hashCode; } diff --git a/mobile/lib/models/auth/auxilary_endpoint.model.dart b/mobile/lib/models/auth/auxilary_endpoint.model.dart index f49d0f692c..c7f472e111 100644 --- a/mobile/lib/models/auth/auxilary_endpoint.model.dart +++ b/mobile/lib/models/auth/auxilary_endpoint.model.dart @@ -5,19 +5,10 @@ class AuxilaryEndpoint { final String url; final AuxCheckStatus status; - const AuxilaryEndpoint({ - required this.url, - required this.status, - }); + const AuxilaryEndpoint({required this.url, required this.status}); - AuxilaryEndpoint copyWith({ - String? url, - AuxCheckStatus? status, - }) { - return AuxilaryEndpoint( - url: url ?? this.url, - status: status ?? this.status, - ); + AuxilaryEndpoint copyWith({String? url, AuxCheckStatus? status}) { + return AuxilaryEndpoint(url: url ?? this.url, status: status ?? this.status); } @override @@ -34,10 +25,7 @@ class AuxilaryEndpoint { int get hashCode => url.hashCode ^ status.hashCode; Map toMap() { - return { - 'url': url, - 'status': status.toMap(), - }; + return {'url': url, 'status': status.toMap()}; } factory AuxilaryEndpoint.fromMap(Map map) { @@ -55,9 +43,7 @@ class AuxilaryEndpoint { class AuxCheckStatus { final String name; - const AuxCheckStatus({ - required this.name, - }); + const AuxCheckStatus({required this.name}); const AuxCheckStatus._(this.name); static const loading = AuxCheckStatus._('loading'); @@ -75,30 +61,21 @@ class AuxCheckStatus { @override int get hashCode => name.hashCode; - AuxCheckStatus copyWith({ - String? name, - }) { - return AuxCheckStatus( - name: name ?? this.name, - ); + AuxCheckStatus copyWith({String? name}) { + return AuxCheckStatus(name: name ?? this.name); } Map toMap() { - return { - 'name': name, - }; + return {'name': name}; } factory AuxCheckStatus.fromMap(Map map) { - return AuxCheckStatus( - name: map['name'] as String, - ); + return AuxCheckStatus(name: map['name'] as String); } String toJson() => json.encode(toMap()); - factory AuxCheckStatus.fromJson(String source) => - AuxCheckStatus.fromMap(json.decode(source) as Map); + factory AuxCheckStatus.fromJson(String source) => AuxCheckStatus.fromMap(json.decode(source) as Map); @override String toString() => 'AuxCheckStatus(name: $name)'; diff --git a/mobile/lib/models/auth/biometric_status.model.dart b/mobile/lib/models/auth/biometric_status.model.dart index 3057f06e9c..ad2b06be04 100644 --- a/mobile/lib/models/auth/biometric_status.model.dart +++ b/mobile/lib/models/auth/biometric_status.model.dart @@ -5,19 +5,12 @@ class BiometricStatus { final List availableBiometrics; final bool canAuthenticate; - const BiometricStatus({ - required this.availableBiometrics, - required this.canAuthenticate, - }); + const BiometricStatus({required this.availableBiometrics, required this.canAuthenticate}); @override - String toString() => - 'BiometricStatus(availableBiometrics: $availableBiometrics, canAuthenticate: $canAuthenticate)'; + String toString() => 'BiometricStatus(availableBiometrics: $availableBiometrics, canAuthenticate: $canAuthenticate)'; - BiometricStatus copyWith({ - List? availableBiometrics, - bool? canAuthenticate, - }) { + BiometricStatus copyWith({List? availableBiometrics, bool? canAuthenticate}) { return BiometricStatus( availableBiometrics: availableBiometrics ?? this.availableBiometrics, canAuthenticate: canAuthenticate ?? this.canAuthenticate, @@ -29,8 +22,7 @@ class BiometricStatus { if (identical(this, other)) return true; final listEquals = const DeepCollectionEquality().equals; - return listEquals(other.availableBiometrics, availableBiometrics) && - other.canAuthenticate == canAuthenticate; + return listEquals(other.availableBiometrics, availableBiometrics) && other.canAuthenticate == canAuthenticate; } @override diff --git a/mobile/lib/models/backup/available_album.model.dart b/mobile/lib/models/backup/available_album.model.dart index e34d2b1abe..502d0b66be 100644 --- a/mobile/lib/models/backup/available_album.model.dart +++ b/mobile/lib/models/backup/available_album.model.dart @@ -4,17 +4,9 @@ class AvailableAlbum { final Album album; final int assetCount; final DateTime? lastBackup; - const AvailableAlbum({ - required this.album, - required this.assetCount, - this.lastBackup, - }); + const AvailableAlbum({required this.album, required this.assetCount, this.lastBackup}); - AvailableAlbum copyWith({ - Album? album, - int? assetCount, - DateTime? lastBackup, - }) { + AvailableAlbum copyWith({Album? album, int? assetCount, DateTime? lastBackup}) { return AvailableAlbum( album: album ?? this.album, assetCount: assetCount ?? this.assetCount, @@ -29,8 +21,7 @@ class AvailableAlbum { bool get isAll => album.isAll; @override - String toString() => - 'AvailableAlbum(albumEntity: $album, lastBackup: $lastBackup)'; + String toString() => 'AvailableAlbum(albumEntity: $album, lastBackup: $lastBackup)'; @override bool operator ==(Object other) { diff --git a/mobile/lib/models/backup/backup_state.model.dart b/mobile/lib/models/backup/backup_state.model.dart index d829f411fc..635d925c3f 100644 --- a/mobile/lib/models/backup/backup_state.model.dart +++ b/mobile/lib/models/backup/backup_state.model.dart @@ -8,13 +8,7 @@ import 'package:immich_mobile/models/backup/available_album.model.dart'; import 'package:immich_mobile/models/backup/current_upload_asset.model.dart'; import 'package:immich_mobile/models/server_info/server_disk_info.model.dart'; -enum BackUpProgressEnum { - idle, - inProgress, - manualInProgress, - inBackground, - done -} +enum BackUpProgressEnum { idle, inProgress, manualInProgress, inBackground, done } class BackUpState { // enum @@ -105,26 +99,21 @@ class BackUpState { progressInFileSize: progressInFileSize ?? this.progressInFileSize, progressInFileSpeed: progressInFileSpeed ?? this.progressInFileSpeed, progressInFileSpeeds: progressInFileSpeeds ?? this.progressInFileSpeeds, - progressInFileSpeedUpdateTime: - progressInFileSpeedUpdateTime ?? this.progressInFileSpeedUpdateTime, - progressInFileSpeedUpdateSentBytes: progressInFileSpeedUpdateSentBytes ?? - this.progressInFileSpeedUpdateSentBytes, - iCloudDownloadProgress: - iCloudDownloadProgress ?? this.iCloudDownloadProgress, + progressInFileSpeedUpdateTime: progressInFileSpeedUpdateTime ?? this.progressInFileSpeedUpdateTime, + progressInFileSpeedUpdateSentBytes: progressInFileSpeedUpdateSentBytes ?? this.progressInFileSpeedUpdateSentBytes, + iCloudDownloadProgress: iCloudDownloadProgress ?? this.iCloudDownloadProgress, cancelToken: cancelToken ?? this.cancelToken, serverInfo: serverInfo ?? this.serverInfo, autoBackup: autoBackup ?? this.autoBackup, backgroundBackup: backgroundBackup ?? this.backgroundBackup, backupRequireWifi: backupRequireWifi ?? this.backupRequireWifi, - backupRequireCharging: - backupRequireCharging ?? this.backupRequireCharging, + backupRequireCharging: backupRequireCharging ?? this.backupRequireCharging, backupTriggerDelay: backupTriggerDelay ?? this.backupTriggerDelay, availableAlbums: availableAlbums ?? this.availableAlbums, selectedBackupAlbums: selectedBackupAlbums ?? this.selectedBackupAlbums, excludedBackupAlbums: excludedBackupAlbums ?? this.excludedBackupAlbums, allUniqueAssets: allUniqueAssets ?? this.allUniqueAssets, - selectedAlbumsBackupAssetsIds: - selectedAlbumsBackupAssetsIds ?? this.selectedAlbumsBackupAssetsIds, + selectedAlbumsBackupAssetsIds: selectedAlbumsBackupAssetsIds ?? this.selectedAlbumsBackupAssetsIds, currentUploadAsset: currentUploadAsset ?? this.currentUploadAsset, ); } @@ -146,8 +135,7 @@ class BackUpState { other.progressInFileSpeed == progressInFileSpeed && collectionEquals(other.progressInFileSpeeds, progressInFileSpeeds) && other.progressInFileSpeedUpdateTime == progressInFileSpeedUpdateTime && - other.progressInFileSpeedUpdateSentBytes == - progressInFileSpeedUpdateSentBytes && + other.progressInFileSpeedUpdateSentBytes == progressInFileSpeedUpdateSentBytes && other.iCloudDownloadProgress == iCloudDownloadProgress && other.cancelToken == cancelToken && other.serverInfo == serverInfo && @@ -160,10 +148,7 @@ class BackUpState { collectionEquals(other.selectedBackupAlbums, selectedBackupAlbums) && collectionEquals(other.excludedBackupAlbums, excludedBackupAlbums) && collectionEquals(other.allUniqueAssets, allUniqueAssets) && - collectionEquals( - other.selectedAlbumsBackupAssetsIds, - selectedAlbumsBackupAssetsIds, - ) && + collectionEquals(other.selectedAlbumsBackupAssetsIds, selectedAlbumsBackupAssetsIds) && other.currentUploadAsset == currentUploadAsset; } diff --git a/mobile/lib/models/backup/current_upload_asset.model.dart b/mobile/lib/models/backup/current_upload_asset.model.dart index edaac06987..2214897357 100644 --- a/mobile/lib/models/backup/current_upload_asset.model.dart +++ b/mobile/lib/models/backup/current_upload_asset.model.dart @@ -53,13 +53,11 @@ class CurrentUploadAsset { factory CurrentUploadAsset.fromMap(Map map) { return CurrentUploadAsset( id: map['id'] as String, - fileCreatedAt: - DateTime.fromMillisecondsSinceEpoch(map['fileCreatedAt'] as int), + fileCreatedAt: DateTime.fromMillisecondsSinceEpoch(map['fileCreatedAt'] as int), fileName: map['fileName'] as String, fileType: map['fileType'] as String, fileSize: map['fileSize'] as int, - iCloudAsset: - map['iCloudAsset'] != null ? map['iCloudAsset'] as bool : null, + iCloudAsset: map['iCloudAsset'] != null ? map['iCloudAsset'] as bool : null, ); } diff --git a/mobile/lib/models/backup/manual_upload_state.model.dart b/mobile/lib/models/backup/manual_upload_state.model.dart index a2d84fbef3..7f797334de 100644 --- a/mobile/lib/models/backup/manual_upload_state.model.dart +++ b/mobile/lib/models/backup/manual_upload_state.model.dart @@ -56,17 +56,14 @@ class ManualUploadState { progressInFileSize: progressInFileSize ?? this.progressInFileSize, progressInFileSpeed: progressInFileSpeed ?? this.progressInFileSpeed, progressInFileSpeeds: progressInFileSpeeds ?? this.progressInFileSpeeds, - progressInFileSpeedUpdateTime: - progressInFileSpeedUpdateTime ?? this.progressInFileSpeedUpdateTime, - progressInFileSpeedUpdateSentBytes: progressInFileSpeedUpdateSentBytes ?? - this.progressInFileSpeedUpdateSentBytes, + progressInFileSpeedUpdateTime: progressInFileSpeedUpdateTime ?? this.progressInFileSpeedUpdateTime, + progressInFileSpeedUpdateSentBytes: progressInFileSpeedUpdateSentBytes ?? this.progressInFileSpeedUpdateSentBytes, cancelToken: cancelToken ?? this.cancelToken, currentUploadAsset: currentUploadAsset ?? this.currentUploadAsset, totalAssetsToUpload: totalAssetsToUpload ?? this.totalAssetsToUpload, currentAssetIndex: currentAssetIndex ?? this.currentAssetIndex, successfulUploads: successfulUploads ?? this.successfulUploads, - showDetailedNotification: - showDetailedNotification ?? this.showDetailedNotification, + showDetailedNotification: showDetailedNotification ?? this.showDetailedNotification, ); } @@ -86,8 +83,7 @@ class ManualUploadState { other.progressInFileSpeed == progressInFileSpeed && collectionEquals(other.progressInFileSpeeds, progressInFileSpeeds) && other.progressInFileSpeedUpdateTime == progressInFileSpeedUpdateTime && - other.progressInFileSpeedUpdateSentBytes == - progressInFileSpeedUpdateSentBytes && + other.progressInFileSpeedUpdateSentBytes == progressInFileSpeedUpdateSentBytes && other.cancelToken == cancelToken && other.currentUploadAsset == currentUploadAsset && other.totalAssetsToUpload == totalAssetsToUpload && diff --git a/mobile/lib/models/backup/success_upload_asset.model.dart b/mobile/lib/models/backup/success_upload_asset.model.dart index 2b39dde5ea..da1e104ba3 100644 --- a/mobile/lib/models/backup/success_upload_asset.model.dart +++ b/mobile/lib/models/backup/success_upload_asset.model.dart @@ -5,17 +5,9 @@ class SuccessUploadAsset { final String remoteAssetId; final bool isDuplicate; - const SuccessUploadAsset({ - required this.candidate, - required this.remoteAssetId, - required this.isDuplicate, - }); + const SuccessUploadAsset({required this.candidate, required this.remoteAssetId, required this.isDuplicate}); - SuccessUploadAsset copyWith({ - BackupCandidate? candidate, - String? remoteAssetId, - bool? isDuplicate, - }) { + SuccessUploadAsset copyWith({BackupCandidate? candidate, String? remoteAssetId, bool? isDuplicate}) { return SuccessUploadAsset( candidate: candidate ?? this.candidate, remoteAssetId: remoteAssetId ?? this.remoteAssetId, @@ -31,12 +23,9 @@ class SuccessUploadAsset { bool operator ==(covariant SuccessUploadAsset other) { if (identical(this, other)) return true; - return other.candidate == candidate && - other.remoteAssetId == remoteAssetId && - other.isDuplicate == isDuplicate; + return other.candidate == candidate && other.remoteAssetId == remoteAssetId && other.isDuplicate == isDuplicate; } @override - int get hashCode => - candidate.hashCode ^ remoteAssetId.hashCode ^ isDuplicate.hashCode; + int get hashCode => candidate.hashCode ^ remoteAssetId.hashCode ^ isDuplicate.hashCode; } diff --git a/mobile/lib/models/cast/cast_manager_state.dart b/mobile/lib/models/cast/cast_manager_state.dart index 703ceb4c47..c948921792 100644 --- a/mobile/lib/models/cast/cast_manager_state.dart +++ b/mobile/lib/models/cast/cast_manager_state.dart @@ -59,8 +59,7 @@ class CastManagerState { String toJson() => json.encode(toMap()); - factory CastManagerState.fromJson(String source) => - CastManagerState.fromMap(json.decode(source)); + factory CastManagerState.fromJson(String source) => CastManagerState.fromMap(json.decode(source)); @override String toString() => @@ -80,9 +79,5 @@ class CastManagerState { @override int get hashCode => - isCasting.hashCode ^ - receiverName.hashCode ^ - castState.hashCode ^ - currentTime.hashCode ^ - duration.hashCode; + isCasting.hashCode ^ receiverName.hashCode ^ castState.hashCode ^ currentTime.hashCode ^ duration.hashCode; } diff --git a/mobile/lib/models/download/download_state.model.dart b/mobile/lib/models/download/download_state.model.dart index c032c73dcd..82d4e31253 100644 --- a/mobile/lib/models/download/download_state.model.dart +++ b/mobile/lib/models/download/download_state.model.dart @@ -10,17 +10,9 @@ class DownloadInfo { // enum final TaskStatus status; - const DownloadInfo({ - required this.fileName, - required this.progress, - required this.status, - }); + const DownloadInfo({required this.fileName, required this.progress, required this.status}); - DownloadInfo copyWith({ - String? fileName, - double? progress, - TaskStatus? status, - }) { + DownloadInfo copyWith({String? fileName, double? progress, TaskStatus? status}) { return DownloadInfo( fileName: fileName ?? this.fileName, progress: progress ?? this.progress, @@ -29,11 +21,7 @@ class DownloadInfo { } Map toMap() { - return { - 'fileName': fileName, - 'progress': progress, - 'status': status.index, - }; + return {'fileName': fileName, 'progress': progress, 'status': status.index}; } factory DownloadInfo.fromMap(Map map) { @@ -46,20 +34,16 @@ class DownloadInfo { String toJson() => json.encode(toMap()); - factory DownloadInfo.fromJson(String source) => - DownloadInfo.fromMap(json.decode(source) as Map); + factory DownloadInfo.fromJson(String source) => DownloadInfo.fromMap(json.decode(source) as Map); @override - String toString() => - 'DownloadInfo(fileName: $fileName, progress: $progress, status: $status)'; + String toString() => 'DownloadInfo(fileName: $fileName, progress: $progress, status: $status)'; @override bool operator ==(covariant DownloadInfo other) { if (identical(this, other)) return true; - return other.fileName == fileName && - other.progress == progress && - other.status == status; + return other.fileName == fileName && other.progress == progress && other.status == status; } @override @@ -71,17 +55,9 @@ class DownloadState { final TaskStatus downloadStatus; final Map taskProgress; final bool showProgress; - const DownloadState({ - required this.downloadStatus, - required this.taskProgress, - required this.showProgress, - }); + const DownloadState({required this.downloadStatus, required this.taskProgress, required this.showProgress}); - DownloadState copyWith({ - TaskStatus? downloadStatus, - Map? taskProgress, - bool? showProgress, - }) { + DownloadState copyWith({TaskStatus? downloadStatus, Map? taskProgress, bool? showProgress}) { return DownloadState( downloadStatus: downloadStatus ?? this.downloadStatus, taskProgress: taskProgress ?? this.taskProgress, @@ -104,6 +80,5 @@ class DownloadState { } @override - int get hashCode => - downloadStatus.hashCode ^ taskProgress.hashCode ^ showProgress.hashCode; + int get hashCode => downloadStatus.hashCode ^ taskProgress.hashCode ^ showProgress.hashCode; } diff --git a/mobile/lib/models/download/livephotos_medatada.model.dart b/mobile/lib/models/download/livephotos_medatada.model.dart index 9c0c7ae4e9..f77a1514ac 100644 --- a/mobile/lib/models/download/livephotos_medatada.model.dart +++ b/mobile/lib/models/download/livephotos_medatada.model.dart @@ -1,43 +1,25 @@ // ignore_for_file: public_member_api_docs, sort_constructors_first import 'dart:convert'; -enum LivePhotosPart { - video, - image, -} +enum LivePhotosPart { video, image } class LivePhotosMetadata { // enum LivePhotosPart part; String id; - LivePhotosMetadata({ - required this.part, - required this.id, - }); + LivePhotosMetadata({required this.part, required this.id}); - LivePhotosMetadata copyWith({ - LivePhotosPart? part, - String? id, - }) { - return LivePhotosMetadata( - part: part ?? this.part, - id: id ?? this.id, - ); + LivePhotosMetadata copyWith({LivePhotosPart? part, String? id}) { + return LivePhotosMetadata(part: part ?? this.part, id: id ?? this.id); } Map toMap() { - return { - 'part': part.index, - 'id': id, - }; + return {'part': part.index, 'id': id}; } factory LivePhotosMetadata.fromMap(Map map) { - return LivePhotosMetadata( - part: LivePhotosPart.values[map['part'] as int], - id: map['id'] as String, - ); + return LivePhotosMetadata(part: LivePhotosPart.values[map['part'] as int], id: map['id'] as String); } String toJson() => json.encode(toMap()); diff --git a/mobile/lib/models/folder/recursive_folder.model.dart b/mobile/lib/models/folder/recursive_folder.model.dart index 62ec670fed..33ac0f4cb4 100644 --- a/mobile/lib/models/folder/recursive_folder.model.dart +++ b/mobile/lib/models/folder/recursive_folder.model.dart @@ -3,9 +3,5 @@ import 'package:immich_mobile/models/folder/root_folder.model.dart'; class RecursiveFolder extends RootFolder { final String name; - const RecursiveFolder({ - required this.name, - required super.path, - required super.subfolders, - }); + const RecursiveFolder({required this.name, required super.path, required super.subfolders}); } diff --git a/mobile/lib/models/folder/root_folder.model.dart b/mobile/lib/models/folder/root_folder.model.dart index 567093ecd5..d4b791b915 100644 --- a/mobile/lib/models/folder/root_folder.model.dart +++ b/mobile/lib/models/folder/root_folder.model.dart @@ -4,8 +4,5 @@ class RootFolder { final List subfolders; final String path; - const RootFolder({ - required this.subfolders, - required this.path, - }); + const RootFolder({required this.subfolders, required this.path}); } diff --git a/mobile/lib/models/map/map_marker.model.dart b/mobile/lib/models/map/map_marker.model.dart index 7ab925464a..0f425306ff 100644 --- a/mobile/lib/models/map/map_marker.model.dart +++ b/mobile/lib/models/map/map_marker.model.dart @@ -4,28 +4,16 @@ import 'package:openapi/api.dart'; class MapMarker { final LatLng latLng; final String assetRemoteId; - const MapMarker({ - required this.latLng, - required this.assetRemoteId, - }); + const MapMarker({required this.latLng, required this.assetRemoteId}); - MapMarker copyWith({ - LatLng? latLng, - String? assetRemoteId, - }) { - return MapMarker( - latLng: latLng ?? this.latLng, - assetRemoteId: assetRemoteId ?? this.assetRemoteId, - ); + MapMarker copyWith({LatLng? latLng, String? assetRemoteId}) { + return MapMarker(latLng: latLng ?? this.latLng, assetRemoteId: assetRemoteId ?? this.assetRemoteId); } - MapMarker.fromDto(MapMarkerResponseDto dto) - : latLng = LatLng(dto.lat, dto.lon), - assetRemoteId = dto.id; + MapMarker.fromDto(MapMarkerResponseDto dto) : latLng = LatLng(dto.lat, dto.lon), assetRemoteId = dto.id; @override - String toString() => - 'MapMarker(latLng: $latLng, assetRemoteId: $assetRemoteId)'; + String toString() => 'MapMarker(latLng: $latLng, assetRemoteId: $assetRemoteId)'; @override bool operator ==(covariant MapMarker other) { diff --git a/mobile/lib/models/memories/memory.model.dart b/mobile/lib/models/memories/memory.model.dart index c8451dda64..8a9db5d51b 100644 --- a/mobile/lib/models/memories/memory.model.dart +++ b/mobile/lib/models/memories/memory.model.dart @@ -7,19 +7,10 @@ import 'package:immich_mobile/entities/asset.entity.dart'; class Memory { final String title; final List assets; - const Memory({ - required this.title, - required this.assets, - }); + const Memory({required this.title, required this.assets}); - Memory copyWith({ - String? title, - List? assets, - }) { - return Memory( - title: title ?? this.title, - assets: assets ?? this.assets, - ); + Memory copyWith({String? title, List? assets}) { + return Memory(title: title ?? this.title, assets: assets ?? this.assets); } @override @@ -30,9 +21,7 @@ class Memory { if (identical(this, other)) return true; final listEquals = const DeepCollectionEquality().equals; - return other is Memory && - other.title == title && - listEquals(other.assets, assets); + return other is Memory && other.title == title && listEquals(other.assets, assets); } @override diff --git a/mobile/lib/models/search/search_curated_content.model.dart b/mobile/lib/models/search/search_curated_content.model.dart index aff61097e2..6e4a083876 100644 --- a/mobile/lib/models/search/search_curated_content.model.dart +++ b/mobile/lib/models/search/search_curated_content.model.dart @@ -14,30 +14,14 @@ class SearchCuratedContent { /// The id to lookup the asset from the server final String id; - const SearchCuratedContent({ - required this.label, - required this.id, - this.subtitle, - }); + const SearchCuratedContent({required this.label, required this.id, this.subtitle}); - SearchCuratedContent copyWith({ - String? label, - String? subtitle, - String? id, - }) { - return SearchCuratedContent( - label: label ?? this.label, - subtitle: subtitle ?? this.subtitle, - id: id ?? this.id, - ); + SearchCuratedContent copyWith({String? label, String? subtitle, String? id}) { + return SearchCuratedContent(label: label ?? this.label, subtitle: subtitle ?? this.subtitle, id: id ?? this.id); } Map toMap() { - return { - 'label': label, - 'subtitle': subtitle, - 'id': id, - }; + return {'label': label, 'subtitle': subtitle, 'id': id}; } factory SearchCuratedContent.fromMap(Map map) { @@ -54,8 +38,7 @@ class SearchCuratedContent { SearchCuratedContent.fromMap(json.decode(source) as Map); @override - String toString() => - 'CuratedContent(label: $label, subtitle: $subtitle, id: $id)'; + String toString() => 'CuratedContent(label: $label, subtitle: $subtitle, id: $id)'; @override bool operator ==(covariant SearchCuratedContent other) { diff --git a/mobile/lib/models/search/search_filter.model.dart b/mobile/lib/models/search/search_filter.model.dart index 835e6aff8f..7f27b4d333 100644 --- a/mobile/lib/models/search/search_filter.model.dart +++ b/mobile/lib/models/search/search_filter.model.dart @@ -8,30 +8,14 @@ class SearchLocationFilter { String? country; String? state; String? city; - SearchLocationFilter({ - this.country, - this.state, - this.city, - }); + SearchLocationFilter({this.country, this.state, this.city}); - SearchLocationFilter copyWith({ - String? country, - String? state, - String? city, - }) { - return SearchLocationFilter( - country: country ?? this.country, - state: state ?? this.state, - city: city ?? this.city, - ); + SearchLocationFilter copyWith({String? country, String? state, String? city}) { + return SearchLocationFilter(country: country ?? this.country, state: state ?? this.state, city: city ?? this.city); } Map toMap() { - return { - 'country': country, - 'state': state, - 'city': city, - }; + return {'country': country, 'state': state, 'city': city}; } factory SearchLocationFilter.fromMap(Map map) { @@ -48,16 +32,13 @@ class SearchLocationFilter { SearchLocationFilter.fromMap(json.decode(source) as Map); @override - String toString() => - 'SearchLocationFilter(country: $country, state: $state, city: $city)'; + String toString() => 'SearchLocationFilter(country: $country, state: $state, city: $city)'; @override bool operator ==(covariant SearchLocationFilter other) { if (identical(this, other)) return true; - return other.country == country && - other.state == state && - other.city == city; + return other.country == country && other.state == state && other.city == city; } @override @@ -67,26 +48,14 @@ class SearchLocationFilter { class SearchCameraFilter { String? make; String? model; - SearchCameraFilter({ - this.make, - this.model, - }); + SearchCameraFilter({this.make, this.model}); - SearchCameraFilter copyWith({ - String? make, - String? model, - }) { - return SearchCameraFilter( - make: make ?? this.make, - model: model ?? this.model, - ); + SearchCameraFilter copyWith({String? make, String? model}) { + return SearchCameraFilter(make: make ?? this.make, model: model ?? this.model); } Map toMap() { - return { - 'make': make, - 'model': model, - }; + return {'make': make, 'model': model}; } factory SearchCameraFilter.fromMap(Map map) { @@ -118,19 +87,10 @@ class SearchCameraFilter { class SearchDateFilter { DateTime? takenBefore; DateTime? takenAfter; - SearchDateFilter({ - this.takenBefore, - this.takenAfter, - }); + SearchDateFilter({this.takenBefore, this.takenAfter}); - SearchDateFilter copyWith({ - DateTime? takenBefore, - DateTime? takenAfter, - }) { - return SearchDateFilter( - takenBefore: takenBefore ?? this.takenBefore, - takenAfter: takenAfter ?? this.takenAfter, - ); + SearchDateFilter copyWith({DateTime? takenBefore, DateTime? takenAfter}) { + return SearchDateFilter(takenBefore: takenBefore ?? this.takenBefore, takenAfter: takenAfter ?? this.takenAfter); } Map toMap() { @@ -142,12 +102,8 @@ class SearchDateFilter { factory SearchDateFilter.fromMap(Map map) { return SearchDateFilter( - takenBefore: map['takenBefore'] != null - ? DateTime.fromMillisecondsSinceEpoch(map['takenBefore'] as int) - : null, - takenAfter: map['takenAfter'] != null - ? DateTime.fromMillisecondsSinceEpoch(map['takenAfter'] as int) - : null, + takenBefore: map['takenBefore'] != null ? DateTime.fromMillisecondsSinceEpoch(map['takenBefore'] as int) : null, + takenAfter: map['takenAfter'] != null ? DateTime.fromMillisecondsSinceEpoch(map['takenAfter'] as int) : null, ); } @@ -157,8 +113,7 @@ class SearchDateFilter { SearchDateFilter.fromMap(json.decode(source) as Map); @override - String toString() => - 'SearchDateFilter(takenBefore: $takenBefore, takenAfter: $takenAfter)'; + String toString() => 'SearchDateFilter(takenBefore: $takenBefore, takenAfter: $takenAfter)'; @override bool operator ==(covariant SearchDateFilter other) { @@ -175,17 +130,9 @@ class SearchDisplayFilters { bool isNotInAlbum = false; bool isArchive = false; bool isFavorite = false; - SearchDisplayFilters({ - required this.isNotInAlbum, - required this.isArchive, - required this.isFavorite, - }); + SearchDisplayFilters({required this.isNotInAlbum, required this.isArchive, required this.isFavorite}); - SearchDisplayFilters copyWith({ - bool? isNotInAlbum, - bool? isArchive, - bool? isFavorite, - }) { + SearchDisplayFilters copyWith({bool? isNotInAlbum, bool? isArchive, bool? isFavorite}) { return SearchDisplayFilters( isNotInAlbum: isNotInAlbum ?? this.isNotInAlbum, isArchive: isArchive ?? this.isArchive, @@ -194,11 +141,7 @@ class SearchDisplayFilters { } Map toMap() { - return { - 'isNotInAlbum': isNotInAlbum, - 'isArchive': isArchive, - 'isFavorite': isFavorite, - }; + return {'isNotInAlbum': isNotInAlbum, 'isArchive': isArchive, 'isFavorite': isFavorite}; } factory SearchDisplayFilters.fromMap(Map map) { @@ -222,14 +165,11 @@ class SearchDisplayFilters { bool operator ==(covariant SearchDisplayFilters other) { if (identical(this, other)) return true; - return other.isNotInAlbum == isNotInAlbum && - other.isArchive == isArchive && - other.isFavorite == isFavorite; + return other.isNotInAlbum == isNotInAlbum && other.isArchive == isArchive && other.isFavorite == isFavorite; } @override - int get hashCode => - isNotInAlbum.hashCode ^ isArchive.hashCode ^ isFavorite.hashCode; + int get hashCode => isNotInAlbum.hashCode ^ isArchive.hashCode ^ isFavorite.hashCode; } class SearchFilter { @@ -237,7 +177,7 @@ class SearchFilter { String? filename; String? description; String? language; - Set people; + Set people; SearchLocationFilter location; SearchCameraFilter camera; SearchDateFilter date; @@ -282,7 +222,7 @@ class SearchFilter { String? filename, String? description, String? language, - Set? people, + Set? people, SearchLocationFilter? location, SearchCameraFilter? camera, SearchDateFilter? date, diff --git a/mobile/lib/models/search/search_result.model.dart b/mobile/lib/models/search/search_result.model.dart index 458a9b4abc..02553869bf 100644 --- a/mobile/lib/models/search/search_result.model.dart +++ b/mobile/lib/models/search/search_result.model.dart @@ -6,19 +6,10 @@ class SearchResult { final List assets; final int? nextPage; - const SearchResult({ - required this.assets, - this.nextPage, - }); + const SearchResult({required this.assets, this.nextPage}); - SearchResult copyWith({ - List? assets, - int? nextPage, - }) { - return SearchResult( - assets: assets ?? this.assets, - nextPage: nextPage ?? this.nextPage, - ); + SearchResult copyWith({List? assets, int? nextPage}) { + return SearchResult(assets: assets ?? this.assets, nextPage: nextPage ?? this.nextPage); } @override diff --git a/mobile/lib/models/search/search_result_page_state.model.dart b/mobile/lib/models/search/search_result_page_state.model.dart index 2fd4dcbcd3..7c8a27b50c 100644 --- a/mobile/lib/models/search/search_result_page_state.model.dart +++ b/mobile/lib/models/search/search_result_page_state.model.dart @@ -52,10 +52,6 @@ class SearchResultPageState { @override int get hashCode { - return isLoading.hashCode ^ - isSuccess.hashCode ^ - isError.hashCode ^ - isSmart.hashCode ^ - searchResult.hashCode; + return isLoading.hashCode ^ isSuccess.hashCode ^ isError.hashCode ^ isSmart.hashCode ^ searchResult.hashCode; } } diff --git a/mobile/lib/models/server_info/server_config.model.dart b/mobile/lib/models/server_info/server_config.model.dart index f07ffde522..37b98afadb 100644 --- a/mobile/lib/models/server_info/server_config.model.dart +++ b/mobile/lib/models/server_info/server_config.model.dart @@ -15,11 +15,7 @@ class ServerConfig { required this.mapLightStyleUrl, }); - ServerConfig copyWith({ - int? trashDays, - String? oauthButtonText, - String? externalDomain, - }) { + ServerConfig copyWith({int? trashDays, String? oauthButtonText, String? externalDomain}) { return ServerConfig( trashDays: trashDays ?? this.trashDays, oauthButtonText: oauthButtonText ?? this.oauthButtonText, @@ -34,11 +30,11 @@ class ServerConfig { 'ServerConfig(trashDays: $trashDays, oauthButtonText: $oauthButtonText, externalDomain: $externalDomain)'; ServerConfig.fromDto(ServerConfigDto dto) - : trashDays = dto.trashDays, - oauthButtonText = dto.oauthButtonText, - externalDomain = dto.externalDomain, - mapDarkStyleUrl = dto.mapDarkStyleUrl, - mapLightStyleUrl = dto.mapLightStyleUrl; + : trashDays = dto.trashDays, + oauthButtonText = dto.oauthButtonText, + externalDomain = dto.externalDomain, + mapDarkStyleUrl = dto.mapDarkStyleUrl, + mapLightStyleUrl = dto.mapLightStyleUrl; @override bool operator ==(covariant ServerConfig other) { @@ -50,6 +46,5 @@ class ServerConfig { } @override - int get hashCode => - trashDays.hashCode ^ oauthButtonText.hashCode ^ externalDomain.hashCode; + int get hashCode => trashDays.hashCode ^ oauthButtonText.hashCode ^ externalDomain.hashCode; } diff --git a/mobile/lib/models/server_info/server_disk_info.model.dart b/mobile/lib/models/server_info/server_disk_info.model.dart index 01ce49beec..01042b9f6d 100644 --- a/mobile/lib/models/server_info/server_disk_info.model.dart +++ b/mobile/lib/models/server_info/server_disk_info.model.dart @@ -13,12 +13,7 @@ class ServerDiskInfo { required this.diskUsagePercentage, }); - ServerDiskInfo copyWith({ - String? diskAvailable, - String? diskSize, - String? diskUse, - double? diskUsagePercentage, - }) { + ServerDiskInfo copyWith({String? diskAvailable, String? diskSize, String? diskUse, double? diskUsagePercentage}) { return ServerDiskInfo( diskAvailable: diskAvailable ?? this.diskAvailable, diskSize: diskSize ?? this.diskSize, @@ -33,10 +28,10 @@ class ServerDiskInfo { } ServerDiskInfo.fromDto(ServerStorageResponseDto dto) - : diskAvailable = dto.diskAvailable, - diskSize = dto.diskSize, - diskUse = dto.diskUse, - diskUsagePercentage = dto.diskUsagePercentage; + : diskAvailable = dto.diskAvailable, + diskSize = dto.diskSize, + diskUse = dto.diskUse, + diskUsagePercentage = dto.diskUsagePercentage; @override bool operator ==(Object other) { @@ -51,9 +46,6 @@ class ServerDiskInfo { @override int get hashCode { - return diskAvailable.hashCode ^ - diskSize.hashCode ^ - diskUse.hashCode ^ - diskUsagePercentage.hashCode; + return diskAvailable.hashCode ^ diskSize.hashCode ^ diskUse.hashCode ^ diskUsagePercentage.hashCode; } } diff --git a/mobile/lib/models/server_info/server_features.model.dart b/mobile/lib/models/server_info/server_features.model.dart index fee88869ed..20b9f29619 100644 --- a/mobile/lib/models/server_info/server_features.model.dart +++ b/mobile/lib/models/server_info/server_features.model.dart @@ -13,12 +13,7 @@ class ServerFeatures { required this.passwordLogin, }); - ServerFeatures copyWith({ - bool? trash, - bool? map, - bool? oauthEnabled, - bool? passwordLogin, - }) { + ServerFeatures copyWith({bool? trash, bool? map, bool? oauthEnabled, bool? passwordLogin}) { return ServerFeatures( trash: trash ?? this.trash, map: map ?? this.map, @@ -33,10 +28,10 @@ class ServerFeatures { } ServerFeatures.fromDto(ServerFeaturesDto dto) - : trash = dto.trash, - map = dto.map, - oauthEnabled = dto.oauth, - passwordLogin = dto.passwordLogin; + : trash = dto.trash, + map = dto.map, + oauthEnabled = dto.oauth, + passwordLogin = dto.passwordLogin; @override bool operator ==(covariant ServerFeatures other) { @@ -50,9 +45,6 @@ class ServerFeatures { @override int get hashCode { - return trash.hashCode ^ - map.hashCode ^ - oauthEnabled.hashCode ^ - passwordLogin.hashCode; + return trash.hashCode ^ map.hashCode ^ oauthEnabled.hashCode ^ passwordLogin.hashCode; } } diff --git a/mobile/lib/models/server_info/server_info.model.dart b/mobile/lib/models/server_info/server_info.model.dart index aafab56e4c..0fa80d45d8 100644 --- a/mobile/lib/models/server_info/server_info.model.dart +++ b/mobile/lib/models/server_info/server_info.model.dart @@ -41,10 +41,8 @@ class ServerInfo { serverConfig: serverConfig ?? this.serverConfig, serverDiskInfo: serverDiskInfo ?? this.serverDiskInfo, isVersionMismatch: isVersionMismatch ?? this.isVersionMismatch, - isNewReleaseAvailable: - isNewReleaseAvailable ?? this.isNewReleaseAvailable, - versionMismatchErrorMessage: - versionMismatchErrorMessage ?? this.versionMismatchErrorMessage, + isNewReleaseAvailable: isNewReleaseAvailable ?? this.isNewReleaseAvailable, + versionMismatchErrorMessage: versionMismatchErrorMessage ?? this.versionMismatchErrorMessage, ); } diff --git a/mobile/lib/models/server_info/server_version.model.dart b/mobile/lib/models/server_info/server_version.model.dart index 1995edb98d..2cb41b0415 100644 --- a/mobile/lib/models/server_info/server_version.model.dart +++ b/mobile/lib/models/server_info/server_version.model.dart @@ -5,22 +5,10 @@ class ServerVersion { final int minor; final int patch; - const ServerVersion({ - required this.major, - required this.minor, - required this.patch, - }); + const ServerVersion({required this.major, required this.minor, required this.patch}); - ServerVersion copyWith({ - int? major, - int? minor, - int? patch, - }) { - return ServerVersion( - major: major ?? this.major, - minor: minor ?? this.minor, - patch: patch ?? this.patch, - ); + ServerVersion copyWith({int? major, int? minor, int? patch}) { + return ServerVersion(major: major ?? this.major, minor: minor ?? this.minor, patch: patch ?? this.patch); } @override @@ -28,19 +16,13 @@ class ServerVersion { return 'ServerVersion(major: $major, minor: $minor, patch: $patch)'; } - ServerVersion.fromDto(ServerVersionResponseDto dto) - : major = dto.major, - minor = dto.minor, - patch = dto.patch_; + ServerVersion.fromDto(ServerVersionResponseDto dto) : major = dto.major, minor = dto.minor, patch = dto.patch_; @override bool operator ==(Object other) { if (identical(this, other)) return true; - return other is ServerVersion && - other.major == major && - other.minor == minor && - other.patch == patch; + return other is ServerVersion && other.major == major && other.minor == minor && other.patch == patch; } @override diff --git a/mobile/lib/models/shared_link/shared_link.model.dart b/mobile/lib/models/shared_link/shared_link.model.dart index a107dd892a..57a1f441eb 100644 --- a/mobile/lib/models/shared_link/shared_link.model.dart +++ b/mobile/lib/models/shared_link/shared_link.model.dart @@ -58,25 +58,23 @@ class SharedLink { } SharedLink.fromDto(SharedLinkResponseDto dto) - : id = dto.id, - allowDownload = dto.allowDownload, - allowUpload = dto.allowUpload, - description = dto.description, - password = dto.password, - expiresAt = dto.expiresAt, - key = dto.key, - showMetadata = dto.showMetadata, - type = dto.type == SharedLinkType.ALBUM - ? SharedLinkSource.album - : SharedLinkSource.individual, - title = dto.type == SharedLinkType.ALBUM - ? dto.album?.albumName.toUpperCase() ?? "UNKNOWN SHARE" - : "INDIVIDUAL SHARE", - thumbAssetId = dto.type == SharedLinkType.ALBUM - ? dto.album?.albumThumbnailAssetId - : dto.assets.isNotEmpty - ? dto.assets[0].id - : null; + : id = dto.id, + allowDownload = dto.allowDownload, + allowUpload = dto.allowUpload, + description = dto.description, + password = dto.password, + expiresAt = dto.expiresAt, + key = dto.key, + showMetadata = dto.showMetadata, + type = dto.type == SharedLinkType.ALBUM ? SharedLinkSource.album : SharedLinkSource.individual, + title = dto.type == SharedLinkType.ALBUM + ? dto.album?.albumName.toUpperCase() ?? "UNKNOWN SHARE" + : "INDIVIDUAL SHARE", + thumbAssetId = dto.type == SharedLinkType.ALBUM + ? dto.album?.albumThumbnailAssetId + : dto.assets.isNotEmpty + ? dto.assets[0].id + : null; @override String toString() => diff --git a/mobile/lib/models/upload/share_intent_attachment.model.dart b/mobile/lib/models/upload/share_intent_attachment.model.dart index 1bdb5b6b48..ae05e4c492 100644 --- a/mobile/lib/models/upload/share_intent_attachment.model.dart +++ b/mobile/lib/models/upload/share_intent_attachment.model.dart @@ -5,21 +5,9 @@ import 'dart:io'; import 'package:immich_mobile/utils/bytes_units.dart'; import 'package:path/path.dart'; -enum ShareIntentAttachmentType { - image, - video, -} +enum ShareIntentAttachmentType { image, video } -enum UploadStatus { - enqueued, - running, - complete, - notFound, - failed, - canceled, - waitingtoRetry, - paused, -} +enum UploadStatus { enqueued, running, complete, notFound, failed, canceled, waitingToRetry, paused } class ShareIntentAttachment { final String path; @@ -91,9 +79,7 @@ class ShareIntentAttachment { String toJson() => json.encode(toMap()); factory ShareIntentAttachment.fromJson(String source) => - ShareIntentAttachment.fromMap( - json.decode(source) as Map, - ); + ShareIntentAttachment.fromMap(json.decode(source) as Map); @override String toString() { diff --git a/mobile/lib/pages/album/album_additional_shared_user_selection.page.dart b/mobile/lib/pages/album/album_additional_shared_user_selection.page.dart index 62406d2e3a..f40ac9ccae 100644 --- a/mobile/lib/pages/album/album_additional_shared_user_selection.page.dart +++ b/mobile/lib/pages/album/album_additional_shared_user_selection.page.dart @@ -14,15 +14,11 @@ import 'package:immich_mobile/widgets/common/user_circle_avatar.dart'; class AlbumAdditionalSharedUserSelectionPage extends HookConsumerWidget { final Album album; - const AlbumAdditionalSharedUserSelectionPage({ - super.key, - required this.album, - }); + const AlbumAdditionalSharedUserSelectionPage({super.key, required this.album}); @override Widget build(BuildContext context, WidgetRef ref) { - final AsyncValue> suggestedShareUsers = - ref.watch(otherUsersProvider); + final AsyncValue> suggestedShareUsers = ref.watch(otherUsersProvider); final sharedUsersList = useState>({}); addNewUsersHandler() { @@ -31,17 +27,9 @@ class AlbumAdditionalSharedUserSelectionPage extends HookConsumerWidget { buildTileIcon(UserDto user) { if (sharedUsersList.value.contains(user)) { - return CircleAvatar( - backgroundColor: context.primaryColor, - child: const Icon( - Icons.check_rounded, - size: 25, - ), - ); + return CircleAvatar(backgroundColor: context.primaryColor, child: const Icon(Icons.check_rounded, size: 25)); } else { - return UserCircleAvatar( - user: user, - ); + return UserCircleAvatar(user: user); } } @@ -54,31 +42,19 @@ class AlbumAdditionalSharedUserSelectionPage extends HookConsumerWidget { padding: const EdgeInsets.symmetric(horizontal: 8.0), child: Chip( backgroundColor: context.primaryColor.withValues(alpha: 0.15), - label: Text( - user.name, - style: const TextStyle( - fontSize: 12, - fontWeight: FontWeight.bold, - ), - ), + label: Text(user.name, style: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold)), ), ), ); } return ListView( children: [ - Wrap( - children: [...usersChip], - ), + Wrap(children: [...usersChip]), Padding( padding: const EdgeInsets.all(16.0), child: Text( 'suggestions'.tr(), - style: const TextStyle( - fontSize: 14, - color: Colors.grey, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(fontSize: 14, color: Colors.grey, fontWeight: FontWeight.bold), ), ), ListView.builder( @@ -88,31 +64,15 @@ class AlbumAdditionalSharedUserSelectionPage extends HookConsumerWidget { return ListTile( leading: buildTileIcon(users[index]), dense: true, - title: Text( - users[index].name, - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), - ), - subtitle: Text( - users[index].email, - style: const TextStyle( - fontSize: 12, - ), - ), + title: Text(users[index].name, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), + subtitle: Text(users[index].email, style: const TextStyle(fontSize: 12)), onTap: () { if (sharedUsersList.value.contains(users[index])) { sharedUsersList.value = sharedUsersList.value - .where( - (selectedUser) => selectedUser.id != users[index].id, - ) + .where((selectedUser) => selectedUser.id != users[index].id) .toSet(); } else { - sharedUsersList.value = { - ...sharedUsersList.value, - users[index], - }; + sharedUsersList.value = {...sharedUsersList.value, users[index]}; } }, ); @@ -125,9 +85,7 @@ class AlbumAdditionalSharedUserSelectionPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( - title: const Text( - 'invite_to_album', - ).tr(), + title: const Text('invite_to_album').tr(), elevation: 0, centerTitle: false, leading: IconButton( @@ -138,21 +96,15 @@ class AlbumAdditionalSharedUserSelectionPage extends HookConsumerWidget { ), actions: [ TextButton( - onPressed: - sharedUsersList.value.isEmpty ? null : addNewUsersHandler, - child: const Text( - "add", - style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold), - ).tr(), + onPressed: sharedUsersList.value.isEmpty ? null : addNewUsersHandler, + child: const Text("add", style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)).tr(), ), ], ), body: suggestedShareUsers.widgetWhen( onData: (users) { for (var sharedUsers in album.sharedUsers) { - users.removeWhere( - (u) => u.id == sharedUsers.id || u.id == album.ownerId, - ); + users.removeWhere((u) => u.id == sharedUsers.id || u.id == album.ownerId); } return buildUserList(users); diff --git a/mobile/lib/pages/album/album_asset_selection.page.dart b/mobile/lib/pages/album/album_asset_selection.page.dart index 7b0ce8cdc4..ccc4c44d43 100644 --- a/mobile/lib/pages/album/album_asset_selection.page.dart +++ b/mobile/lib/pages/album/album_asset_selection.page.dart @@ -13,11 +13,7 @@ import 'package:immich_mobile/widgets/asset_grid/immich_asset_grid.dart'; @RoutePage() class AlbumAssetSelectionPage extends HookConsumerWidget { - const AlbumAssetSelectionPage({ - super.key, - required this.existingAssets, - this.canDeselect = false, - }); + const AlbumAssetSelectionPage({super.key, required this.existingAssets, this.canDeselect = false}); final Set existingAssets; final bool canDeselect; @@ -52,10 +48,7 @@ class AlbumAssetSelectionPage extends HookConsumerWidget { }, ), title: selected.value.isEmpty - ? const Text( - 'add_photos', - style: TextStyle(fontSize: 18), - ).tr() + ? const Text('add_photos', style: TextStyle(fontSize: 18)).tr() : const Text( 'share_assets_selected', style: TextStyle(fontSize: 18), @@ -65,24 +58,17 @@ class AlbumAssetSelectionPage extends HookConsumerWidget { if (selected.value.isNotEmpty || canDeselect) TextButton( onPressed: () { - var payload = - AssetSelectionPageResult(selectedAssets: selected.value); - AutoRouter.of(context) - .popForced(payload); + var payload = AssetSelectionPageResult(selectedAssets: selected.value); + AutoRouter.of(context).popForced(payload); }, child: Text( canDeselect ? "done" : "add", - style: TextStyle( - fontWeight: FontWeight.bold, - color: context.primaryColor, - ), + style: TextStyle(fontWeight: FontWeight.bold, color: context.primaryColor), ).tr(), ), ], ), - body: assetSelectionRenderList.widgetWhen( - onData: (data) => buildBody(data), - ), + body: assetSelectionRenderList.widgetWhen(onData: (data) => buildBody(data)), ); } } diff --git a/mobile/lib/pages/album/album_control_button.dart b/mobile/lib/pages/album/album_control_button.dart index c453ace618..578eb839a0 100644 --- a/mobile/lib/pages/album/album_control_button.dart +++ b/mobile/lib/pages/album/album_control_button.dart @@ -7,11 +7,7 @@ class AlbumControlButton extends ConsumerWidget { final void Function()? onAddPhotosPressed; final void Function()? onAddUsersPressed; - const AlbumControlButton({ - super.key, - this.onAddPhotosPressed, - this.onAddUsersPressed, - }); + const AlbumControlButton({super.key, this.onAddPhotosPressed, this.onAddUsersPressed}); @override Widget build(BuildContext context, WidgetRef ref) { diff --git a/mobile/lib/pages/album/album_date_range.dart b/mobile/lib/pages/album/album_date_range.dart index 591be260f6..dbfd9214f1 100644 --- a/mobile/lib/pages/album/album_date_range.dart +++ b/mobile/lib/pages/album/album_date_range.dart @@ -33,25 +33,20 @@ class AlbumDateRange extends ConsumerWidget { padding: const EdgeInsets.only(left: 16.0), child: Text( _getDateRangeText(startDate, endDate), - style: context.textTheme.labelLarge?.copyWith( - color: context.colorScheme.onSurfaceVariant, - ), + style: context.textTheme.labelLarge?.copyWith(color: context.colorScheme.onSurfaceVariant), ), ); } @pragma('vm:prefer-inline') String _getDateRangeText(DateTime startDate, DateTime endDate) { - if (startDate.day == endDate.day && - startDate.month == endDate.month && - startDate.year == endDate.year) { + if (startDate.day == endDate.day && startDate.month == endDate.month && startDate.year == endDate.year) { return DateFormat.yMMMd().format(startDate); } - final String startDateText = (startDate.year == endDate.year - ? DateFormat.MMMd() - : DateFormat.yMMMd()) - .format(startDate); + final String startDateText = (startDate.year == endDate.year ? DateFormat.MMMd() : DateFormat.yMMMd()).format( + startDate, + ); final String endDateText = DateFormat.yMMMd().format(endDate); return "$startDateText - $endDateText"; } diff --git a/mobile/lib/pages/album/album_description.dart b/mobile/lib/pages/album/album_description.dart index 37c5beb2c2..383367e8b7 100644 --- a/mobile/lib/pages/album/album_description.dart +++ b/mobile/lib/pages/album/album_description.dart @@ -36,10 +36,7 @@ class AlbumDescription extends ConsumerWidget { return Padding( padding: const EdgeInsets.only(left: 16, right: 8), - child: Text( - albumDescription ?? 'add_a_description'.tr(), - style: context.textTheme.bodyLarge, - ), + child: Text(albumDescription ?? 'add_a_description'.tr(), style: context.textTheme.bodyLarge), ); } } diff --git a/mobile/lib/pages/album/album_options.page.dart b/mobile/lib/pages/album/album_options.page.dart index f177686128..e659340f26 100644 --- a/mobile/lib/pages/album/album_options.page.dart +++ b/mobile/lib/pages/album/album_options.page.dart @@ -7,8 +7,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/theme_extensions.dart'; -import 'package:immich_mobile/infrastructure/entities/user.entity.dart' - as entity; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart' as entity; import 'package:immich_mobile/providers/album/album.provider.dart'; import 'package:immich_mobile/providers/album/current_album.provider.dart'; import 'package:immich_mobile/providers/auth.provider.dart'; @@ -28,8 +27,7 @@ class AlbumOptionsPage extends HookConsumerWidget { return const SizedBox(); } - final sharedUsers = - useState(album.sharedUsers.map((u) => u.toDto()).toList()); + final sharedUsers = useState(album.sharedUsers.map((u) => u.toDto()).toList()); final owner = album.owner.value; final userId = ref.watch(authProvider).userId; final activityEnabled = useState(album.activityEnabled); @@ -50,13 +48,10 @@ class AlbumOptionsPage extends HookConsumerWidget { isProcessing.value = true; try { - final isSuccess = - await ref.read(albumProvider.notifier).leaveAlbum(album); + final isSuccess = await ref.read(albumProvider.notifier).leaveAlbum(album); if (isSuccess) { - context.navigateTo( - const TabControllerRoute(children: [AlbumsRoute()]), - ); + context.navigateTo(const TabControllerRoute(children: [AlbumsRoute()])); } else { showErrorMessage(); } @@ -99,8 +94,7 @@ class AlbumOptionsPage extends HookConsumerWidget { actions = [ ListTile( leading: const Icon(Icons.person_remove_rounded), - title: const Text("shared_album_section_people_action_remove_user") - .tr(), + title: const Text("shared_album_section_people_action_remove_user").tr(), onTap: () => removeUserFromAlbum(user), ), ]; @@ -114,10 +108,7 @@ class AlbumOptionsPage extends HookConsumerWidget { return SafeArea( child: Padding( padding: const EdgeInsets.only(top: 24.0), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [...actions], - ), + child: Column(mainAxisSize: MainAxisSize.min, children: [...actions]), ), ); }, @@ -126,23 +117,10 @@ class AlbumOptionsPage extends HookConsumerWidget { buildOwnerInfo() { return ListTile( - leading: owner != null - ? UserCircleAvatar(user: owner.toDto()) - : const SizedBox(), - title: Text( - album.owner.value?.name ?? "", - style: const TextStyle( - fontWeight: FontWeight.w500, - ), - ), - subtitle: Text( - album.owner.value?.email ?? "", - style: TextStyle(color: context.colorScheme.onSurfaceSecondary), - ), - trailing: Text( - "owner", - style: context.textTheme.labelLarge, - ).tr(), + leading: owner != null ? UserCircleAvatar(user: owner.toDto()) : const SizedBox(), + title: Text(album.owner.value?.name ?? "", style: const TextStyle(fontWeight: FontWeight.w500)), + subtitle: Text(album.owner.value?.email ?? "", style: TextStyle(color: context.colorScheme.onSurfaceSecondary)), + trailing: Text("owner", style: context.textTheme.labelLarge).tr(), ); } @@ -154,28 +132,11 @@ class AlbumOptionsPage extends HookConsumerWidget { itemBuilder: (context, index) { final user = sharedUsers.value[index]; return ListTile( - leading: UserCircleAvatar( - user: user, - radius: 22, - ), - title: Text( - user.name, - style: const TextStyle( - fontWeight: FontWeight.w500, - ), - ), - subtitle: Text( - user.email, - style: TextStyle( - color: context.colorScheme.onSurfaceSecondary, - ), - ), - trailing: userId == user.id || isOwner - ? const Icon(Icons.more_horiz_rounded) - : const SizedBox(), - onTap: userId == user.id || isOwner - ? () => handleUserClick(user) - : null, + leading: UserCircleAvatar(user: user, radius: 22), + title: Text(user.name, style: const TextStyle(fontWeight: FontWeight.w500)), + subtitle: Text(user.email, style: TextStyle(color: context.colorScheme.onSurfaceSecondary)), + trailing: userId == user.id || isOwner ? const Icon(Icons.more_horiz_rounded) : const SizedBox(), + onTap: userId == user.id || isOwner ? () => handleUserClick(user) : null, ); }, ); @@ -204,26 +165,19 @@ class AlbumOptionsPage extends HookConsumerWidget { value: activityEnabled.value, onChanged: (bool value) async { activityEnabled.value = value; - if (await ref - .read(albumProvider.notifier) - .setActivitystatus(album, value)) { + if (await ref.read(albumProvider.notifier).setActivitystatus(album, value)) { album.activityEnabled = value; } }, - activeColor: activityEnabled.value - ? context.primaryColor - : context.themeData.disabledColor, + activeColor: activityEnabled.value ? context.primaryColor : context.themeData.disabledColor, dense: true, title: Text( "comments_and_likes", - style: context.textTheme.titleMedium - ?.copyWith(fontWeight: FontWeight.w500), + style: context.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w500), ).tr(), subtitle: Text( "let_others_respond", - style: context.textTheme.labelLarge?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.labelLarge?.copyWith(color: context.colorScheme.onSurfaceSecondary), ).tr(), ), buildSectionTitle("shared_album_section_people_title".tr()), diff --git a/mobile/lib/pages/album/album_shared_user_icons.dart b/mobile/lib/pages/album/album_shared_user_icons.dart index 723bb1e252..fe1823ec61 100644 --- a/mobile/lib/pages/album/album_shared_user_icons.dart +++ b/mobile/lib/pages/album/album_shared_user_icons.dart @@ -41,11 +41,7 @@ class AlbumSharedUserIcons extends HookConsumerWidget { itemBuilder: ((context, index) { return Padding( padding: const EdgeInsets.only(right: 8.0), - child: UserCircleAvatar( - user: sharedUsers.value[index], - radius: 18, - size: 36, - ), + child: UserCircleAvatar(user: sharedUsers.value[index], radius: 18, size: 36), ); }), itemCount: sharedUsers.value.length, diff --git a/mobile/lib/pages/album/album_shared_user_selection.page.dart b/mobile/lib/pages/album/album_shared_user_selection.page.dart index d5d963b206..562f02a2ab 100644 --- a/mobile/lib/pages/album/album_shared_user_selection.page.dart +++ b/mobile/lib/pages/album/album_shared_user_selection.page.dart @@ -25,10 +25,7 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget { final suggestedShareUsers = ref.watch(otherUsersProvider); createSharedAlbum() async { - var newAlbum = await ref.watch(albumProvider.notifier).createAlbum( - ref.watch(albumTitleProvider), - assets, - ); + var newAlbum = await ref.watch(albumProvider.notifier).createAlbum(ref.watch(albumTitleProvider), assets); if (newAlbum != null) { ref.watch(albumTitleProvider.notifier).clearAlbumTitle(); @@ -40,9 +37,7 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget { child: SnackBar( content: Text( 'select_user_for_sharing_page_err_album', - style: context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), ).tr(), ), ); @@ -50,17 +45,9 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget { buildTileIcon(UserDto user) { if (sharedUsersList.value.contains(user)) { - return CircleAvatar( - backgroundColor: context.primaryColor, - child: const Icon( - Icons.check_rounded, - size: 25, - ), - ); + return CircleAvatar(backgroundColor: context.primaryColor, child: const Icon(Icons.check_rounded, size: 25)); } else { - return UserCircleAvatar( - user: user, - ); + return UserCircleAvatar(user: user); } } @@ -75,11 +62,7 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget { backgroundColor: context.primaryColor.withValues(alpha: 0.15), label: Text( user.email, - style: const TextStyle( - fontSize: 12, - color: Colors.black87, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(fontSize: 12, color: Colors.black87, fontWeight: FontWeight.bold), ), ), ), @@ -87,18 +70,12 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget { } return ListView( children: [ - Wrap( - children: [...usersChip], - ), + Wrap(children: [...usersChip]), Padding( padding: const EdgeInsets.all(16.0), child: const Text( 'suggestions', - style: TextStyle( - fontSize: 14, - color: Colors.grey, - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 14, color: Colors.grey, fontWeight: FontWeight.bold), ).tr(), ), ListView.builder( @@ -107,25 +84,14 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget { itemBuilder: ((context, index) { return ListTile( leading: buildTileIcon(users[index]), - title: Text( - users[index].email, - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), - ), + title: Text(users[index].email, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), onTap: () { if (sharedUsersList.value.contains(users[index])) { sharedUsersList.value = sharedUsersList.value - .where( - (selectedUser) => selectedUser.id != users[index].id, - ) + .where((selectedUser) => selectedUser.id != users[index].id) .toSet(); } else { - sharedUsersList.value = { - ...sharedUsersList.value, - users[index], - }; + sharedUsersList.value = {...sharedUsersList.value, users[index]}; } }, ); @@ -138,10 +104,7 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( - title: Text( - 'invite_to_album', - style: TextStyle(color: context.primaryColor), - ).tr(), + title: Text('invite_to_album', style: TextStyle(color: context.primaryColor)).tr(), elevation: 0, centerTitle: false, leading: IconButton( @@ -152,9 +115,7 @@ class AlbumSharedUserSelectionPage extends HookConsumerWidget { ), actions: [ TextButton( - style: TextButton.styleFrom( - foregroundColor: context.primaryColor, - ), + style: TextButton.styleFrom(foregroundColor: context.primaryColor), onPressed: sharedUsersList.value.isEmpty ? null : createSharedAlbum, child: const Text( "create_album", diff --git a/mobile/lib/pages/album/album_title.dart b/mobile/lib/pages/album/album_title.dart index ccea200f3a..6c7fc3faaa 100644 --- a/mobile/lib/pages/album/album_title.dart +++ b/mobile/lib/pages/album/album_title.dart @@ -19,32 +19,20 @@ class AlbumTitle extends ConsumerWidget { return const (false, false, ''); } - return ( - album.ownerId == userId, - album.isRemote, - album.name, - ); + return (album.ownerId == userId, album.isRemote, album.name); }), ); if (isOwner && isRemote) { return Padding( padding: const EdgeInsets.only(left: 8, right: 8), - child: AlbumViewerEditableTitle( - albumName: albumName, - titleFocusNode: titleFocusNode, - ), + child: AlbumViewerEditableTitle(albumName: albumName, titleFocusNode: titleFocusNode), ); } return Padding( padding: const EdgeInsets.only(left: 16, right: 8), - child: Text( - albumName, - style: context.textTheme.headlineLarge?.copyWith( - fontWeight: FontWeight.w700, - ), - ), + child: Text(albumName, style: context.textTheme.headlineLarge?.copyWith(fontWeight: FontWeight.w700)), ); } } diff --git a/mobile/lib/pages/album/album_viewer.dart b/mobile/lib/pages/album/album_viewer.dart index 2edf6082ac..97853fb96a 100644 --- a/mobile/lib/pages/album/album_viewer.dart +++ b/mobile/lib/pages/album/album_viewer.dart @@ -48,8 +48,7 @@ class AlbumViewer extends HookConsumerWidget { ); Future onRemoveFromAlbumPressed(Iterable assets) async { - final bool isSuccess = - await ref.read(albumProvider.notifier).removeAsset(album, assets); + final bool isSuccess = await ref.read(albumProvider.notifier).removeAsset(album, assets); if (!isSuccess) { ImmichToast.show( @@ -65,21 +64,15 @@ class AlbumViewer extends HookConsumerWidget { /// Find out if the assets in album exist on the device /// If they exist, add to selected asset state to show they are already selected. void onAddPhotosPressed() async { - AssetSelectionPageResult? returnPayload = - await context.pushRoute( - AlbumAssetSelectionRoute( - existingAssets: album.assets, - canDeselect: false, - ), + AssetSelectionPageResult? returnPayload = await context.pushRoute( + AlbumAssetSelectionRoute(existingAssets: album.assets, canDeselect: false), ); if (returnPayload != null && returnPayload.selectedAssets.isNotEmpty) { // Check if there is new assets add isProcessing.value = true; - await ref - .watch(albumProvider.notifier) - .addAssets(album, returnPayload.selectedAssets); + await ref.watch(albumProvider.notifier).addAssets(album, returnPayload.selectedAssets); isProcessing.value = false; } @@ -102,9 +95,7 @@ class AlbumViewer extends HookConsumerWidget { onActivitiesPressed() { if (album.remoteId != null) { ref.read(currentAssetProvider.notifier).set(null); - context.pushRoute( - const ActivitiesRoute(), - ); + context.pushRoute(const ActivitiesRoute()); } } @@ -133,14 +124,8 @@ class AlbumViewer extends HookConsumerWidget { children: [ const SizedBox(height: 32), const AlbumDateRange(), - AlbumTitle( - key: const ValueKey("albumTitle"), - titleFocusNode: titleFocusNode, - ), - AlbumDescription( - key: const ValueKey("albumDescription"), - descriptionFocusNode: descriptionFocusNode, - ), + AlbumTitle(key: const ValueKey("albumTitle"), titleFocusNode: titleFocusNode), + AlbumDescription(key: const ValueKey("albumDescription"), descriptionFocusNode: descriptionFocusNode), const AlbumSharedUserIcons(), if (album.isRemote) Padding( diff --git a/mobile/lib/pages/album/album_viewer.page.dart b/mobile/lib/pages/album/album_viewer.page.dart index 146a93a0a6..c99dacd9b7 100644 --- a/mobile/lib/pages/album/album_viewer.page.dart +++ b/mobile/lib/pages/album/album_viewer.page.dart @@ -21,9 +21,7 @@ class AlbumViewerPage extends HookConsumerWidget { ref.listen(assetSelectionTimelineProvider, (_, __) {}); ref.listen(albumWatcher(albumId), (_, albumFuture) { - albumFuture.whenData( - (value) => ref.read(currentAlbumProvider.notifier).set(value), - ); + albumFuture.whenData((value) => ref.read(currentAlbumProvider.notifier).set(value)); }); return const Scaffold(body: AlbumViewer()); diff --git a/mobile/lib/pages/albums/albums.page.dart b/mobile/lib/pages/albums/albums.page.dart index 3fc628afd3..5f155c2f0d 100644 --- a/mobile/lib/pages/albums/albums.page.dart +++ b/mobile/lib/pages/albums/albums.page.dart @@ -26,8 +26,7 @@ class AlbumsPage extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final albums = - ref.watch(albumProvider).where((album) => album.isRemote).toList(); + final albums = ref.watch(albumProvider).where((album) => album.isRemote).toList(); final albumSortOption = ref.watch(albumSortByOptionsProvider); final albumSortIsReverse = ref.watch(albumSortOrderProvider); final sorted = albumSortOption.sortFn(albums, albumSortIsReverse); @@ -53,21 +52,18 @@ class AlbumsPage extends HookConsumerWidget { filterMode.value = mode; } - useEffect( - () { - searchController.addListener(() { + useEffect(() { + searchController.addListener(() { + onSearch(searchController.text, filterMode.value); + }); + + return () { + searchController.removeListener(() { onSearch(searchController.text, filterMode.value); }); - - return () { - searchController.removeListener(() { - onSearch(searchController.text, filterMode.value); - }); - debounceTimer.value?.cancel(); - }; - }, - [], - ); + debounceTimer.value?.cancel(); + }; + }, []); clearSearch() { filterMode.value = QuickFilterMode.all; @@ -80,13 +76,8 @@ class AlbumsPage extends HookConsumerWidget { showUploadButton: false, actions: [ IconButton( - icon: const Icon( - Icons.add_rounded, - size: 28, - ), - onPressed: () => context.pushRoute( - CreateAlbumRoute(), - ), + icon: const Icon(Icons.add_rounded, size: 28), + onPressed: () => context.pushRoute(CreateAlbumRoute()), ), ], ), @@ -101,13 +92,8 @@ class AlbumsPage extends HookConsumerWidget { children: [ Container( decoration: BoxDecoration( - border: Border.all( - color: context.colorScheme.onSurface.withAlpha(0), - width: 0, - ), - borderRadius: const BorderRadius.all( - Radius.circular(24), - ), + border: Border.all(color: context.colorScheme.onSurface.withAlpha(0), width: 0), + borderRadius: const BorderRadius.all(Radius.circular(24)), gradient: LinearGradient( colors: [ context.colorScheme.primary.withValues(alpha: 0.075), @@ -125,14 +111,10 @@ class AlbumsPage extends HookConsumerWidget { hintText: 'search_albums'.tr(), prefixIcon: const Icon(Icons.search_rounded), suffixIcon: searchController.text.isNotEmpty - ? IconButton( - icon: const Icon(Icons.clear_rounded), - onPressed: clearSearch, - ) + ? IconButton(icon: const Icon(Icons.clear_rounded), onPressed: clearSearch) : null, controller: searchController, - onChanged: (_) => - onSearch(searchController.text, filterMode.value), + onChanged: (_) => onSearch(searchController.text, filterMode.value), focusNode: searchFocusNode, onTapOutside: (_) => searchFocusNode.unfocus(), ), @@ -155,10 +137,7 @@ class AlbumsPage extends HookConsumerWidget { isSelected: filterMode.value == QuickFilterMode.sharedWithMe, onTap: () { changeFilter(QuickFilterMode.sharedWithMe); - onSearch( - searchController.text, - QuickFilterMode.sharedWithMe, - ); + onSearch(searchController.text, QuickFilterMode.sharedWithMe); }, ), QuickFilterButton( @@ -166,10 +145,7 @@ class AlbumsPage extends HookConsumerWidget { isSelected: filterMode.value == QuickFilterMode.myAlbums, onTap: () { changeFilter(QuickFilterMode.myAlbums); - onSearch( - searchController.text, - QuickFilterMode.myAlbums, - ); + onSearch(searchController.text, QuickFilterMode.myAlbums); }, ), ], @@ -179,12 +155,7 @@ class AlbumsPage extends HookConsumerWidget { children: [ const SortButton(), IconButton( - icon: Icon( - isGrid.value - ? Icons.view_list_outlined - : Icons.grid_view_outlined, - size: 24, - ), + icon: Icon(isGrid.value ? Icons.view_list_outlined : Icons.grid_view_outlined, size: 24), onPressed: toggleViewMode, ), ], @@ -196,8 +167,7 @@ class AlbumsPage extends HookConsumerWidget { ? GridView.builder( shrinkWrap: true, physics: const ClampingScrollPhysics(), - gridDelegate: - const SliverGridDelegateWithMaxCrossAxisExtent( + gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 250, mainAxisSpacing: 12, crossAxisSpacing: 12, @@ -206,9 +176,7 @@ class AlbumsPage extends HookConsumerWidget { itemBuilder: (context, index) { return AlbumThumbnailCard( album: sorted[index], - onTap: () => context.pushRoute( - AlbumViewerRoute(albumId: sorted[index].id), - ), + onTap: () => context.pushRoute(AlbumViewerRoute(albumId: sorted[index].id)), showOwner: true, ); }, @@ -226,46 +194,22 @@ class AlbumsPage extends HookConsumerWidget { sorted[index].name, maxLines: 2, overflow: TextOverflow.ellipsis, - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w600, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600), ), subtitle: sorted[index].ownerId != null ? Text( - '${'items_count'.t( - context: context, - args: { - 'count': sorted[index].assetCount, - }, - )} • ${sorted[index].ownerId != userId ? 'shared_by_user'.t( - context: context, - args: { - 'user': sorted[index].ownerName!, - }, - ) : 'owned'.t(context: context)}', + '${'items_count'.t(context: context, args: {'count': sorted[index].assetCount})} • ${sorted[index].ownerId != userId ? 'shared_by_user'.t(context: context, args: {'user': sorted[index].ownerName!}) : 'owned'.t(context: context)}', overflow: TextOverflow.ellipsis, - style: - context.textTheme.bodyMedium?.copyWith( - color: context - .colorScheme.onSurfaceSecondary, + style: context.textTheme.bodyMedium?.copyWith( + color: context.colorScheme.onSurfaceSecondary, ), ) : null, - onTap: () => context.pushRoute( - AlbumViewerRoute(albumId: sorted[index].id), - ), - leadingPadding: const EdgeInsets.only( - right: 16, - ), + onTap: () => context.pushRoute(AlbumViewerRoute(albumId: sorted[index].id)), + leadingPadding: const EdgeInsets.only(right: 16), leading: ClipRRect( - borderRadius: const BorderRadius.all( - Radius.circular(15), - ), - child: ImmichThumbnail( - asset: sorted[index].thumbnail.value, - width: 80, - height: 80, - ), + borderRadius: const BorderRadius.all(Radius.circular(15)), + child: ImmichThumbnail(asset: sorted[index].thumbnail.value, width: 80, height: 80), ), // minVerticalPadding: 1, ), @@ -282,12 +226,7 @@ class AlbumsPage extends HookConsumerWidget { } class QuickFilterButton extends StatelessWidget { - const QuickFilterButton({ - super.key, - required this.isSelected, - required this.onTap, - required this.label, - }); + const QuickFilterButton({super.key, required this.isSelected, required this.onTap, required this.label}); final bool isSelected; final VoidCallback onTap; @@ -298,27 +237,18 @@ class QuickFilterButton extends StatelessWidget { return TextButton( onPressed: onTap, style: ButtonStyle( - backgroundColor: WidgetStateProperty.all( - isSelected ? context.colorScheme.primary : Colors.transparent, - ), + backgroundColor: WidgetStateProperty.all(isSelected ? context.colorScheme.primary : Colors.transparent), shape: WidgetStateProperty.all( RoundedRectangleBorder( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), - side: BorderSide( - color: context.colorScheme.onSurface.withAlpha(25), - width: 1, - ), + borderRadius: const BorderRadius.all(Radius.circular(20)), + side: BorderSide(color: context.colorScheme.onSurface.withAlpha(25), width: 1), ), ), ), child: Text( label, style: TextStyle( - color: isSelected - ? context.colorScheme.onPrimary - : context.colorScheme.onSurface, + color: isSelected ? context.colorScheme.onPrimary : context.colorScheme.onSurface, fontSize: 14, ), ), @@ -338,15 +268,9 @@ class SortButton extends ConsumerWidget { style: MenuStyle( elevation: const WidgetStatePropertyAll(1), shape: WidgetStateProperty.all( - const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(24), - ), - ), - ), - padding: const WidgetStatePropertyAll( - EdgeInsets.all(4), + const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(24))), ), + padding: const WidgetStatePropertyAll(EdgeInsets.all(4)), ), consumeOutsideTap: true, menuChildren: AlbumSortMode.values @@ -354,47 +278,35 @@ class SortButton extends ConsumerWidget { (mode) => MenuItemButton( leadingIcon: albumSortOption == mode ? albumSortIsReverse - ? Icon( - Icons.keyboard_arrow_down, - color: albumSortOption == mode - ? context.colorScheme.onPrimary - : context.colorScheme.onSurface, - ) - : Icon( - Icons.keyboard_arrow_up_rounded, - color: albumSortOption == mode - ? context.colorScheme.onPrimary - : context.colorScheme.onSurface, - ) + ? Icon( + Icons.keyboard_arrow_down, + color: albumSortOption == mode + ? context.colorScheme.onPrimary + : context.colorScheme.onSurface, + ) + : Icon( + Icons.keyboard_arrow_up_rounded, + color: albumSortOption == mode + ? context.colorScheme.onPrimary + : context.colorScheme.onSurface, + ) : const Icon(Icons.abc, color: Colors.transparent), onPressed: () { final selected = albumSortOption == mode; // Switch direction if (selected) { - ref - .read(albumSortOrderProvider.notifier) - .changeSortDirection(!albumSortIsReverse); + ref.read(albumSortOrderProvider.notifier).changeSortDirection(!albumSortIsReverse); } else { - ref - .read(albumSortByOptionsProvider.notifier) - .changeSortMode(mode); + ref.read(albumSortByOptionsProvider.notifier).changeSortMode(mode); } }, style: ButtonStyle( - padding: WidgetStateProperty.all( - const EdgeInsets.fromLTRB(16, 16, 32, 16), - ), + padding: WidgetStateProperty.all(const EdgeInsets.fromLTRB(16, 16, 32, 16)), backgroundColor: WidgetStateProperty.all( - albumSortOption == mode - ? context.colorScheme.primary - : Colors.transparent, + albumSortOption == mode ? context.colorScheme.primary : Colors.transparent, ), shape: WidgetStateProperty.all( - const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(24), - ), - ), + const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(24))), ), ), child: Text( diff --git a/mobile/lib/pages/backup/album_preview.page.dart b/mobile/lib/pages/backup/album_preview.page.dart index b9fed41305..def31afcd4 100644 --- a/mobile/lib/pages/backup/album_preview.page.dart +++ b/mobile/lib/pages/backup/album_preview.page.dart @@ -19,28 +19,20 @@ class AlbumPreviewPage extends HookConsumerWidget { final assets = useState>([]); getAssetsInAlbum() async { - assets.value = await ref - .read(albumMediaRepositoryProvider) - .getAssets(album.localId!); + assets.value = await ref.read(albumMediaRepositoryProvider).getAssets(album.localId!); } - useEffect( - () { - getAssetsInAlbum(); - return null; - }, - [], - ); + useEffect(() { + getAssetsInAlbum(); + return null; + }, []); return Scaffold( appBar: AppBar( elevation: 0, title: Column( children: [ - Text( - album.name, - style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold), - ), + Text(album.name, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), Padding( padding: const EdgeInsets.only(top: 4.0), child: Text( @@ -54,10 +46,7 @@ class AlbumPreviewPage extends HookConsumerWidget { ), ], ), - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_new_rounded), - ), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_new_rounded)), ), body: GridView.builder( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( @@ -67,11 +56,7 @@ class AlbumPreviewPage extends HookConsumerWidget { ), itemCount: assets.value.length, itemBuilder: (context, index) { - return ImmichThumbnail( - asset: assets.value[index], - width: 100, - height: 100, - ); + return ImmichThumbnail(asset: assets.value[index], width: 100, height: 100); }, ), ); diff --git a/mobile/lib/pages/backup/backup_album_selection.page.dart b/mobile/lib/pages/backup/backup_album_selection.page.dart index e51d259969..d222211577 100644 --- a/mobile/lib/pages/backup/backup_album_selection.page.dart +++ b/mobile/lib/pages/backup/backup_album_selection.page.dart @@ -19,50 +19,33 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final selectedBackupAlbums = ref.watch(backupProvider).selectedBackupAlbums; final excludedBackupAlbums = ref.watch(backupProvider).excludedBackupAlbums; - final enableSyncUploadAlbum = - useAppSettingsState(AppSettingsEnum.syncAlbums); + final enableSyncUploadAlbum = useAppSettingsState(AppSettingsEnum.syncAlbums); final isDarkTheme = context.isDarkTheme; final albums = ref.watch(backupProvider).availableAlbums; - useEffect( - () { - ref.watch(backupProvider.notifier).getBackupInfo(); - return null; - }, - [], - ); + useEffect(() { + ref.watch(backupProvider.notifier).getBackupInfo(); + return null; + }, []); buildAlbumSelectionList() { if (albums.isEmpty) { - return const SliverToBoxAdapter( - child: Center( - child: CircularProgressIndicator(), - ), - ); + return const SliverToBoxAdapter(child: Center(child: CircularProgressIndicator())); } return SliverPadding( padding: const EdgeInsets.symmetric(vertical: 12.0), sliver: SliverList( - delegate: SliverChildBuilderDelegate( - ((context, index) { - return AlbumInfoListTile( - album: albums[index], - ); - }), - childCount: albums.length, - ), + delegate: SliverChildBuilderDelegate(((context, index) { + return AlbumInfoListTile(album: albums[index]); + }), childCount: albums.length), ), ); } buildAlbumSelectionGrid() { if (albums.isEmpty) { - return const SliverToBoxAdapter( - child: Center( - child: CircularProgressIndicator(), - ), - ); + return const SliverToBoxAdapter(child: Center(child: CircularProgressIndicator())); } return SliverPadding( @@ -75,9 +58,7 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { ), itemCount: albums.length, itemBuilder: ((context, index) { - return AlbumInfoCard( - album: albums[index], - ); + return AlbumInfoCard(album: albums[index]); }), ), ); @@ -85,8 +66,7 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { buildSelectedAlbumNameChip() { return selectedBackupAlbums.map((album) { - void removeSelection() => - ref.read(backupProvider.notifier).removeAlbumForBackup(album); + void removeSelection() => ref.read(backupProvider.notifier).removeAlbumForBackup(album); return Padding( padding: const EdgeInsets.only(right: 8.0), @@ -103,10 +83,7 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { ), backgroundColor: context.primaryColor, deleteIconColor: isDarkTheme ? Colors.black : Colors.white, - deleteIcon: const Icon( - Icons.cancel_rounded, - size: 15, - ), + deleteIcon: const Icon(Icons.cancel_rounded, size: 15), onDeleted: removeSelection, ), ), @@ -117,9 +94,7 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { buildExcludedAlbumNameChip() { return excludedBackupAlbums.map((album) { void removeSelection() { - ref - .watch(backupProvider.notifier) - .removeExcludedAlbumForBackup(album); + ref.watch(backupProvider.notifier).removeExcludedAlbumForBackup(album); } return GestureDetector( @@ -129,18 +104,11 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { child: Chip( label: Text( album.name, - style: TextStyle( - fontSize: 12, - color: context.scaffoldBackgroundColor, - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 12, color: context.scaffoldBackgroundColor, fontWeight: FontWeight.bold), ), backgroundColor: Colors.red[300], deleteIconColor: context.scaffoldBackgroundColor, - deleteIcon: const Icon( - Icons.cancel_rounded, - size: 15, - ), + deleteIcon: const Icon(Icons.cancel_rounded, size: 15), onDeleted: removeSelection, ), ), @@ -159,13 +127,8 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), - title: const Text( - "backup_album_selection_page_select_albums", - ).tr(), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_rounded)), + title: const Text("backup_album_selection_page_select_albums").tr(), elevation: 0, ), body: CustomScrollView( @@ -176,25 +139,14 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( - padding: const EdgeInsets.symmetric( - vertical: 8.0, - horizontal: 16.0, - ), - child: Text( - "backup_album_selection_page_selection_info", - style: context.textTheme.titleSmall, - ).tr(), + padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0), + child: Text("backup_album_selection_page_selection_info", style: context.textTheme.titleSmall).tr(), ), - // Selected Album Chips + // Selected Album Chips Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: Wrap( - children: [ - ...buildSelectedAlbumNameChip(), - ...buildExcludedAlbumNameChip(), - ], - ), + child: Wrap(children: [...buildSelectedAlbumNameChip(), ...buildExcludedAlbumNameChip()]), ), SettingsSwitchListTile( @@ -202,25 +154,15 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { title: "sync_albums".tr(), subtitle: "sync_upload_album_setting_subtitle".tr(), contentPadding: const EdgeInsets.symmetric(horizontal: 16), - titleStyle: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.bold, - ), - subtitleStyle: context.textTheme.labelLarge?.copyWith( - color: context.colorScheme.primary, - ), + titleStyle: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.bold), + subtitleStyle: context.textTheme.labelLarge?.copyWith(color: context.colorScheme.primary), onChanged: handleSyncAlbumToggle, ), ListTile( title: Text( "backup_album_selection_page_albums_device".tr( - namedArgs: { - 'count': ref - .watch(backupProvider) - .availableAlbums - .length - .toString(), - }, + namedArgs: {'count': ref.watch(backupProvider).availableAlbums.length.toString()}, ), style: context.textTheme.titleSmall, ), @@ -228,46 +170,30 @@ class BackupAlbumSelectionPage extends HookConsumerWidget { padding: const EdgeInsets.symmetric(vertical: 8.0), child: Text( "backup_album_selection_page_albums_tap", - style: context.textTheme.labelLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor), ).tr(), ), trailing: IconButton( splashRadius: 16, - icon: Icon( - Icons.info, - size: 20, - color: context.primaryColor, - ), + icon: Icon(Icons.info, size: 20, color: context.primaryColor), onPressed: () { // show the dialog showDialog( context: context, builder: (BuildContext context) { return AlertDialog( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(10), - ), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))), elevation: 5, title: Text( 'backup_album_selection_page_selection_info', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: context.primaryColor, - ), + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: context.primaryColor), ).tr(), content: SingleChildScrollView( child: ListBody( children: [ const Text( 'backup_album_selection_page_assets_scatter', - style: TextStyle( - fontSize: 14, - ), + style: TextStyle(fontSize: 14), ).tr(), ], ), diff --git a/mobile/lib/pages/backup/backup_controller.page.dart b/mobile/lib/pages/backup/backup_controller.page.dart index ca094437ac..093ff952ae 100644 --- a/mobile/lib/pages/backup/backup_controller.page.dart +++ b/mobile/lib/pages/backup/backup_controller.page.dart @@ -30,59 +30,45 @@ class BackupControllerPage extends HookConsumerWidget { final hasAnyAlbum = backupState.selectedBackupAlbums.isNotEmpty; final didGetBackupInfo = useState(false); - bool hasExclusiveAccess = - backupState.backupProgress != BackUpProgressEnum.inBackground; - bool shouldBackup = backupState.allUniqueAssets.length - - backupState.selectedAlbumsBackupAssetsIds.length == - 0 || + bool hasExclusiveAccess = backupState.backupProgress != BackUpProgressEnum.inBackground; + bool shouldBackup = + backupState.allUniqueAssets.length - backupState.selectedAlbumsBackupAssetsIds.length == 0 || !hasExclusiveAccess ? false : true; - useEffect( - () { - // Update the background settings information just to make sure we - // have the latest, since the platform channel will not update - // automatically - if (Platform.isIOS) { - ref.watch(iOSBackgroundSettingsProvider.notifier).refresh(); - } + useEffect(() { + // Update the background settings information just to make sure we + // have the latest, since the platform channel will not update + // automatically + if (Platform.isIOS) { + ref.watch(iOSBackgroundSettingsProvider.notifier).refresh(); + } - ref - .watch(websocketProvider.notifier) - .stopListenToEvent('on_upload_success'); + ref.watch(websocketProvider.notifier).stopListenToEvent('on_upload_success'); - return () { - WakelockPlus.disable(); - }; - }, - [], - ); + return () { + WakelockPlus.disable(); + }; + }, []); - useEffect( - () { - if (backupState.backupProgress == BackUpProgressEnum.idle && - !didGetBackupInfo.value) { - ref.watch(backupProvider.notifier).getBackupInfo(); - didGetBackupInfo.value = true; - } - return null; - }, - [backupState.backupProgress], - ); + useEffect(() { + if (backupState.backupProgress == BackUpProgressEnum.idle && !didGetBackupInfo.value) { + ref.watch(backupProvider.notifier).getBackupInfo(); + didGetBackupInfo.value = true; + } + return null; + }, [backupState.backupProgress]); - useEffect( - () { - if (backupState.backupProgress == BackUpProgressEnum.inProgress) { - WakelockPlus.enable(); - } else { - WakelockPlus.disable(); - } + useEffect(() { + if (backupState.backupProgress == BackUpProgressEnum.inProgress) { + WakelockPlus.enable(); + } else { + WakelockPlus.disable(); + } - return null; - }, - [backupState.backupProgress], - ); + return null; + }, [backupState.backupProgress]); Widget buildSelectedAlbumName() { var text = "backup_controller_page_backup_selected".tr(); @@ -101,9 +87,7 @@ class BackupControllerPage extends HookConsumerWidget { padding: const EdgeInsets.only(top: 8.0), child: Text( text.trim().substring(0, text.length - 2), - style: context.textTheme.labelLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor), ), ); } else { @@ -111,9 +95,7 @@ class BackupControllerPage extends HookConsumerWidget { padding: const EdgeInsets.only(top: 8.0), child: Text( "backup_controller_page_none_selected".tr(), - style: context.textTheme.labelLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor), ), ); } @@ -132,9 +114,7 @@ class BackupControllerPage extends HookConsumerWidget { padding: const EdgeInsets.only(top: 8.0), child: Text( text.trim().substring(0, text.length - 2), - style: context.textTheme.labelLarge?.copyWith( - color: Colors.red[300], - ), + style: context.textTheme.labelLarge?.copyWith(color: Colors.red[300]), ), ); } else { @@ -147,22 +127,14 @@ class BackupControllerPage extends HookConsumerWidget { padding: const EdgeInsets.only(top: 8.0), child: Card( shape: RoundedRectangleBorder( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), - side: BorderSide( - color: context.colorScheme.outlineVariant, - width: 1, - ), + borderRadius: const BorderRadius.all(Radius.circular(20)), + side: BorderSide(color: context.colorScheme.outlineVariant, width: 1), ), elevation: 0, borderOnForeground: false, child: ListTile( minVerticalPadding: 18, - title: Text( - "backup_controller_page_albums", - style: context.textTheme.titleMedium, - ).tr(), + title: Text("backup_controller_page_albums", style: context.textTheme.titleMedium).tr(), subtitle: Padding( padding: const EdgeInsets.only(top: 8.0), child: Column( @@ -170,9 +142,7 @@ class BackupControllerPage extends HookConsumerWidget { children: [ Text( "backup_controller_page_to_backup", - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ).tr(), buildSelectedAlbumName(), buildExcludedAlbumName(), @@ -183,18 +153,11 @@ class BackupControllerPage extends HookConsumerWidget { onPressed: () async { await context.pushRoute(const BackupAlbumSelectionRoute()); // waited until returning from selection - await ref - .read(backupProvider.notifier) - .backupAlbumSelectionDone(); + await ref.read(backupProvider.notifier).backupAlbumSelectionDone(); // waited until backup albums are stored in DB ref.read(albumProvider.notifier).refreshDeviceAlbums(); }, - child: const Text( - "select", - style: TextStyle( - fontWeight: FontWeight.bold, - ), - ).tr(), + child: const Text("select", style: TextStyle(fontWeight: FontWeight.bold)).tr(), ), ), ), @@ -203,21 +166,18 @@ class BackupControllerPage extends HookConsumerWidget { void startBackup() { ref.watch(errorBackupListProvider.notifier).empty(); - if (ref.watch(backupProvider).backupProgress != - BackUpProgressEnum.inBackground) { + if (ref.watch(backupProvider).backupProgress != BackUpProgressEnum.inBackground) { ref.watch(backupProvider.notifier).startBackupProcess(); } } Widget buildBackupButton() { return Padding( - padding: const EdgeInsets.only( - top: 24, - ), + padding: const EdgeInsets.only(top: 24), child: Container( - child: backupState.backupProgress == BackUpProgressEnum.inProgress || - backupState.backupProgress == - BackUpProgressEnum.manualInProgress + child: + backupState.backupProgress == BackUpProgressEnum.inProgress || + backupState.backupProgress == BackUpProgressEnum.manualInProgress ? ElevatedButton( style: ElevatedButton.styleFrom( foregroundColor: Colors.grey[50], @@ -225,29 +185,19 @@ class BackupControllerPage extends HookConsumerWidget { // padding: const EdgeInsets.all(14), ), onPressed: () { - if (backupState.backupProgress == - BackUpProgressEnum.manualInProgress) { + if (backupState.backupProgress == BackUpProgressEnum.manualInProgress) { ref.read(manualUploadProvider.notifier).cancelBackup(); } else { ref.read(backupProvider.notifier).cancelBackup(); } }, - child: const Text( - "cancel", - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), - ).tr(), + child: const Text("cancel", style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)).tr(), ) : ElevatedButton( onPressed: shouldBackup ? startBackup : null, child: const Text( "backup_controller_page_start_backup", - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ).tr(), ), ), @@ -257,36 +207,28 @@ class BackupControllerPage extends HookConsumerWidget { buildBackgroundBackupInfo() { return const ListTile( leading: Icon(Icons.info_outline_rounded), - title: Text( - "Background backup is currently running, cannot start manual backup", - ), + title: Text("Background backup is currently running, cannot start manual backup"), ); } buildLoadingIndicator() { return const Padding( padding: EdgeInsets.only(top: 42.0), - child: Center( - child: CircularProgressIndicator(), - ), + child: Center(child: CircularProgressIndicator()), ); } return Scaffold( appBar: AppBar( elevation: 0, - title: const Text( - "backup_controller_page_backup", - ).tr(), + title: const Text("backup_controller_page_backup").tr(), leading: IconButton( onPressed: () { ref.watch(websocketProvider.notifier).listenUploadEvent(); context.maybePop(true); }, splashRadius: 24, - icon: const Icon( - Icons.arrow_back_ios_rounded, - ), + icon: const Icon(Icons.arrow_back_ios_rounded), ), actions: [ Padding( @@ -294,9 +236,7 @@ class BackupControllerPage extends HookConsumerWidget { child: IconButton( onPressed: () => context.pushRoute(const BackupOptionsRoute()), splashRadius: 24, - icon: const Icon( - Icons.settings_outlined, - ), + icon: const Icon(Icons.settings_outlined), ), ), ], @@ -336,10 +276,7 @@ class BackupControllerPage extends HookConsumerWidget { if (!hasExclusiveAccess) buildBackgroundBackupInfo(), buildBackupButton(), ] - : [ - buildFolderSelectionTile(), - if (!didGetBackupInfo.value) buildLoadingIndicator(), - ], + : [buildFolderSelectionTile(), if (!didGetBackupInfo.value) buildLoadingIndicator()], ), ), ], diff --git a/mobile/lib/pages/backup/backup_options.page.dart b/mobile/lib/pages/backup/backup_options.page.dart index 29822cab15..846a32a742 100644 --- a/mobile/lib/pages/backup/backup_options.page.dart +++ b/mobile/lib/pages/backup/backup_options.page.dart @@ -15,9 +15,7 @@ class BackupOptionsPage extends StatelessWidget { leading: IconButton( onPressed: () => context.maybePop(true), splashRadius: 24, - icon: const Icon( - Icons.arrow_back_ios_rounded, - ), + icon: const Icon(Icons.arrow_back_ios_rounded), ), ), body: const BackupSettings(), diff --git a/mobile/lib/pages/backup/drift_backup.page.dart b/mobile/lib/pages/backup/drift_backup.page.dart new file mode 100644 index 0000000000..70e8190014 --- /dev/null +++ b/mobile/lib/pages/backup/drift_backup.page.dart @@ -0,0 +1,252 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; +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/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/theme_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/backup/backup_toggle_button.widget.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/user.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/widgets/backup/backup_info_card.dart'; + +@RoutePage() +class DriftBackupPage extends ConsumerStatefulWidget { + const DriftBackupPage({super.key}); + + @override + ConsumerState createState() => _DriftBackupPageState(); +} + +class _DriftBackupPageState extends ConsumerState { + @override + void initState() { + super.initState(); + final currentUser = ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + + ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id); + } + + Future startBackup() async { + final currentUser = ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + + await ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id); + await ref.read(driftBackupProvider.notifier).startBackup(currentUser.id); + } + + Future stopBackup() async { + await ref.read(driftBackupProvider.notifier).cancel(); + } + + @override + Widget build(BuildContext context) { + final selectedAlbum = ref + .watch(backupAlbumProvider) + .where((album) => album.backupSelection == BackupSelection.selected) + .toList(); + + return Scaffold( + appBar: AppBar( + elevation: 0, + title: Text("backup_controller_page_backup".t()), + leading: IconButton( + onPressed: () { + context.maybePop(true); + }, + splashRadius: 24, + icon: const Icon(Icons.arrow_back_ios_rounded), + ), + actions: [ + IconButton( + onPressed: () { + context.pushRoute(const DriftBackupOptionsRoute()); + }, + icon: const Icon(Icons.settings_outlined), + tooltip: "backup_options".t(context: context), + ), + ], + ), + body: Stack( + children: [ + Padding( + padding: const EdgeInsets.only(left: 16.0, right: 16, bottom: 32), + child: ListView( + children: [ + const SizedBox(height: 8), + const _BackupAlbumSelectionCard(), + if (selectedAlbum.isNotEmpty) ...[ + const _TotalCard(), + const _BackupCard(), + const _RemainderCard(), + const Divider(), + BackupToggleButton(onStart: () async => await startBackup(), onStop: () async => await stopBackup()), + TextButton.icon( + icon: const Icon(Icons.info_outline_rounded), + onPressed: () => context.pushRoute(const DriftUploadDetailRoute()), + label: Text("view_details".t(context: context)), + ), + ], + ], + ), + ), + ], + ), + ); + } +} + +class _BackupAlbumSelectionCard extends ConsumerWidget { + const _BackupAlbumSelectionCard(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + Widget buildSelectedAlbumName() { + String text = "backup_controller_page_backup_selected".tr(); + final albums = ref + .watch(backupAlbumProvider) + .where((album) => album.backupSelection == BackupSelection.selected) + .toList(); + + if (albums.isNotEmpty) { + for (var album in albums) { + if (album.name == "Recent" || album.name == "Recents") { + text += "${album.name} (${'all'.tr()}), "; + } else { + text += "${album.name}, "; + } + } + + return Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Text( + text.trim().substring(0, text.length - 2), + style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor), + ), + ); + } else { + return Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Text( + "backup_controller_page_none_selected".tr(), + style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor), + ), + ); + } + } + + Widget buildExcludedAlbumName() { + String text = "backup_controller_page_excluded".tr(); + final albums = ref + .watch(backupAlbumProvider) + .where((album) => album.backupSelection == BackupSelection.excluded) + .toList(); + + if (albums.isNotEmpty) { + for (var album in albums) { + text += "${album.name}, "; + } + + return Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Text( + text.trim().substring(0, text.length - 2), + style: context.textTheme.labelLarge?.copyWith(color: Colors.red[300]), + ), + ); + } else { + return const SizedBox(); + } + } + + return Card( + shape: RoundedRectangleBorder( + borderRadius: const BorderRadius.all(Radius.circular(20)), + side: BorderSide(color: context.colorScheme.outlineVariant, width: 1), + ), + elevation: 0, + borderOnForeground: false, + child: ListTile( + minVerticalPadding: 18, + title: Text("backup_controller_page_albums", style: context.textTheme.titleMedium).tr(), + subtitle: Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "backup_controller_page_to_backup", + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), + ).tr(), + buildSelectedAlbumName(), + buildExcludedAlbumName(), + ], + ), + ), + trailing: ElevatedButton( + onPressed: () async { + await context.pushRoute(const DriftBackupAlbumSelectionRoute()); + final currentUser = ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id); + }, + child: const Text("select", style: TextStyle(fontWeight: FontWeight.bold)).tr(), + ), + ), + ); + } +} + +class _TotalCard extends ConsumerWidget { + const _TotalCard(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final totalCount = ref.watch(driftBackupProvider.select((p) => p.totalCount)); + + return BackupInfoCard( + title: "total".tr(), + subtitle: "backup_controller_page_total_sub".tr(), + info: totalCount.toString(), + ); + } +} + +class _BackupCard extends ConsumerWidget { + const _BackupCard(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final backupCount = ref.watch(driftBackupProvider.select((p) => p.backupCount)); + + return BackupInfoCard( + title: "backup_controller_page_backup".tr(), + subtitle: "backup_controller_page_backup_sub".tr(), + info: backupCount.toString(), + ); + } +} + +class _RemainderCard extends ConsumerWidget { + const _RemainderCard(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final remainderCount = ref.watch(driftBackupProvider.select((p) => p.remainderCount)); + return BackupInfoCard( + title: "backup_controller_page_remainder".tr(), + subtitle: "backup_controller_page_remainder_sub".tr(), + info: remainderCount.toString(), + ); + } +} diff --git a/mobile/lib/pages/backup/drift_backup_album_selection.page.dart b/mobile/lib/pages/backup/drift_backup_album_selection.page.dart new file mode 100644 index 0000000000..865845525a --- /dev/null +++ b/mobile/lib/pages/backup/drift_backup_album_selection.page.dart @@ -0,0 +1,458 @@ +import 'dart:io'; + +import 'package:auto_route/auto_route.dart'; +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/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.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/drift_backup.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; +import 'package:immich_mobile/widgets/backup/drift_album_info_list_tile.dart'; +import 'package:immich_mobile/widgets/common/search_field.dart'; + +@RoutePage() +class DriftBackupAlbumSelectionPage extends ConsumerStatefulWidget { + const DriftBackupAlbumSelectionPage({super.key}); + + @override + ConsumerState createState() => _DriftBackupAlbumSelectionPageState(); +} + +class _DriftBackupAlbumSelectionPageState extends ConsumerState { + String _searchQuery = ''; + bool _isSearchMode = false; + int _initialTotalAssetCount = 0; + bool _hasPopped = false; + late ValueNotifier _enableSyncUploadAlbum; + late TextEditingController _searchController; + late FocusNode _searchFocusNode; + + @override + void initState() { + super.initState(); + _enableSyncUploadAlbum = ValueNotifier(false); + _searchController = TextEditingController(); + _searchFocusNode = FocusNode(); + + _enableSyncUploadAlbum.value = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.syncAlbums); + ref.read(backupAlbumProvider.notifier).getAll(); + + _initialTotalAssetCount = ref.read(driftBackupProvider.select((p) => p.totalCount)); + } + + @override + void dispose() { + _enableSyncUploadAlbum.dispose(); + _searchController.dispose(); + _searchFocusNode.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final albums = ref.watch(backupAlbumProvider); + final albumCount = albums.length; + // Filter albums based on search query + final filteredAlbums = albums.where((album) { + if (_searchQuery.isEmpty) return true; + return album.name.toLowerCase().contains(_searchQuery.toLowerCase()); + }).toList(); + + final selectedBackupAlbums = albums.where((album) => album.backupSelection == BackupSelection.selected).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( + onPopInvokedWithResult: (didPop, result) async { + // There is an issue with Flutter where the pop event + // can be triggered multiple times, so we guard it with _hasPopped + if (didPop && !_hasPopped) { + _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( + appBar: AppBar( + leading: IconButton( + onPressed: () async => await context.maybePop(), + icon: const Icon(Icons.arrow_back_ios_rounded), + ), + title: _isSearchMode + ? SearchField( + hintText: 'search_albums'.t(context: context), + autofocus: true, + controller: _searchController, + focusNode: _searchFocusNode, + onChanged: (value) => setState(() => _searchQuery = value.trim()), + ) + : const Text("backup_album_selection_page_select_albums").t(context: context), + actions: [ + if (!_isSearchMode) + IconButton( + icon: const Icon(Icons.search), + onPressed: () => setState(() { + _isSearchMode = true; + _searchQuery = ''; + }), + ) + else + IconButton( + icon: const Icon(Icons.close), + onPressed: () => setState(() { + _isSearchMode = false; + _searchQuery = ''; + _searchController.clear(); + }), + ), + ], + elevation: 0, + ), + body: CustomScrollView( + physics: const ClampingScrollPhysics(), + slivers: [ + SliverToBoxAdapter( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0), + child: Text( + "backup_album_selection_page_selection_info", + style: context.textTheme.titleSmall, + ).t(context: context), + ), + + // Selected Album Chips + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Wrap( + children: [ + _SelectedAlbumNameChips(selectedBackupAlbums: selectedBackupAlbums), + _ExcludedAlbumNameChips(excludedBackupAlbums: excludedBackupAlbums), + ], + ), + ), + + // SettingsSwitchListTile( + // valueNotifier: _enableSyncUploadAlbum, + // title: "sync_albums".t(context: context), + // subtitle: "sync_upload_album_setting_subtitle".t(context: context), + // contentPadding: const EdgeInsets.symmetric(horizontal: 16), + // titleStyle: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.bold), + // subtitleStyle: context.textTheme.labelLarge?.copyWith(color: context.colorScheme.primary), + // onChanged: handleSyncAlbumToggle, + // ), + ListTile( + title: Text( + "albums_on_device_count".t(context: context, args: {'count': albumCount.toString()}), + style: context.textTheme.titleSmall, + ), + subtitle: Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Text( + "backup_album_selection_page_albums_tap", + style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor), + ).t(context: context), + ), + trailing: IconButton( + splashRadius: 16, + icon: Icon(Icons.info, size: 20, color: context.primaryColor), + onPressed: () { + // show the dialog + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))), + elevation: 5, + title: Text( + 'backup_album_selection_page_selection_info', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + 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); + } + }, + ), + ], + ), + ), + ); + } +} + +class _AlbumSelectionList extends StatelessWidget { + final List filteredAlbums; + final String searchQuery; + + const _AlbumSelectionList({required this.filteredAlbums, required this.searchQuery}); + + @override + Widget build(BuildContext context) { + if (filteredAlbums.isEmpty && searchQuery.isNotEmpty) { + return SliverToBoxAdapter( + child: Center( + child: Padding( + padding: const EdgeInsets.all(24.0), + child: Text('album_search_not_found'.t(context: context)), + ), + ), + ); + } + + if (filteredAlbums.isEmpty) { + return const SliverToBoxAdapter(child: Center(child: CircularProgressIndicator())); + } + + return SliverPadding( + padding: const EdgeInsets.symmetric(vertical: 12.0), + sliver: SliverList( + delegate: SliverChildBuilderDelegate(((context, index) { + return DriftAlbumInfoListTile(album: filteredAlbums[index]); + }), childCount: filteredAlbums.length), + ), + ); + } +} + +class _AlbumSelectionGrid extends StatelessWidget { + final List filteredAlbums; + final String searchQuery; + + const _AlbumSelectionGrid({required this.filteredAlbums, required this.searchQuery}); + + @override + Widget build(BuildContext context) { + if (filteredAlbums.isEmpty && searchQuery.isNotEmpty) { + return SliverToBoxAdapter( + child: Center( + child: Padding( + padding: const EdgeInsets.all(24.0), + child: Text('album_search_not_found'.t(context: context)), + ), + ), + ); + } + + if (filteredAlbums.isEmpty) { + return const SliverToBoxAdapter(child: Center(child: CircularProgressIndicator())); + } + + return SliverPadding( + padding: const EdgeInsets.all(12.0), + sliver: SliverGrid.builder( + gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( + maxCrossAxisExtent: 300, + mainAxisSpacing: 12, + crossAxisSpacing: 12, + ), + itemCount: filteredAlbums.length, + itemBuilder: ((context, index) { + return DriftAlbumInfoListTile(album: filteredAlbums[index]); + }), + ), + ); + } +} + +class _SelectedAlbumNameChips extends ConsumerWidget { + final List selectedBackupAlbums; + + const _SelectedAlbumNameChips({required this.selectedBackupAlbums}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return Wrap( + children: selectedBackupAlbums.asMap().entries.map((entry) { + final album = entry.value; + + void removeSelection() { + ref.read(backupAlbumProvider.notifier).deselectAlbum(album); + } + + return Padding( + padding: const EdgeInsets.only(right: 8.0), + child: GestureDetector( + onTap: removeSelection, + child: AnimatedContainer( + duration: const Duration(milliseconds: 200), + curve: Curves.easeInOut, + child: Chip( + label: Text( + album.name, + style: TextStyle( + fontSize: 12, + color: context.isDarkTheme ? Colors.black : Colors.white, + fontWeight: FontWeight.bold, + ), + ), + backgroundColor: context.primaryColor, + deleteIconColor: context.isDarkTheme ? Colors.black : Colors.white, + deleteIcon: const Icon(Icons.cancel_rounded, size: 15), + onDeleted: removeSelection, + ), + ), + ), + ); + }).toList(), + ); + } +} + +class _ExcludedAlbumNameChips extends ConsumerWidget { + final List excludedBackupAlbums; + + const _ExcludedAlbumNameChips({required this.excludedBackupAlbums}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return Wrap( + children: excludedBackupAlbums.asMap().entries.map((entry) { + final album = entry.value; + + void removeSelection() { + ref.read(backupAlbumProvider.notifier).deselectAlbum(album); + } + + return GestureDetector( + onTap: removeSelection, + child: Padding( + padding: const EdgeInsets.only(right: 8.0), + child: AnimatedContainer( + duration: const Duration(milliseconds: 200), + curve: Curves.easeInOut, + child: Chip( + label: Text( + album.name, + style: TextStyle(fontSize: 12, color: context.scaffoldBackgroundColor, fontWeight: FontWeight.bold), + ), + backgroundColor: Colors.red[300], + deleteIconColor: context.scaffoldBackgroundColor, + deleteIcon: const Icon(Icons.cancel_rounded, size: 15), + onDeleted: removeSelection, + ), + ), + ), + ); + }).toList(), + ); + } +} + +class _SelectAllButton extends ConsumerWidget { + final List filteredAlbums; + final List selectedBackupAlbums; + + const _SelectAllButton({required this.filteredAlbums, required this.selectedBackupAlbums}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final canSelectAll = filteredAlbums.where((album) => album.backupSelection != BackupSelection.selected).isNotEmpty; + + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), + child: Row( + children: [ + Expanded( + child: ElevatedButton.icon( + onPressed: canSelectAll + ? () { + for (final album in filteredAlbums) { + if (album.backupSelection != BackupSelection.selected) { + ref.read(backupAlbumProvider.notifier).selectAlbum(album); + } + } + } + : null, + icon: const Icon(Icons.select_all), + label: AnimatedSwitcher( + duration: const Duration(milliseconds: 200), + child: Text("select_all".t(context: context)), + ), + style: ElevatedButton.styleFrom(padding: const EdgeInsets.symmetric(vertical: 12.0)), + ), + ), + const SizedBox(width: 8.0), + Expanded( + child: OutlinedButton.icon( + onPressed: selectedBackupAlbums.isNotEmpty + ? () { + for (final album in filteredAlbums) { + if (album.backupSelection == BackupSelection.selected) { + ref.read(backupAlbumProvider.notifier).deselectAlbum(album); + } + } + } + : null, + icon: const Icon(Icons.deselect), + label: Text('deselect_all'.t(context: context)), + style: OutlinedButton.styleFrom(padding: const EdgeInsets.symmetric(vertical: 12.0)), + ), + ), + ], + ), + ); + } +} diff --git a/mobile/lib/pages/backup/drift_backup_options.page.dart b/mobile/lib/pages/backup/drift_backup_options.page.dart new file mode 100644 index 0000000000..92f911ae1e --- /dev/null +++ b/mobile/lib/pages/backup/drift_backup_options.page.dart @@ -0,0 +1,68 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/providers/app_settings.provider.dart'; +import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; +import 'package:immich_mobile/widgets/settings/backup_settings/drift_backup_settings.dart'; + +@RoutePage() +class DriftBackupOptionsPage extends ConsumerWidget { + const DriftBackupOptionsPage({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + bool hasPopped = false; + final previousWifiReqForVideos = Store.tryGet(StoreKey.useWifiForUploadVideos) ?? false; + final previousWifiReqForPhotos = Store.tryGet(StoreKey.useWifiForUploadPhotos) ?? false; + return PopScope( + onPopInvokedWithResult: (didPop, result) async { + // There is an issue with Flutter where the pop event + // can be triggered multiple times, so we guard it with _hasPopped + + final currentWifiReqForVideos = Store.tryGet(StoreKey.useWifiForUploadVideos) ?? false; + final currentWifiReqForPhotos = Store.tryGet(StoreKey.useWifiForUploadPhotos) ?? false; + + if (currentWifiReqForVideos == previousWifiReqForVideos && + currentWifiReqForPhotos == previousWifiReqForPhotos) { + return; + } + + if (didPop && !hasPopped) { + hasPopped = true; + + final currentUser = ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + + await ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id); + final isBackupEnabled = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup); + if (!isBackupEnabled) { + return; + } + + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text("network_requirements_updated".t(context: context)), + duration: const Duration(seconds: 4), + ), + ); + + final backupNotifier = ref.read(driftBackupProvider.notifier); + backupNotifier.cancel().then((_) { + backupNotifier.startBackup(currentUser.id); + }); + } + }, + child: Scaffold( + appBar: AppBar(title: Text("backup_options".t(context: context))), + body: const DriftBackupSettings(), + ), + ); + } +} diff --git a/mobile/lib/pages/backup/drift_upload_detail.page.dart b/mobile/lib/pages/backup/drift_upload_detail.page.dart new file mode 100644 index 0000000000..36dbe4e128 --- /dev/null +++ b/mobile/lib/pages/backup/drift_upload_detail.page.dart @@ -0,0 +1,313 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; +import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart'; +import 'package:immich_mobile/utils/bytes_units.dart'; +import 'package:path/path.dart' as path; + +@RoutePage() +class DriftUploadDetailPage extends ConsumerWidget { + const DriftUploadDetailPage({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final uploadItems = ref.watch(driftBackupProvider.select((state) => state.uploadItems)); + + return Scaffold( + appBar: AppBar( + title: Text("upload_details".t(context: context)), + backgroundColor: context.colorScheme.surface, + elevation: 0, + scrolledUnderElevation: 1, + ), + body: uploadItems.isEmpty ? _buildEmptyState(context) : _buildUploadList(uploadItems), + ); + } + + Widget _buildEmptyState(BuildContext context) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.cloud_off_rounded, size: 80, color: context.colorScheme.onSurface.withValues(alpha: 0.3)), + const SizedBox(height: 16), + Text( + "no_uploads_in_progress".t(context: context), + style: context.textTheme.titleMedium?.copyWith(color: context.colorScheme.onSurface.withValues(alpha: 0.6)), + ), + ], + ), + ); + } + + Widget _buildUploadList(Map uploadItems) { + return ListView.separated( + addAutomaticKeepAlives: true, + padding: const EdgeInsets.all(16), + itemCount: uploadItems.length, + separatorBuilder: (context, index) => const SizedBox(height: 4), + itemBuilder: (context, index) { + final item = uploadItems.values.elementAt(index); + return _buildUploadCard(context, item); + }, + ); + } + + Widget _buildUploadCard(BuildContext context, DriftUploadStatus item) { + final isCompleted = item.progress >= 1.0; + final double progressPercentage = (item.progress * 100).clamp(0, 100); + + return Card( + elevation: 0, + color: item.isFailed != null ? context.colorScheme.errorContainer : context.colorScheme.surfaceContainer, + shape: RoundedRectangleBorder( + borderRadius: const BorderRadius.all(Radius.circular(16)), + side: BorderSide(color: context.colorScheme.outline.withValues(alpha: 0.1), width: 1), + ), + child: InkWell( + onTap: () => _showFileDetailDialog(context, item), + borderRadius: const BorderRadius.all(Radius.circular(16)), + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + path.basename(item.filename), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + const SizedBox(height: 4), + Text( + 'Tap for more details', + style: context.textTheme.bodySmall?.copyWith( + color: context.colorScheme.onSurface.withValues(alpha: 0.6), + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ], + ), + ), + _buildProgressIndicator( + context, + item.progress, + progressPercentage, + isCompleted, + item.networkSpeedAsString, + ), + ], + ), + ], + ), + ), + ), + ); + } + + Widget _buildProgressIndicator( + BuildContext context, + double progress, + double percentage, + bool isCompleted, + String networkSpeedAsString, + ) { + return Column( + children: [ + Stack( + alignment: AlignmentDirectional.center, + children: [ + SizedBox( + width: 36, + height: 36, + child: TweenAnimationBuilder( + tween: Tween(begin: 0.0, end: progress), + duration: const Duration(milliseconds: 300), + builder: (context, value, _) => CircularProgressIndicator( + backgroundColor: context.colorScheme.outline.withValues(alpha: 0.2), + strokeWidth: 3, + value: value, + color: isCompleted ? context.colorScheme.primary : context.colorScheme.secondary, + ), + ), + ), + if (isCompleted) + Icon(Icons.check_circle_rounded, size: 28, color: context.colorScheme.primary) + else + Text( + percentage.toStringAsFixed(0), + style: context.textTheme.labelSmall?.copyWith(fontWeight: FontWeight.bold, fontSize: 10), + ), + ], + ), + Text( + networkSpeedAsString, + style: context.textTheme.labelSmall?.copyWith( + color: context.colorScheme.onSurface.withValues(alpha: 0.6), + fontSize: 10, + ), + ), + ], + ); + } + + Future _showFileDetailDialog(BuildContext context, DriftUploadStatus item) async { + showDialog( + context: context, + builder: (context) => FileDetailDialog(uploadStatus: item), + ); + } +} + +class FileDetailDialog extends ConsumerWidget { + final DriftUploadStatus uploadStatus; + + const FileDetailDialog({super.key, required this.uploadStatus}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return AlertDialog( + insetPadding: const EdgeInsets.all(20), + backgroundColor: context.colorScheme.surfaceContainerLow, + shape: RoundedRectangleBorder( + borderRadius: const BorderRadius.all(Radius.circular(16)), + side: BorderSide(color: context.colorScheme.outline.withValues(alpha: 0.2), width: 1), + ), + title: Row( + children: [ + Icon(Icons.info_outline, color: context.primaryColor, size: 24), + const SizedBox(width: 8), + Expanded( + child: Text( + "details".t(context: context), + style: context.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w600, color: context.primaryColor), + ), + ), + ], + ), + content: SizedBox( + width: double.maxFinite, + child: FutureBuilder( + future: _getAssetDetails(ref, uploadStatus.taskId), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const SizedBox(height: 200, child: Center(child: CircularProgressIndicator())); + } + + final asset = snapshot.data; + return SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + // Thumbnail at the top center + Center( + child: ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(12)), + child: Container( + width: 128, + height: 128, + decoration: BoxDecoration( + border: Border.all(color: context.colorScheme.outline.withValues(alpha: 0.2), width: 1), + borderRadius: const BorderRadius.all(Radius.circular(12)), + ), + child: asset != null + ? Thumbnail(asset: asset, size: const Size(512, 512), fit: BoxFit.cover) + : null, + ), + ), + ), + const SizedBox(height: 24), + if (asset != null) ...[ + _buildInfoSection(context, [ + _buildInfoRow(context, "Filename", path.basename(uploadStatus.filename)), + _buildInfoRow(context, "Local ID", asset.id), + _buildInfoRow(context, "File Size", formatHumanReadableBytes(uploadStatus.fileSize, 2)), + if (asset.width != null) _buildInfoRow(context, "Width", "${asset.width}px"), + if (asset.height != null) _buildInfoRow(context, "Height", "${asset.height}px"), + _buildInfoRow(context, "Created At", asset.createdAt.toString()), + _buildInfoRow(context, "Updated At", asset.updatedAt.toString()), + if (asset.checksum != null) _buildInfoRow(context, "Checksum", asset.checksum!), + ]), + ], + ], + ), + ); + }, + ), + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: Text( + "close".t(), + style: TextStyle(fontWeight: FontWeight.w600, color: context.primaryColor), + ), + ), + ], + ); + } + + Widget _buildInfoSection(BuildContext context, List children) { + return Container( + width: double.infinity, + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: context.colorScheme.surfaceContainer, + borderRadius: const BorderRadius.all(Radius.circular(12)), + border: Border.all(color: context.colorScheme.outline.withValues(alpha: 0.1), width: 1), + ), + child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [...children]), + ); + } + + Widget _buildInfoRow(BuildContext context, String label, String value) { + return Padding( + padding: const EdgeInsets.only(bottom: 4), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: 100, + child: Text( + "$label:", + style: context.textTheme.labelMedium?.copyWith( + fontWeight: FontWeight.w500, + color: context.colorScheme.onSurface.withValues(alpha: 0.7), + ), + ), + ), + Expanded( + child: Text( + value, + style: context.textTheme.labelMedium?.copyWith(), + maxLines: 3, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + ); + } + + Future _getAssetDetails(WidgetRef ref, String localAssetId) async { + try { + final repository = ref.read(localAssetRepository); + return await repository.getById(localAssetId); + } catch (e) { + return null; + } + } +} diff --git a/mobile/lib/pages/backup/failed_backup_status.page.dart b/mobile/lib/pages/backup/failed_backup_status.page.dart index 167b0f2ebd..b533895cd7 100644 --- a/mobile/lib/pages/backup/failed_backup_status.page.dart +++ b/mobile/lib/pages/backup/failed_backup_status.page.dart @@ -25,9 +25,7 @@ class FailedBackupStatusPage extends HookConsumerWidget { context.maybePop(true); }, splashRadius: 24, - icon: const Icon( - Icons.arrow_back_ios_rounded, - ), + icon: const Icon(Icons.arrow_back_ios_rounded), ), ), body: ListView.builder( @@ -37,19 +35,13 @@ class FailedBackupStatusPage extends HookConsumerWidget { var errorAsset = errorBackupList.elementAt(index); return Padding( - padding: const EdgeInsets.symmetric( - horizontal: 12.0, - vertical: 4, - ), + padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 4), child: Card( shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all( Radius.circular(15), // if you need this ), - side: BorderSide( - color: Colors.black12, - width: 1, - ), + side: BorderSide(color: Colors.black12, width: 1), ), elevation: 0, child: Row( @@ -57,12 +49,7 @@ class FailedBackupStatusPage extends HookConsumerWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ ConstrainedBox( - constraints: const BoxConstraints( - minWidth: 100, - minHeight: 100, - maxWidth: 100, - maxHeight: 150, - ), + constraints: const BoxConstraints(minWidth: 100, minHeight: 100, maxWidth: 100, maxHeight: 150), child: ClipRRect( borderRadius: const BorderRadius.only( bottomLeft: Radius.circular(15), @@ -71,11 +58,7 @@ class FailedBackupStatusPage extends HookConsumerWidget { clipBehavior: Clip.hardEdge, child: Image( fit: BoxFit.cover, - image: ImmichLocalThumbnailProvider( - asset: errorAsset.asset, - height: 512, - width: 512, - ), + image: ImmichLocalThumbnailProvider(asset: errorAsset.asset, height: 512, width: 512), ), ), ), @@ -91,22 +74,14 @@ class FailedBackupStatusPage extends HookConsumerWidget { children: [ Text( DateFormat.yMMMMd().format( - DateTime.parse( - errorAsset.fileCreatedAt.toString(), - ).toLocal(), + DateTime.parse(errorAsset.fileCreatedAt.toString()).toLocal(), ), style: TextStyle( fontWeight: FontWeight.w600, - color: context.isDarkTheme - ? Colors.white70 - : Colors.grey[800], + color: context.isDarkTheme ? Colors.white70 : Colors.grey[800], ), ), - Icon( - Icons.error, - color: Colors.red.withAlpha(200), - size: 18, - ), + Icon(Icons.error, color: Colors.red.withAlpha(200), size: 18), ], ), Padding( @@ -115,19 +90,14 @@ class FailedBackupStatusPage extends HookConsumerWidget { errorAsset.fileName, maxLines: 1, overflow: TextOverflow.ellipsis, - style: TextStyle( - fontWeight: FontWeight.bold, - color: context.primaryColor, - ), + style: TextStyle(fontWeight: FontWeight.bold, color: context.primaryColor), ), ), Text( errorAsset.errorMessage, style: TextStyle( fontWeight: FontWeight.w500, - color: context.isDarkTheme - ? Colors.white70 - : Colors.grey[800], + color: context.isDarkTheme ? Colors.white70 : Colors.grey[800], ), ), ], diff --git a/mobile/lib/pages/common/activities.page.dart b/mobile/lib/pages/common/activities.page.dart index 776ee9977b..1a1955af40 100644 --- a/mobile/lib/pages/common/activities.page.dart +++ b/mobile/lib/pages/common/activities.page.dart @@ -16,9 +16,7 @@ import 'package:immich_mobile/widgets/activities/dismissible_activity.dart'; @RoutePage() class ActivitiesPage extends HookConsumerWidget { - const ActivitiesPage({ - super.key, - }); + const ActivitiesPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -27,10 +25,8 @@ class ActivitiesPage extends HookConsumerWidget { final asset = ref.watch(currentAssetProvider); final user = ref.watch(currentUserProvider); - final activityNotifier = ref - .read(albumActivityProvider(album.remoteId!, asset?.remoteId).notifier); - final activities = - ref.watch(albumActivityProvider(album.remoteId!, asset?.remoteId)); + final activityNotifier = ref.read(albumActivityProvider(album.remoteId!, asset?.remoteId).notifier); + final activities = ref.watch(albumActivityProvider(album.remoteId!, asset?.remoteId)); final listViewScrollController = useScrollController(); @@ -49,10 +45,7 @@ class ActivitiesPage extends HookConsumerWidget { body: activities.widgetWhen( onData: (data) { final liked = data.firstWhereOrNull( - (a) => - a.type == ActivityType.like && - a.user.id == user?.id && - a.assetId == asset?.remoteId, + (a) => a.type == ActivityType.like && a.user.id == user?.id && a.assetId == asset?.remoteId, ); return SafeArea( @@ -65,14 +58,11 @@ class ActivitiesPage extends HookConsumerWidget { itemBuilder: (context, index) { // Additional vertical gap after the last element if (index == data.length) { - return const SizedBox( - height: 80, - ); + return const SizedBox(height: 80); } final activity = data[index]; - final canDelete = activity.user.id == user?.id || - album.ownerId == user?.id; + final canDelete = activity.user.id == user?.id || album.ownerId == user?.id; return Padding( padding: const EdgeInsets.all(5), @@ -80,8 +70,7 @@ class ActivitiesPage extends HookConsumerWidget { activity.id, ActivityTile(activity), onDismiss: canDelete - ? (activityId) async => await activityNotifier - .removeActivity(activity.id) + ? (activityId) async => await activityNotifier.removeActivity(activity.id) : null, ), ); diff --git a/mobile/lib/pages/common/app_log.page.dart b/mobile/lib/pages/common/app_log.page.dart index 359a541de0..fe0c0ea442 100644 --- a/mobile/lib/pages/common/app_log.page.dart +++ b/mobile/lib/pages/common/app_log.page.dart @@ -12,17 +12,13 @@ import 'package:intl/intl.dart'; @RoutePage() class AppLogPage extends HookConsumerWidget { - const AppLogPage({ - super.key, - }); + const AppLogPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final immichLogger = LogService.I; final shouldReload = useState(false); - final logMessages = useFuture( - useMemoized(() => immichLogger.getMessages(), [shouldReload.value]), - ); + final logMessages = useFuture(useMemoized(() => immichLogger.getMessages(), [shouldReload.value])); Widget colorStatusIndicator(Color color) { return Column( @@ -31,38 +27,29 @@ class AppLogPage extends HookConsumerWidget { Container( width: 10, height: 10, - decoration: BoxDecoration( - color: color, - shape: BoxShape.circle, - ), + decoration: BoxDecoration(color: color, shape: BoxShape.circle), ), ], ); } Widget buildLeadingIcon(LogLevel level) => switch (level) { - LogLevel.info => colorStatusIndicator(context.primaryColor), - LogLevel.severe => colorStatusIndicator(Colors.redAccent), - LogLevel.warning => colorStatusIndicator(Colors.orangeAccent), - _ => colorStatusIndicator(Colors.grey), - }; + LogLevel.info => colorStatusIndicator(context.primaryColor), + LogLevel.severe => colorStatusIndicator(Colors.redAccent), + LogLevel.warning => colorStatusIndicator(Colors.orangeAccent), + _ => colorStatusIndicator(Colors.grey), + }; Color getTileColor(LogLevel level) => switch (level) { - LogLevel.info => Colors.transparent, - LogLevel.severe => Colors.redAccent.withValues(alpha: 0.25), - LogLevel.warning => Colors.orangeAccent.withValues(alpha: 0.25), - _ => context.primaryColor.withValues(alpha: 0.1), - }; + LogLevel.info => Colors.transparent, + LogLevel.severe => Colors.redAccent.withValues(alpha: 0.25), + LogLevel.warning => Colors.orangeAccent.withValues(alpha: 0.25), + _ => context.primaryColor.withValues(alpha: 0.1), + }; return Scaffold( appBar: AppBar( - title: const Text( - "Logs", - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 16.0, - ), - ), + title: const Text("Logs", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16.0)), scrolledUnderElevation: 1, elevation: 2, actions: [ @@ -81,12 +68,7 @@ class AppLogPage extends HookConsumerWidget { Builder( builder: (BuildContext iconContext) { return IconButton( - icon: Icon( - Icons.share_rounded, - color: context.primaryColor, - semanticLabel: "Share logs", - size: 20.0, - ), + icon: Icon(Icons.share_rounded, color: context.primaryColor, semanticLabel: "Share logs", size: 20.0), onPressed: () { ImmichLogger.shareLogs(iconContext); }, @@ -98,10 +80,7 @@ class AppLogPage extends HookConsumerWidget { onPressed: () { context.maybePop(); }, - icon: const Icon( - Icons.arrow_back_ios_new_rounded, - size: 20.0, - ), + icon: const Icon(Icons.arrow_back_ios_new_rounded, size: 20.0), ), centerTitle: true, ), @@ -113,11 +92,7 @@ class AppLogPage extends HookConsumerWidget { itemBuilder: (context, index) { var logMessage = logMessages.data![index]; return ListTile( - onTap: () => context.pushRoute( - AppLogDetailRoute( - logMessage: logMessage, - ), - ), + onTap: () => context.pushRoute(AppLogDetailRoute(logMessage: logMessage)), trailing: const Icon(Icons.arrow_forward_ios_rounded), visualDensity: VisualDensity.compact, dense: true, @@ -125,18 +100,11 @@ class AppLogPage extends HookConsumerWidget { minLeadingWidth: 10, title: Text( truncateLogMessage(logMessage.message, 4), - style: TextStyle( - fontSize: 14.0, - color: context.colorScheme.onSurface, - fontFamily: "Inconsolata", - ), + style: TextStyle(fontSize: 14.0, color: context.colorScheme.onSurface, fontFamily: "Inconsolata"), ), subtitle: Text( "at ${DateFormat("HH:mm:ss.SSS").format(logMessage.createdAt)} in ${logMessage.logger}", - style: TextStyle( - fontSize: 12.0, - color: context.colorScheme.onSurfaceSecondary, - ), + style: TextStyle(fontSize: 12.0, color: context.colorScheme.onSurfaceSecondary), ), leading: buildLeadingIcon(logMessage.level), ); diff --git a/mobile/lib/pages/common/app_log_detail.page.dart b/mobile/lib/pages/common/app_log_detail.page.dart index b88d6aeb3a..a9cf634faf 100644 --- a/mobile/lib/pages/common/app_log_detail.page.dart +++ b/mobile/lib/pages/common/app_log_detail.page.dart @@ -27,11 +27,7 @@ class AppLogDetailPage extends HookConsumerWidget { padding: const EdgeInsets.only(bottom: 8.0), child: Text( header, - style: TextStyle( - fontSize: 12.0, - color: context.primaryColor, - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 12.0, color: context.primaryColor, fontWeight: FontWeight.bold), ), ), IconButton( @@ -41,38 +37,26 @@ class AppLogDetailPage extends HookConsumerWidget { SnackBar( content: Text( "Copied to clipboard", - style: context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), ), ), ); }); }, - icon: Icon( - Icons.copy, - size: 16.0, - color: context.primaryColor, - ), + icon: Icon(Icons.copy, size: 16.0, color: context.primaryColor), ), ], ), Container( decoration: BoxDecoration( color: context.colorScheme.surfaceContainerHigh, - borderRadius: const BorderRadius.all( - Radius.circular(15.0), - ), + borderRadius: const BorderRadius.all(Radius.circular(15.0)), ), child: Padding( padding: const EdgeInsets.all(8.0), child: SelectableText( text, - style: const TextStyle( - fontSize: 12.0, - fontWeight: FontWeight.bold, - fontFamily: "Inconsolata", - ), + style: const TextStyle(fontSize: 12.0, fontWeight: FontWeight.bold, fontFamily: "Inconsolata"), ), ), ), @@ -91,29 +75,19 @@ class AppLogDetailPage extends HookConsumerWidget { padding: const EdgeInsets.only(bottom: 8.0), child: Text( "FROM", - style: TextStyle( - fontSize: 12.0, - color: context.primaryColor, - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 12.0, color: context.primaryColor, fontWeight: FontWeight.bold), ), ), Container( decoration: BoxDecoration( color: context.colorScheme.surfaceContainerHigh, - borderRadius: const BorderRadius.all( - Radius.circular(15.0), - ), + borderRadius: const BorderRadius.all(Radius.circular(15.0)), ), child: Padding( padding: const EdgeInsets.all(8.0), child: SelectableText( context1.toString(), - style: const TextStyle( - fontSize: 12.0, - fontWeight: FontWeight.bold, - fontFamily: "Inconsolata", - ), + style: const TextStyle(fontSize: 12.0, fontWeight: FontWeight.bold, fontFamily: "Inconsolata"), ), ), ), @@ -123,22 +97,14 @@ class AppLogDetailPage extends HookConsumerWidget { } return Scaffold( - appBar: AppBar( - title: const Text("Log Detail"), - ), + appBar: AppBar(title: const Text("Log Detail")), body: SafeArea( child: ListView( children: [ buildTextWithCopyButton("MESSAGE", logMessage.message), - if (logMessage.error != null) - buildTextWithCopyButton("DETAILS", logMessage.error.toString()), - if (logMessage.logger != null) - buildLogContext1(logMessage.logger.toString()), - if (logMessage.stack != null) - buildTextWithCopyButton( - "STACK TRACE", - logMessage.stack.toString(), - ), + if (logMessage.error != null) buildTextWithCopyButton("DETAILS", logMessage.error.toString()), + if (logMessage.logger != null) buildLogContext1(logMessage.logger.toString()), + if (logMessage.stack != null) buildTextWithCopyButton("STACK TRACE", logMessage.stack.toString()), ], ), ), diff --git a/mobile/lib/pages/common/change_experience.page.dart b/mobile/lib/pages/common/change_experience.page.dart index 74c3116962..45392a38f6 100644 --- a/mobile/lib/pages/common/change_experience.page.dart +++ b/mobile/lib/pages/common/change_experience.page.dart @@ -2,12 +2,17 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/providers/album/album.provider.dart'; import 'package:immich_mobile/providers/asset.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/manual_upload.provider.dart'; import 'package:immich_mobile/providers/gallery_permission.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; +import 'package:immich_mobile/providers/websocket.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/utils/migration.dart'; import 'package:permission_handler/permission_handler.dart'; @@ -42,19 +47,29 @@ class _ChangeExperiencePageState extends ConsumerState { albumNotifier.dispose(); } - final permission = await ref - .read(galleryPermissionNotifier.notifier) - .requestGalleryPermission(); + // Cancel uploads + await Store.put(StoreKey.backgroundBackup, false); + ref + .read(backupProvider.notifier) + .configureBackgroundBackup(enabled: false, onBatteryInfo: () {}, onError: (_) {}); + ref.read(backupProvider.notifier).setAutoBackup(false); + ref.read(backupProvider.notifier).cancelBackup(); + ref.read(manualUploadProvider.notifier).cancelBackup(); + // Start listening to new websocket events + ref.read(websocketProvider.notifier).stopListenToOldEvents(); + ref.read(websocketProvider.notifier).startListeningToBetaEvents(); + + final permission = await ref.read(galleryPermissionNotifier.notifier).requestGalleryPermission(); if (permission.isGranted) { await ref.read(backgroundSyncProvider).syncLocal(full: true); - await migrateDeviceAssetToSqlite( - ref.read(isarProvider), - ref.read(driftProvider), - ); + await migrateDeviceAssetToSqlite(ref.read(isarProvider), ref.read(driftProvider)); + await migrateBackupAlbumsToSqlite(ref.read(isarProvider), ref.read(driftProvider)); } } else { await ref.read(backgroundSyncProvider).cancel(); + ref.read(websocketProvider.notifier).stopListeningToBetaEvents(); + ref.read(websocketProvider.notifier).startListeningToOldEvents(); } if (mounted) { @@ -75,16 +90,8 @@ class _ChangeExperiencePageState extends ConsumerState { AnimatedSwitcher( duration: Durations.long4, child: hasMigrated - ? const Icon( - Icons.check_circle_rounded, - color: Colors.green, - size: 48.0, - ) - : const SizedBox( - width: 50.0, - height: 50.0, - child: CircularProgressIndicator(), - ), + ? const Icon(Icons.check_circle_rounded, color: Colors.green, size: 48.0) + : const SizedBox(width: 50.0, height: 50.0, child: CircularProgressIndicator()), ), const SizedBox(height: 16.0), Center( @@ -113,9 +120,7 @@ class _ChangeExperiencePageState extends ConsumerState { child: ElevatedButton( onPressed: () { context.replaceRoute( - widget.switchingToBeta - ? const TabShellRoute() - : const TabControllerRoute(), + widget.switchingToBeta ? const TabShellRoute() : const TabControllerRoute(), ); }, child: const Text("Continue"), diff --git a/mobile/lib/pages/common/create_album.page.dart b/mobile/lib/pages/common/create_album.page.dart index faf1671a9e..5a0d4154f8 100644 --- a/mobile/lib/pages/common/create_album.page.dart +++ b/mobile/lib/pages/common/create_album.page.dart @@ -20,22 +20,16 @@ import 'package:immich_mobile/widgets/album/shared_album_thumbnail_image.dart'; class CreateAlbumPage extends HookConsumerWidget { final List? assets; - const CreateAlbumPage({ - super.key, - this.assets, - }); + const CreateAlbumPage({super.key, this.assets}); @override Widget build(BuildContext context, WidgetRef ref) { - final albumTitleController = - useTextEditingController.fromValue(TextEditingValue.empty); + final albumTitleController = useTextEditingController.fromValue(TextEditingValue.empty); final albumTitleTextFieldFocusNode = useFocusNode(); final albumDescriptionTextFieldFocusNode = useFocusNode(); final isAlbumTitleTextFieldFocus = useState(false); final isAlbumTitleEmpty = useState(true); - final selectedAssets = useState>( - assets != null ? Set.from(assets!) : const {}, - ); + final selectedAssets = useState>(assets != null ? Set.from(assets!) : const {}); void onBackgroundTapped() { albumTitleTextFieldFocusNode.unfocus(); @@ -45,19 +39,13 @@ class CreateAlbumPage extends HookConsumerWidget { if (albumTitleController.text.isEmpty) { albumTitleController.text = 'create_album_page_untitled'.tr(); isAlbumTitleEmpty.value = false; - ref - .watch(albumTitleProvider.notifier) - .setAlbumTitle('create_album_page_untitled'.tr()); + ref.watch(albumTitleProvider.notifier).setAlbumTitle('create_album_page_untitled'.tr()); } } onSelectPhotosButtonPressed() async { - AssetSelectionPageResult? selectedAsset = - await context.pushRoute( - AlbumAssetSelectionRoute( - existingAssets: selectedAssets.value, - canDeselect: true, - ), + AssetSelectionPageResult? selectedAsset = await context.pushRoute( + AlbumAssetSelectionRoute(existingAssets: selectedAssets.value, canDeselect: true), ); if (selectedAsset == null) { selectedAssets.value = const {}; @@ -68,10 +56,7 @@ class CreateAlbumPage extends HookConsumerWidget { buildTitleInputField() { return Padding( - padding: const EdgeInsets.only( - right: 10, - left: 10, - ), + padding: const EdgeInsets.only(right: 10, left: 10), child: AlbumTitleTextField( isAlbumTitleEmpty: isAlbumTitleEmpty, albumTitleTextFieldFocusNode: albumTitleTextFieldFocusNode, @@ -83,10 +68,7 @@ class CreateAlbumPage extends HookConsumerWidget { buildDescriptionInputField() { return Padding( - padding: const EdgeInsets.only( - right: 10, - left: 10, - ), + padding: const EdgeInsets.only(right: 10, left: 10), child: AlbumViewerEditableDescription( albumDescription: '', descriptionFocusNode: albumDescriptionTextFieldFocusNode, @@ -99,10 +81,7 @@ class CreateAlbumPage extends HookConsumerWidget { return SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.only(top: 200, left: 18), - child: Text( - 'create_shared_album_page_share_add_assets', - style: context.textTheme.labelLarge, - ).tr(), + child: Text('create_shared_album_page_share_add_assets', style: context.textTheme.labelLarge).tr(), ), ); } @@ -118,20 +97,12 @@ class CreateAlbumPage extends HookConsumerWidget { child: FilledButton.icon( style: FilledButton.styleFrom( alignment: Alignment.centerLeft, - padding: - const EdgeInsets.symmetric(vertical: 24, horizontal: 16), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(10), - ), - ), + padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 16), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))), backgroundColor: context.colorScheme.surfaceContainerHigh, ), onPressed: onSelectPhotosButtonPressed, - icon: Icon( - Icons.add_rounded, - color: context.primaryColor, - ), + icon: Icon(Icons.add_rounded, color: context.primaryColor), label: Padding( padding: const EdgeInsets.only(left: 8.0), child: Text( @@ -179,17 +150,12 @@ class CreateAlbumPage extends HookConsumerWidget { crossAxisSpacing: 5.0, mainAxisSpacing: 5, ), - delegate: SliverChildBuilderDelegate( - (BuildContext context, int index) { - return GestureDetector( - onTap: onBackgroundTapped, - child: SharedAlbumThumbnailImage( - asset: selectedAssets.value.elementAt(index), - ), - ); - }, - childCount: selectedAssets.value.length, - ), + delegate: SliverChildBuilderDelegate((BuildContext context, int index) { + return GestureDetector( + onTap: onBackgroundTapped, + child: SharedAlbumThumbnailImage(asset: selectedAssets.value.elementAt(index)), + ); + }, childCount: selectedAssets.value.length), ), ); } @@ -199,10 +165,9 @@ class CreateAlbumPage extends HookConsumerWidget { Future createAlbum() async { onBackgroundTapped(); - var newAlbum = await ref.watch(albumProvider.notifier).createAlbum( - ref.read(albumTitleProvider), - selectedAssets.value, - ); + var newAlbum = await ref + .watch(albumProvider.notifier) + .createAlbum(ref.read(albumTitleProvider), selectedAssets.value); if (newAlbum != null) { ref.read(albumProvider.notifier).refreshRemoteAlbums(); @@ -225,20 +190,15 @@ class CreateAlbumPage extends HookConsumerWidget { }, icon: const Icon(Icons.close_rounded), ), - title: const Text( - 'create_album', - ).tr(), + title: const Text('create_album').tr(), actions: [ TextButton( - onPressed: - albumTitleController.text.isNotEmpty ? createAlbum : null, + onPressed: albumTitleController.text.isNotEmpty ? createAlbum : null, child: Text( 'create'.tr(), style: TextStyle( fontWeight: FontWeight.bold, - color: albumTitleController.text.isNotEmpty - ? context.primaryColor - : context.themeData.disabledColor, + color: albumTitleController.text.isNotEmpty ? context.primaryColor : context.themeData.disabledColor, ), ), ), diff --git a/mobile/lib/pages/common/download_panel.dart b/mobile/lib/pages/common/download_panel.dart index cc543c9e4e..0775f5b4e4 100644 --- a/mobile/lib/pages/common/download_panel.dart +++ b/mobile/lib/pages/common/download_panel.dart @@ -6,22 +6,13 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/providers/asset_viewer/download.provider.dart'; class DownloadPanel extends ConsumerWidget { - const DownloadPanel({ - super.key, - }); + const DownloadPanel({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { - final showProgress = ref.watch( - downloadStateProvider.select((state) => state.showProgress), - ); + final showProgress = ref.watch(downloadStateProvider.select((state) => state.showProgress)); - final tasks = ref - .watch( - downloadStateProvider.select((state) => state.taskProgress), - ) - .entries - .toList(); + final tasks = ref.watch(downloadStateProvider.select((state) => state.taskProgress)).entries.toList(); onCancelDownload(String id) { ref.watch(downloadStateProvider.notifier).cancelDownload(id); @@ -34,8 +25,7 @@ class DownloadPanel extends ConsumerWidget { duration: const Duration(milliseconds: 300), child: showProgress ? ConstrainedBox( - constraints: - BoxConstraints.loose(Size(context.width - 32, 300)), + constraints: BoxConstraints.loose(Size(context.width - 32, 300)), child: ListView.builder( shrinkWrap: true, itemCount: tasks.length, @@ -75,62 +65,46 @@ class DownloadTaskTile extends StatelessWidget { final progressPercent = (progress * 100).round(); String getStatusText() => switch (status) { - TaskStatus.running => 'downloading'.tr(), - TaskStatus.complete => 'download_complete'.tr(), - TaskStatus.failed => 'download_failed'.tr(), - TaskStatus.canceled => 'download_canceled'.tr(), - TaskStatus.paused => 'download_paused'.tr(), - TaskStatus.enqueued => 'download_enqueue'.tr(), - TaskStatus.notFound => 'download_notfound'.tr(), - TaskStatus.waitingToRetry => 'download_waiting_to_retry'.tr(), - }; + TaskStatus.running => 'downloading'.tr(), + TaskStatus.complete => 'download_complete'.tr(), + TaskStatus.failed => 'download_failed'.tr(), + TaskStatus.canceled => 'download_canceled'.tr(), + TaskStatus.paused => 'download_paused'.tr(), + TaskStatus.enqueued => 'download_enqueue'.tr(), + TaskStatus.notFound => 'download_notfound'.tr(), + TaskStatus.waitingToRetry => 'download_waiting_to_retry'.tr(), + }; return SizedBox( key: const ValueKey('download_progress'), width: context.width - 32, child: Card( clipBehavior: Clip.antiAlias, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(16), - ), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16))), child: ListTile( minVerticalPadding: 18, leading: const Icon(Icons.video_file_outlined), - title: Text( - getStatusText(), - style: context.textTheme.labelLarge, - ), + title: Text(getStatusText(), style: context.textTheme.labelLarge), trailing: IconButton( icon: Icon(Icons.close, color: context.colorScheme.onError), onPressed: onCancelDownload, - style: ElevatedButton.styleFrom( - backgroundColor: context.colorScheme.error.withAlpha(200), - ), + style: ElevatedButton.styleFrom(backgroundColor: context.colorScheme.error.withAlpha(200)), ), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - fileName, - style: context.textTheme.labelMedium, - ), + Text(fileName, style: context.textTheme.labelMedium), Row( children: [ Expanded( child: LinearProgressIndicator( minHeight: 8.0, value: progress, - borderRadius: - const BorderRadius.all(Radius.circular(10.0)), + borderRadius: const BorderRadius.all(Radius.circular(10.0)), ), ), const SizedBox(width: 8), - Text( - '$progressPercent%', - style: context.textTheme.labelSmall, - ), + Text('$progressPercent%', style: context.textTheme.labelSmall), ], ), ], diff --git a/mobile/lib/pages/common/gallery_stacked_children.dart b/mobile/lib/pages/common/gallery_stacked_children.dart index eafc325049..7145bc2553 100644 --- a/mobile/lib/pages/common/gallery_stacked_children.dart +++ b/mobile/lib/pages/common/gallery_stacked_children.dart @@ -36,11 +36,7 @@ class GalleryStackedChildren extends HookConsumerWidget { shrinkWrap: true, scrollDirection: Axis.horizontal, itemCount: stackElements.length, - padding: const EdgeInsets.only( - left: 5, - right: 5, - bottom: 30, - ), + padding: const EdgeInsets.only(left: 5, right: 5, bottom: 30), itemBuilder: (context, index) { final currentAsset = stackElements.elementAt(index); final assetId = currentAsset.remoteId; @@ -63,9 +59,7 @@ class GalleryStackedChildren extends HookConsumerWidget { ? const BoxDecoration( color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(6)), - border: Border.fromBorderSide( - BorderSide(color: Colors.white, width: 2), - ), + border: Border.fromBorderSide(BorderSide(color: Colors.white, width: 2)), ) : const BoxDecoration( color: Colors.white, diff --git a/mobile/lib/pages/common/gallery_viewer.page.dart b/mobile/lib/pages/common/gallery_viewer.page.dart index 539406365a..3c279dfcd2 100644 --- a/mobile/lib/pages/common/gallery_viewer.page.dart +++ b/mobile/lib/pages/common/gallery_viewer.page.dart @@ -87,11 +87,7 @@ class GalleryViewerPage extends HookConsumerWidget { if (index < totalAssets.value && index >= 0) { final asset = loadAsset(index); await precacheImage( - ImmichImage.imageProvider( - asset: asset, - width: context.width, - height: context.height, - ), + ImmichImage.imageProvider(asset: asset, width: context.width, height: context.height), context, onError: onError, ); @@ -103,23 +99,20 @@ class GalleryViewerPage extends HookConsumerWidget { } } - useEffect( - () { - if (ref.read(showControlsProvider)) { - SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); - } else { - SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive); - } + useEffect(() { + if (ref.read(showControlsProvider)) { + SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); + } else { + SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive); + } - // Delay this a bit so we can finish loading the page - Timer(const Duration(milliseconds: 400), () { - precacheNextImage(currentIndex.value + 1); - }); + // Delay this a bit so we can finish loading the page + Timer(const Duration(milliseconds: 400), () { + precacheNextImage(currentIndex.value + 1); + }); - return null; - }, - const [], - ); + return null; + }, const []); useEffect(() { final asset = loadAsset(currentIndex.value); @@ -136,9 +129,7 @@ class GalleryViewerPage extends HookConsumerWidget { duration: const Duration(seconds: 1), content: Text( "local_asset_cast_failed".tr(), - style: context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), ), ), ); @@ -147,9 +138,7 @@ class GalleryViewerPage extends HookConsumerWidget { } } return null; - }, [ - ref.watch(castProvider).isCasting, - ]); + }, [ref.watch(castProvider).isCasting]); void showInfo() { final asset = ref.read(currentAssetProvider); @@ -157,9 +146,7 @@ class GalleryViewerPage extends HookConsumerWidget { return; } showModalBottomSheet( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(15.0)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(15.0))), barrierColor: Colors.transparent, isScrollControlled: true, showDragHandle: true, @@ -174,20 +161,10 @@ class GalleryViewerPage extends HookConsumerWidget { expand: false, builder: (context, scrollController) { return Padding( - padding: EdgeInsets.only( - bottom: context.viewInsets.bottom, - ), - child: ref.watch(appSettingsServiceProvider).getSetting( - AppSettingsEnum.advancedTroubleshooting, - ) - ? AdvancedBottomSheet( - assetDetail: asset, - scrollController: scrollController, - ) - : DetailPanel( - asset: asset, - scrollController: scrollController, - ), + padding: EdgeInsets.only(bottom: context.viewInsets.bottom), + child: ref.watch(appSettingsServiceProvider).getSetting(AppSettingsEnum.advancedTroubleshooting) + ? AdvancedBottomSheet(assetDetail: asset, scrollController: scrollController) + : DetailPanel(asset: asset, scrollController: scrollController), ); }, ); @@ -258,17 +235,13 @@ class GalleryViewerPage extends HookConsumerWidget { tightMode: true, initialScale: PhotoViewComputedScale.contained * 0.99, minScale: PhotoViewComputedScale.contained * 0.99, - errorBuilder: (context, error, stackTrace) => ImmichImage( - asset, - fit: BoxFit.contain, - ), + errorBuilder: (context, error, stackTrace) => ImmichImage(asset, fit: BoxFit.contain), ); } PhotoViewGalleryPageOptions buildVideo(BuildContext context, Asset asset) { return PhotoViewGalleryPageOptions.customChild( - onDragStart: (_, details, __, ___) => - localPosition.value = details.localPosition, + onDragStart: (_, details, __, ___) => localPosition.value = details.localPosition, onDragUpdate: (_, details, __) => handleSwipeUpDown(details), heroAttributes: _getHeroAttributes(asset), filterQuality: FilterQuality.high, @@ -284,11 +257,7 @@ class GalleryViewerPage extends HookConsumerWidget { asset: asset, image: Image( key: ValueKey(asset), - image: ImmichImage.imageProvider( - asset: asset, - width: context.width, - height: context.height, - ), + image: ImmichImage.imageProvider(asset: asset, width: context.width, height: context.height), fit: BoxFit.contain, height: context.height, width: context.width, @@ -304,8 +273,7 @@ class GalleryViewerPage extends HookConsumerWidget { final stackId = newAsset.stackId; if (stackId != null && currentIndex.value == index) { - final stackElements = - ref.read(assetStackStateProvider(newAsset.stackId!)); + final stackElements = ref.read(assetStackStateProvider(newAsset.stackId!)); if (stackIndex.value < stackElements.length) { newAsset = stackElements.elementAt(stackIndex.value); } @@ -319,8 +287,7 @@ class GalleryViewerPage extends HookConsumerWidget { return PopScope( // Change immersive mode back to normal "edgeToEdge" mode - onPopInvokedWithResult: (didPop, _) => - SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge), + onPopInvokedWithResult: (didPop, _) => SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge), child: Scaffold( backgroundColor: Colors.black, body: Stack( @@ -335,8 +302,7 @@ class GalleryViewerPage extends HookConsumerWidget { if (asset.isImage && !ref.read(isPlayingMotionVideoProvider)) { isZoomed.value = state != PhotoViewScaleState.initial; - ref.read(showControlsProvider.notifier).show = - !isZoomed.value; + ref.read(showControlsProvider.notifier).show = !isZoomed.value; } }, gaplessPlayback: true, @@ -346,17 +312,8 @@ class GalleryViewerPage extends HookConsumerWidget { child: Stack( fit: StackFit.expand, children: [ - BackdropFilter( - filter: ui.ImageFilter.blur( - sigmaX: 10, - sigmaY: 10, - ), - ), - ImmichThumbnail( - key: ValueKey(asset), - asset: asset, - fit: BoxFit.contain, - ), + BackdropFilter(filter: ui.ImageFilter.blur(sigmaX: 10, sigmaY: 10)), + ImmichThumbnail(key: ValueKey(asset), asset: asset, fit: BoxFit.contain), ], ), ); @@ -365,9 +322,9 @@ class GalleryViewerPage extends HookConsumerWidget { scrollPhysics: isZoomed.value ? const NeverScrollableScrollPhysics() // Don't allow paging while scrolled in : (Platform.isIOS - ? const FastScrollPhysics() // Use bouncing physics for iOS - : const FastClampingScrollPhysics() // Use heavy physics for Android - ), + ? const FastScrollPhysics() // Use bouncing physics for iOS + : const FastClampingScrollPhysics() // Use heavy physics for Android + ), itemCount: totalAssets.value, scrollDirection: Axis.horizontal, onPageChanged: (value, _) { @@ -405,9 +362,7 @@ class GalleryViewerPage extends HookConsumerWidget { duration: const Duration(seconds: 2), content: Text( "local_asset_cast_failed".tr(), - style: context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), ), ), ); @@ -420,10 +375,7 @@ class GalleryViewerPage extends HookConsumerWidget { top: 0, left: 0, right: 0, - child: GalleryAppBar( - key: const ValueKey('app-bar'), - showInfo: showInfo, - ), + child: GalleryAppBar(key: const ValueKey('app-bar'), showInfo: showInfo), ), Positioned( bottom: 0, @@ -454,9 +406,7 @@ class GalleryViewerPage extends HookConsumerWidget { @pragma('vm:prefer-inline') PhotoViewHeroAttributes _getHeroAttributes(Asset asset) { return PhotoViewHeroAttributes( - tag: asset.isInDb - ? asset.id + heroOffset - : '${asset.remoteId}-$heroOffset', + tag: asset.isInDb ? asset.id + heroOffset : '${asset.remoteId}-$heroOffset', transitionOnUserGestures: true, ); } diff --git a/mobile/lib/pages/common/headers_settings.page.dart b/mobile/lib/pages/common/headers_settings.page.dart index 0f4ab882c8..4cf683b4d9 100644 --- a/mobile/lib/pages/common/headers_settings.page.dart +++ b/mobile/lib/pages/common/headers_settings.page.dart @@ -79,10 +79,8 @@ class HeaderSettingsPage extends HookConsumerWidget { padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 16.0), itemCount: list.length, itemBuilder: (ctx, index) => list[index], - separatorBuilder: (context, index) => const Padding( - padding: EdgeInsets.only(bottom: 16.0, left: 8, right: 8), - child: Divider(), - ), + separatorBuilder: (context, index) => + const Padding(padding: EdgeInsets.only(bottom: 16.0, left: 8, right: 8), child: Divider()), ), ), ); @@ -109,12 +107,9 @@ class HeaderKeyValueSettings extends StatelessWidget { final SettingsHeader header; final Function() onRemove; - HeaderKeyValueSettings({ - super.key, - required this.header, - required this.onRemove, - }) : keyController = TextEditingController(text: header.key), - valueController = TextEditingController(text: header.value); + HeaderKeyValueSettings({super.key, required this.header, required this.onRemove}) + : keyController = TextEditingController(text: header.key), + valueController = TextEditingController(text: header.value); String? emptyFieldValidator(String? value) { if (value == null || value.isEmpty) { @@ -150,9 +145,7 @@ class HeaderKeyValueSettings extends StatelessWidget { Padding( padding: const EdgeInsets.only(left: 8), child: IconButton( - style: ElevatedButton.styleFrom( - padding: const EdgeInsets.symmetric(vertical: 12), - ), + style: ElevatedButton.styleFrom(padding: const EdgeInsets.symmetric(vertical: 12)), color: Colors.red[400], onPressed: onRemove, icon: const Icon(Icons.delete_outline), diff --git a/mobile/lib/pages/common/large_leading_tile.dart b/mobile/lib/pages/common/large_leading_tile.dart index 4f22a5f2b2..4563834473 100644 --- a/mobile/lib/pages/common/large_leading_tile.dart +++ b/mobile/lib/pages/common/large_leading_tile.dart @@ -8,10 +8,7 @@ class LargeLeadingTile extends StatelessWidget { required this.onTap, required this.title, this.subtitle, - this.leadingPadding = const EdgeInsets.symmetric( - vertical: 8, - horizontal: 16.0, - ), + this.leadingPadding = const EdgeInsets.symmetric(vertical: 8, horizontal: 16.0), this.borderRadius = 20.0, this.trailing, this.selected = false, @@ -40,26 +37,19 @@ class LargeLeadingTile extends StatelessWidget { child: Container( decoration: BoxDecoration( color: selected - ? selectedTileColor ?? - Theme.of(context).primaryColor.withAlpha(30) + ? selectedTileColor ?? Theme.of(context).primaryColor.withAlpha(30) : tileColor ?? Colors.transparent, borderRadius: BorderRadius.circular(borderRadius), ), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ - Padding( - padding: leadingPadding, - child: leading, - ), + Padding(padding: leadingPadding, child: leading), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - SizedBox( - width: context.width * 0.6, - child: title, - ), + SizedBox(width: context.width * 0.6, child: title), subtitle ?? const SizedBox.shrink(), ], ), diff --git a/mobile/lib/pages/common/native_video_viewer.page.dart b/mobile/lib/pages/common/native_video_viewer.page.dart index 8afa6ab4e3..d8b6db2276 100644 --- a/mobile/lib/pages/common/native_video_viewer.page.dart +++ b/mobile/lib/pages/common/native_video_viewer.page.dart @@ -63,6 +63,8 @@ class NativeVideoViewerPage extends HookConsumerWidget { final isCasting = ref.watch(castProvider.select((c) => c.isCasting)); + final isVideoReady = useState(false); + Future createSource() async { if (!context.mounted) { return null; @@ -76,10 +78,7 @@ class NativeVideoViewerPage extends HookConsumerWidget { throw Exception('No file found for the video'); } - final source = await VideoSource.init( - path: file.path, - type: VideoSourceType.file, - ); + final source = await VideoSource.init(path: file.path, type: VideoSourceType.file); return source; } @@ -88,8 +87,7 @@ class NativeVideoViewerPage extends HookConsumerWidget { final isOriginalVideo = ref .read(appSettingsServiceProvider) .getSetting(AppSettingsEnum.loadOriginalVideo); - final String postfixUrl = - isOriginalVideo ? 'original' : 'video/playback'; + final String postfixUrl = isOriginalVideo ? 'original' : 'video/playback'; final String videoUrl = asset.livePhotoVideoId != null ? '$serverEndpoint/assets/${asset.livePhotoVideoId}/$postfixUrl' : '$serverEndpoint/assets/${asset.remoteId}/$postfixUrl'; @@ -101,31 +99,24 @@ class NativeVideoViewerPage extends HookConsumerWidget { ); return source; } catch (error) { - log.severe( - 'Error creating video source for asset ${asset.fileName}: $error', - ); + log.severe('Error creating video source for asset ${asset.fileName}: $error'); return null; } } final videoSource = useMemoized>(() => createSource()); final aspectRatio = useState(asset.aspectRatio); - useMemoized( - () async { - if (!context.mounted || aspectRatio.value != null) { - return null; - } + useMemoized(() async { + if (!context.mounted || aspectRatio.value != null) { + return null; + } - try { - aspectRatio.value = - await ref.read(assetServiceProvider).getAspectRatio(asset); - } catch (error) { - log.severe( - 'Error getting aspect ratio for asset ${asset.fileName}: $error', - ); - } - }, - ); + try { + aspectRatio.value = await ref.read(assetServiceProvider).getAspectRatio(asset); + } catch (error) { + log.severe('Error getting aspect ratio for asset ${asset.fileName}: $error'); + } + }); void checkIfBuffering() { if (!context.mounted) { @@ -133,11 +124,11 @@ class NativeVideoViewerPage extends HookConsumerWidget { } final videoPlayback = ref.read(videoPlaybackValueProvider); - if ((isBuffering.value || - videoPlayback.state == VideoPlaybackState.initializing) && + if ((isBuffering.value || videoPlayback.state == VideoPlaybackState.initializing) && videoPlayback.state != VideoPlaybackState.buffering) { - ref.read(videoPlaybackValueProvider.notifier).value = - videoPlayback.copyWith(state: VideoPlaybackState.buffering); + ref.read(videoPlaybackValueProvider.notifier).value = videoPlayback.copyWith( + state: VideoPlaybackState.buffering, + ); } } @@ -193,10 +184,11 @@ class NativeVideoViewerPage extends HookConsumerWidget { return; } - final videoPlayback = - VideoPlaybackValue.fromNativeController(videoController); + final videoPlayback = VideoPlaybackValue.fromNativeController(videoController); ref.read(videoPlaybackValueProvider.notifier).value = videoPlayback; + isVideoReady.value = true; + try { await videoController.play(); await videoController.setVolume(0.9); @@ -211,8 +203,7 @@ class NativeVideoViewerPage extends HookConsumerWidget { return; } - final videoPlayback = - VideoPlaybackValue.fromNativeController(videoController); + final videoPlayback = VideoPlaybackValue.fromNativeController(videoController); if (videoPlayback.state == VideoPlaybackState.playing) { // Sync with the controls playing WakelockPlus.enable(); @@ -221,8 +212,7 @@ class NativeVideoViewerPage extends HookConsumerWidget { WakelockPlus.disable(); } - ref.read(videoPlaybackValueProvider.notifier).status = - videoPlayback.state; + ref.read(videoPlaybackValueProvider.notifier).status = videoPlayback.state; } void onPlaybackPositionChanged() { @@ -241,8 +231,7 @@ class NativeVideoViewerPage extends HookConsumerWidget { return; } - ref.read(videoPlaybackValueProvider.notifier).position = - Duration(seconds: playbackInfo.position); + ref.read(videoPlaybackValueProvider.notifier).position = Duration(seconds: playbackInfo.position); // Check if the video is buffering if (playbackInfo.status == PlaybackStatus.playing) { @@ -261,18 +250,14 @@ class NativeVideoViewerPage extends HookConsumerWidget { } if (videoController.playbackInfo?.status == PlaybackStatus.stopped && - !ref - .read(appSettingsServiceProvider) - .getSetting(AppSettingsEnum.loopVideo)) { + !ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.loopVideo)) { ref.read(isPlayingMotionVideoProvider.notifier).playing = false; } } void removeListeners(NativeVideoPlayerController controller) { - controller.onPlaybackPositionChanged - .removeListener(onPlaybackPositionChanged); - controller.onPlaybackStatusChanged - .removeListener(onPlaybackStatusChanged); + controller.onPlaybackPositionChanged.removeListener(onPlaybackPositionChanged); + controller.onPlaybackStatusChanged.removeListener(onPlaybackStatusChanged); controller.onPlaybackReady.removeListener(onPlaybackReady); controller.onPlaybackEnded.removeListener(onPlaybackEnded); } @@ -297,9 +282,7 @@ class NativeVideoViewerPage extends HookConsumerWidget { nc.loadVideoSource(source).catchError((error) { log.severe('Error loading video source: $error'); }); - final loopVideo = ref - .read(appSettingsServiceProvider) - .getSetting(AppSettingsEnum.loopVideo); + final loopVideo = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.loopVideo); nc.setLoop(loopVideo); controller.value = nc; @@ -332,48 +315,42 @@ class NativeVideoViewerPage extends HookConsumerWidget { // This delay seems like a hacky way to resolve underlying bugs in video // playback, but other resolutions failed thus far Timer( - Platform.isIOS - ? Duration(milliseconds: 300 * playbackDelayFactor) - : imageToVideo - ? Duration(milliseconds: 200 * playbackDelayFactor) - : Duration(milliseconds: 400 * playbackDelayFactor), () { - if (!context.mounted) { - return; - } - - currentAsset.value = value; - if (currentAsset.value == asset) { - onPlaybackReady(); - } - }); - }); - - useEffect( - () { - // If opening a remote video from a hero animation, delay visibility to avoid a stutter - final timer = isVisible.value - ? null - : Timer( - const Duration(milliseconds: 300), - () => isVisible.value = true, - ); - - return () { - timer?.cancel(); - final playerController = controller.value; - if (playerController == null) { + Platform.isIOS + ? Duration(milliseconds: 300 * playbackDelayFactor) + : imageToVideo + ? Duration(milliseconds: 200 * playbackDelayFactor) + : Duration(milliseconds: 400 * playbackDelayFactor), + () { + if (!context.mounted) { return; } - removeListeners(playerController); - playerController.stop().catchError((error) { - log.fine('Error stopping video: $error'); - }); - WakelockPlus.disable(); - }; - }, - const [], - ); + currentAsset.value = value; + if (currentAsset.value == asset) { + onPlaybackReady(); + } + }, + ); + }); + + useEffect(() { + // If opening a remote video from a hero animation, delay visibility to avoid a stutter + final timer = isVisible.value ? null : Timer(const Duration(milliseconds: 300), () => isVisible.value = true); + + return () { + timer?.cancel(); + final playerController = controller.value; + if (playerController == null) { + return; + } + removeListeners(playerController); + playerController.stop().catchError((error) { + log.fine('Error stopping video: $error'); + }); + + WakelockPlus.disable(); + }; + }, const []); useOnAppLifecycleStateChange((_, state) async { if (state == AppLifecycleState.resumed && shouldPlayOnForeground.value) { @@ -393,7 +370,7 @@ class NativeVideoViewerPage extends HookConsumerWidget { children: [ // This remains under the video to avoid flickering // For motion videos, this is the image portion of the asset - Center(key: ValueKey(asset.id), child: image), + if (!isVideoReady.value || asset.isMotionPhoto) Center(key: ValueKey(asset.id), child: image), if (aspectRatio.value != null && !isCasting) Visibility.maintain( key: ValueKey(asset), @@ -403,12 +380,7 @@ class NativeVideoViewerPage extends HookConsumerWidget { child: AspectRatio( key: ValueKey(asset), aspectRatio: aspectRatio.value!, - child: isCurrent - ? NativeVideoPlayerView( - key: ValueKey(asset), - onViewReady: initController, - ) - : null, + child: isCurrent ? NativeVideoPlayerView(key: ValueKey(asset), onViewReady: initController) : null, ), ), ), diff --git a/mobile/lib/pages/common/settings.page.dart b/mobile/lib/pages/common/settings.page.dart index e45001270c..7bc8cd2b3a 100644 --- a/mobile/lib/pages/common/settings.page.dart +++ b/mobile/lib/pages/common/settings.page.dart @@ -1,75 +1,51 @@ import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:flutter_hooks/flutter_hooks.dart' hide Store; +import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/widgets/settings/advanced_settings.dart'; import 'package:immich_mobile/widgets/settings/asset_list_settings/asset_list_settings.dart'; import 'package:immich_mobile/widgets/settings/asset_viewer_settings/asset_viewer_settings.dart'; import 'package:immich_mobile/widgets/settings/backup_settings/backup_settings.dart'; +import 'package:immich_mobile/widgets/settings/backup_settings/drift_backup_settings.dart'; +import 'package:immich_mobile/widgets/settings/beta_sync_settings/beta_sync_settings.dart'; import 'package:immich_mobile/widgets/settings/beta_timeline_list_tile.dart'; import 'package:immich_mobile/widgets/settings/language_settings.dart'; import 'package:immich_mobile/widgets/settings/networking_settings/networking_settings.dart'; import 'package:immich_mobile/widgets/settings/notification_setting.dart'; import 'package:immich_mobile/widgets/settings/preference_settings/preference_setting.dart'; +import 'package:immich_mobile/widgets/settings/settings_card.dart'; enum SettingSection { - advanced( - 'advanced', - Icons.build_outlined, - "advanced_settings_tile_subtitle", - ), - assetViewer( - 'asset_viewer_settings_title', - Icons.image_outlined, - "asset_viewer_settings_subtitle", - ), - backup( - 'backup_controller_page_backup', - Icons.cloud_upload_outlined, - "backup_setting_subtitle", - ), - languages( - 'language', - Icons.language, - "setting_languages_subtitle", - ), - networking( - 'networking_settings', - Icons.wifi, - "networking_subtitle", - ), - notifications( - 'notifications', - Icons.notifications_none_rounded, - "setting_notifications_subtitle", - ), - preferences( - 'preferences_settings_title', - Icons.interests_outlined, - "preferences_settings_subtitle", - ), - timeline( - 'asset_list_settings_title', - Icons.auto_awesome_mosaic_outlined, - "asset_list_settings_subtitle", - ); + beta('beta_sync', Icons.sync_outlined, "beta_sync_subtitle"), + advanced('advanced', Icons.build_outlined, "advanced_settings_tile_subtitle"), + assetViewer('asset_viewer_settings_title', Icons.image_outlined, "asset_viewer_settings_subtitle"), + backup('backup', Icons.cloud_upload_outlined, "backup_settings_subtitle"), + languages('language', Icons.language, "setting_languages_subtitle"), + networking('networking_settings', Icons.wifi, "networking_subtitle"), + notifications('notifications', Icons.notifications_none_rounded, "setting_notifications_subtitle"), + preferences('preferences_settings_title', Icons.interests_outlined, "preferences_settings_subtitle"), + timeline('asset_list_settings_title', Icons.auto_awesome_mosaic_outlined, "asset_list_settings_subtitle"); final String title; final String subtitle; final IconData icon; Widget get widget => switch (this) { - SettingSection.advanced => const AdvancedSettings(), - SettingSection.assetViewer => const AssetViewerSettings(), - SettingSection.backup => const BackupSettings(), - SettingSection.languages => const LanguageSettings(), - SettingSection.networking => const NetworkingSettings(), - SettingSection.notifications => const NotificationSetting(), - SettingSection.preferences => const PreferenceSetting(), - SettingSection.timeline => const AssetListSettings(), - }; + SettingSection.beta => const _BetaLandscapeToggle(), + SettingSection.advanced => const AdvancedSettings(), + SettingSection.assetViewer => const AssetViewerSettings(), + SettingSection.backup => + Store.tryGet(StoreKey.betaTimeline) ?? false ? const DriftBackupSettings() : const BackupSettings(), + SettingSection.languages => const LanguageSettings(), + SettingSection.networking => const NetworkingSettings(), + SettingSection.notifications => const NotificationSetting(), + SettingSection.preferences => const PreferenceSetting(), + SettingSection.timeline => const AssetListSettings(), + }; const SettingSection(this.title, this.icon, this.subtitle); } @@ -82,10 +58,7 @@ class SettingsPage extends StatelessWidget { Widget build(BuildContext context) { context.locale; return Scaffold( - appBar: AppBar( - centerTitle: false, - title: const Text('settings').tr(), - ), + appBar: AppBar(centerTitle: false, title: const Text('settings').tr()), body: context.isMobile ? const _MobileLayout() : const _TabletLayout(), ); } @@ -96,58 +69,32 @@ class _MobileLayout extends StatelessWidget { @override Widget build(BuildContext context) { final List settings = SettingSection.values - .map( - (setting) => Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16.0, - ), - child: Card( - elevation: 0, - clipBehavior: Clip.antiAlias, - color: context.colorScheme.surfaceContainer, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(16)), - ), - margin: const EdgeInsets.symmetric(vertical: 4.0), - child: ListTile( - contentPadding: const EdgeInsets.symmetric( - horizontal: 16.0, - ), - leading: Container( - decoration: BoxDecoration( - borderRadius: const BorderRadius.all(Radius.circular(16)), - color: context.isDarkTheme - ? Colors.black26 - : Colors.white.withAlpha(100), + .expand( + (setting) => setting == SettingSection.beta + ? [ + const BetaTimelineListTile(), + if (Store.isBetaTimelineEnabled) + SettingsCard( + icon: Icons.sync_outlined, + title: 'beta_sync'.tr(), + subtitle: 'beta_sync_subtitle'.tr(), + settingRoute: const BetaSyncSettingsRoute(), + ), + ] + : [ + SettingsCard( + title: setting.title.tr(), + subtitle: setting.subtitle.tr(), + icon: setting.icon, + settingRoute: SettingsSubRoute(section: setting), ), - padding: const EdgeInsets.all(16.0), - child: Icon(setting.icon, color: context.primaryColor), - ), - title: Text( - setting.title, - style: context.textTheme.titleMedium!.copyWith( - fontWeight: FontWeight.w600, - color: context.primaryColor, - ), - ).tr(), - subtitle: Text( - setting.subtitle, - style: context.textTheme.labelLarge, - ).tr(), - onTap: () => - context.pushRoute(SettingsSubRoute(section: setting)), - ), - ), - ), + ], ) .toList(); return ListView( physics: const ClampingScrollPhysics(), padding: const EdgeInsets.only(top: 10.0, bottom: 56), - children: [ - const BetaTimelineListTile(), - ...settings, - ], + children: [...settings], ); } } @@ -156,8 +103,7 @@ class _TabletLayout extends HookWidget { const _TabletLayout(); @override Widget build(BuildContext context) { - final selectedSection = - useState(SettingSection.values.first); + final selectedSection = useState(SettingSection.values.first); return Row( mainAxisAlignment: MainAxisAlignment.start, @@ -165,27 +111,39 @@ class _TabletLayout extends HookWidget { Expanded( flex: 2, child: CustomScrollView( - slivers: SettingSection.values - .map( - (s) => SliverToBoxAdapter( - child: ListTile( - title: Text(s.title).tr(), - leading: Icon(s.icon), - selected: s.index == selectedSection.value.index, - selectedColor: context.primaryColor, - selectedTileColor: context.themeData.highlightColor, - onTap: () => selectedSection.value = s, - ), + slivers: [ + ...SettingSection.values.map( + (s) => SliverToBoxAdapter( + child: ListTile( + title: Text(s.title).tr(), + leading: Icon(s.icon), + selected: s.index == selectedSection.value.index, + selectedColor: context.primaryColor, + selectedTileColor: context.themeData.highlightColor, + onTap: () => selectedSection.value = s, ), - ) - .toList(), + ), + ), + ], ), ), const VerticalDivider(width: 1), - Expanded( - flex: 4, - child: selectedSection.value.widget, - ), + Expanded(flex: 4, child: selectedSection.value.widget), + ], + ); + } +} + +class _BetaLandscapeToggle extends HookWidget { + const _BetaLandscapeToggle(); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + const SizedBox(height: 100, child: BetaTimelineListTile()), + if (Store.isBetaTimelineEnabled) const Expanded(child: BetaSyncSettings()), ], ); } @@ -201,10 +159,7 @@ class SettingsSubPage extends StatelessWidget { Widget build(BuildContext context) { context.locale; return Scaffold( - appBar: AppBar( - centerTitle: false, - title: Text(section.title).tr(), - ), + appBar: AppBar(centerTitle: false, title: Text(section.title).tr()), body: section.widget, ); } diff --git a/mobile/lib/pages/common/splash_screen.page.dart b/mobile/lib/pages/common/splash_screen.page.dart index 598e920651..2bda4f90f9 100644 --- a/mobile/lib/pages/common/splash_screen.page.dart +++ b/mobile/lib/pages/common/splash_screen.page.dart @@ -42,50 +42,34 @@ class SplashScreenPageState extends ConsumerState { final endpoint = Store.tryGet(StoreKey.serverEndpoint); final accessToken = Store.tryGet(StoreKey.accessToken); - bool isAuthSuccess = false; - if (accessToken != null && serverUrl != null && endpoint != null) { - try { - isAuthSuccess = await ref.read(authProvider.notifier).saveAuthInfo( - accessToken: accessToken, - ); - } catch (error, stackTrace) { - log.severe( - 'Cannot set success login info', - error, - stackTrace, - ); - } + ref + .read(authProvider.notifier) + .saveAuthInfo(accessToken: accessToken) + .then( + (a) => {log.info('Successfully updated auth info with access token: $accessToken')}, + onError: (exception) => { + log.severe('Failed to update auth info with access token: $accessToken'), + ref.read(authProvider.notifier).logout(), + context.replaceRoute(const LoginRoute()), + }, + ); } else { - isAuthSuccess = false; - log.severe( - 'Missing authentication, server, or endpoint info from the local store', - ); - } - - if (!isAuthSuccess) { - log.severe( - 'Unable to login using offline or online methods - Logging out completely', - ); + log.severe('Missing crucial offline login info - Logging out completely'); ref.read(authProvider.notifier).logout(); context.replaceRoute(const LoginRoute()); return; } if (context.router.current.name == SplashScreenRoute.name) { - context.replaceRoute( - Store.isBetaTimelineEnabled - ? const TabShellRoute() - : const TabControllerRoute(), - ); + context.replaceRoute(Store.isBetaTimelineEnabled ? const TabShellRoute() : const TabControllerRoute()); } if (Store.isBetaTimelineEnabled) { return; } - final hasPermission = - await ref.read(galleryPermissionNotifier.notifier).hasPermission; + final hasPermission = await ref.read(galleryPermissionNotifier.notifier).hasPermission; if (hasPermission) { // Resume backup (if enable) then navigate ref.watch(backupProvider.notifier).resumeBackup(); @@ -96,11 +80,7 @@ class SplashScreenPageState extends ConsumerState { Widget build(BuildContext context) { return const Scaffold( body: Center( - child: Image( - image: AssetImage('assets/immich-logo.png'), - width: 80, - filterQuality: FilterQuality.high, - ), + child: Image(image: AssetImage('assets/immich-logo.png'), width: 80, filterQuality: FilterQuality.high), ), ); } diff --git a/mobile/lib/pages/common/tab_controller.page.dart b/mobile/lib/pages/common/tab_controller.page.dart index e713b3f8da..ef637ba1c8 100644 --- a/mobile/lib/pages/common/tab_controller.page.dart +++ b/mobile/lib/pages/common/tab_controller.page.dart @@ -20,8 +20,7 @@ class TabControllerPage extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final isRefreshingAssets = ref.watch(assetProvider); final isRefreshingRemoteAlbums = ref.watch(isRefreshingRemoteAlbumProvider); - final isScreenLandscape = - MediaQuery.orientationOf(context) == Orientation.landscape; + final isScreenLandscape = MediaQuery.orientationOf(context) == Orientation.landscape; Widget buildIcon({required Widget icon, required bool isProcessing}) { if (!isProcessing) return icon; @@ -37,9 +36,7 @@ class TabControllerPage extends HookConsumerWidget { width: 20, child: CircularProgressIndicator( strokeWidth: 2, - valueColor: AlwaysStoppedAnimation( - context.primaryColor, - ), + valueColor: AlwaysStoppedAnimation(context.primaryColor), ), ), ), @@ -66,51 +63,31 @@ class TabControllerPage extends HookConsumerWidget { final navigationDestinations = [ NavigationDestination( label: 'photos'.tr(), - icon: const Icon( - Icons.photo_library_outlined, - ), + icon: const Icon(Icons.photo_library_outlined), selectedIcon: buildIcon( isProcessing: isRefreshingAssets, - icon: Icon( - Icons.photo_library, - color: context.primaryColor, - ), + icon: Icon(Icons.photo_library, color: context.primaryColor), ), ), NavigationDestination( label: 'search'.tr(), - icon: const Icon( - Icons.search_rounded, - ), - selectedIcon: Icon( - Icons.search, - color: context.primaryColor, - ), + icon: const Icon(Icons.search_rounded), + selectedIcon: Icon(Icons.search, color: context.primaryColor), ), NavigationDestination( label: 'albums'.tr(), - icon: const Icon( - Icons.photo_album_outlined, - ), + icon: const Icon(Icons.photo_album_outlined), selectedIcon: buildIcon( isProcessing: isRefreshingRemoteAlbums, - icon: Icon( - Icons.photo_album_rounded, - color: context.primaryColor, - ), + icon: Icon(Icons.photo_album_rounded, color: context.primaryColor), ), ), NavigationDestination( label: 'library'.tr(), - icon: const Icon( - Icons.space_dashboard_outlined, - ), + icon: const Icon(Icons.space_dashboard_outlined), selectedIcon: buildIcon( isProcessing: isRefreshingAssets, - icon: Icon( - Icons.space_dashboard_rounded, - color: context.primaryColor, - ), + icon: Icon(Icons.space_dashboard_rounded, color: context.primaryColor), ), ), ]; @@ -118,8 +95,7 @@ class TabControllerPage extends HookConsumerWidget { Widget bottomNavigationBar(TabsRouter tabsRouter) { return NavigationBar( selectedIndex: tabsRouter.activeIndex, - onDestinationSelected: (index) => - onNavigationSelected(tabsRouter, index), + onDestinationSelected: (index) => onNavigationSelected(tabsRouter, index), destinations: navigationDestinations, ); } @@ -127,16 +103,9 @@ class TabControllerPage extends HookConsumerWidget { Widget navigationRail(TabsRouter tabsRouter) { return NavigationRail( destinations: navigationDestinations - .map( - (e) => NavigationRailDestination( - icon: e.icon, - label: Text(e.label), - selectedIcon: e.selectedIcon, - ), - ) + .map((e) => NavigationRailDestination(icon: e.icon, label: Text(e.label), selectedIcon: e.selectedIcon)) .toList(), - onDestinationSelected: (index) => - onNavigationSelected(tabsRouter, index), + onDestinationSelected: (index) => onNavigationSelected(tabsRouter, index), selectedIndex: tabsRouter.activeIndex, labelType: NavigationRailLabelType.all, groupAlignment: 0.0, @@ -145,23 +114,14 @@ class TabControllerPage extends HookConsumerWidget { final multiselectEnabled = ref.watch(multiselectProvider); return AutoTabsRouter( - routes: [ - const PhotosRoute(), - SearchRoute(), - const AlbumsRoute(), - const LibraryRoute(), - ], + routes: [const PhotosRoute(), SearchRoute(), const AlbumsRoute(), const LibraryRoute()], duration: const Duration(milliseconds: 600), - transitionBuilder: (context, child, animation) => FadeTransition( - opacity: animation, - child: child, - ), + transitionBuilder: (context, child, animation) => FadeTransition(opacity: animation, child: child), builder: (context, child) { final tabsRouter = AutoTabsRouter.of(context); return PopScope( canPop: tabsRouter.activeIndex == 0, - onPopInvokedWithResult: (didPop, _) => - !didPop ? tabsRouter.setActiveIndex(0) : null, + onPopInvokedWithResult: (didPop, _) => !didPop ? tabsRouter.setActiveIndex(0) : null, child: Scaffold( resizeToAvoidBottomInset: false, body: isScreenLandscape @@ -173,9 +133,7 @@ class TabControllerPage extends HookConsumerWidget { ], ) : child, - bottomNavigationBar: multiselectEnabled || isScreenLandscape - ? null - : bottomNavigationBar(tabsRouter), + bottomNavigationBar: multiselectEnabled || isScreenLandscape ? null : bottomNavigationBar(tabsRouter), ), ); }, diff --git a/mobile/lib/pages/common/tab_shell.page.dart b/mobile/lib/pages/common/tab_shell.page.dart index 24efff143f..e06f7ca441 100644 --- a/mobile/lib/pages/common/tab_shell.page.dart +++ b/mobile/lib/pages/common/tab_shell.page.dart @@ -2,15 +2,20 @@ import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/timeline.model.dart'; +import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; -import 'package:immich_mobile/providers/asset_viewer/scroll_notifier.provider.dart'; +import 'package:immich_mobile/providers/app_settings.provider.dart'; +import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; import 'package:immich_mobile/providers/haptic_feedback.provider.dart'; import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; import 'package:immich_mobile/providers/search/search_input_focus.provider.dart'; import 'package:immich_mobile/providers/tab.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/providers/websocket.provider.dart'; import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/utils/migration.dart'; @RoutePage() @@ -25,9 +30,22 @@ class _TabShellPageState extends ConsumerState { @override void initState() { super.initState(); - WidgetsBinding.instance.addPostFrameCallback((_) { + + WidgetsBinding.instance.addPostFrameCallback((_) async { ref.read(websocketProvider.notifier).connect(); - runNewSync(ref, full: true); + + final isEnableBackup = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup); + + await runNewSync(ref, full: true).then((_) async { + if (isEnableBackup) { + final currentUser = ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + + await ref.read(driftBackupProvider.notifier).handleBackupResume(currentUser.id); + } + }); }); } @@ -35,95 +53,35 @@ class _TabShellPageState extends ConsumerState { Widget build(BuildContext context) { final isScreenLandscape = context.orientation == Orientation.landscape; - Widget buildIcon({required Widget icon, required bool isProcessing}) { - if (!isProcessing) return icon; - return Stack( - alignment: Alignment.center, - clipBehavior: Clip.none, - children: [ - icon, - Positioned( - right: -18, - child: SizedBox( - height: 20, - width: 20, - child: CircularProgressIndicator( - strokeWidth: 2, - valueColor: AlwaysStoppedAnimation( - context.primaryColor, - ), - ), - ), - ), - ], - ); - } - final navigationDestinations = [ NavigationDestination( label: 'photos'.tr(), - icon: const Icon( - Icons.photo_library_outlined, - ), - selectedIcon: buildIcon( - isProcessing: false, - icon: Icon( - Icons.photo_library, - color: context.primaryColor, - ), - ), + icon: const Icon(Icons.photo_library_outlined), + selectedIcon: Icon(Icons.photo_library, color: context.primaryColor), ), NavigationDestination( label: 'search'.tr(), - icon: const Icon( - Icons.search_rounded, - ), - selectedIcon: Icon( - Icons.search, - color: context.primaryColor, - ), + icon: const Icon(Icons.search_rounded), + selectedIcon: Icon(Icons.search, color: context.primaryColor), ), NavigationDestination( label: 'albums'.tr(), - icon: const Icon( - Icons.photo_album_outlined, - ), - selectedIcon: buildIcon( - isProcessing: false, - icon: Icon( - Icons.photo_album_rounded, - color: context.primaryColor, - ), - ), + icon: const Icon(Icons.photo_album_outlined), + selectedIcon: Icon(Icons.photo_album_rounded, color: context.primaryColor), ), NavigationDestination( label: 'library'.tr(), - icon: const Icon( - Icons.space_dashboard_outlined, - ), - selectedIcon: buildIcon( - isProcessing: false, - icon: Icon( - Icons.space_dashboard_rounded, - color: context.primaryColor, - ), - ), + icon: const Icon(Icons.space_dashboard_outlined), + selectedIcon: Icon(Icons.space_dashboard_rounded, color: context.primaryColor), ), ]; Widget navigationRail(TabsRouter tabsRouter) { return NavigationRail( destinations: navigationDestinations - .map( - (e) => NavigationRailDestination( - icon: e.icon, - label: Text(e.label), - selectedIcon: e.selectedIcon, - ), - ) + .map((e) => NavigationRailDestination(icon: e.icon, label: Text(e.label), selectedIcon: e.selectedIcon)) .toList(), - onDestinationSelected: (index) => - _onNavigationSelected(tabsRouter, index, ref), + onDestinationSelected: (index) => _onNavigationSelected(tabsRouter, index, ref), selectedIndex: tabsRouter.activeIndex, labelType: NavigationRailLabelType.all, groupAlignment: 0.0, @@ -131,23 +89,14 @@ class _TabShellPageState extends ConsumerState { } return AutoTabsRouter( - routes: [ - const MainTimelineRoute(), - DriftSearchRoute(), - const DriftAlbumsRoute(), - const DriftLibraryRoute(), - ], + routes: [const MainTimelineRoute(), DriftSearchRoute(), const DriftAlbumsRoute(), const DriftLibraryRoute()], duration: const Duration(milliseconds: 600), - transitionBuilder: (context, child, animation) => FadeTransition( - opacity: animation, - child: child, - ), + transitionBuilder: (context, child, animation) => FadeTransition(opacity: animation, child: child), builder: (context, child) { final tabsRouter = AutoTabsRouter.of(context); return PopScope( canPop: tabsRouter.activeIndex == 0, - onPopInvokedWithResult: (didPop, _) => - !didPop ? tabsRouter.setActiveIndex(0) : null, + onPopInvokedWithResult: (didPop, _) => !didPop ? tabsRouter.setActiveIndex(0) : null, child: Scaffold( resizeToAvoidBottomInset: false, body: isScreenLandscape @@ -159,10 +108,7 @@ class _TabShellPageState extends ConsumerState { ], ) : child, - bottomNavigationBar: _BottomNavigationBar( - tabsRouter: tabsRouter, - destinations: navigationDestinations, - ), + bottomNavigationBar: _BottomNavigationBar(tabsRouter: tabsRouter, destinations: navigationDestinations), ), ); }, @@ -173,7 +119,7 @@ class _TabShellPageState extends ConsumerState { void _onNavigationSelected(TabsRouter router, int index, WidgetRef ref) { // On Photos page menu tapped if (router.activeIndex == 0 && index == 0) { - scrollToTopNotifierProvider.scrollToTop(); + EventStream.shared.emit(const ScrollToTopEvent()); } // On Search page tapped @@ -183,7 +129,7 @@ void _onNavigationSelected(TabsRouter router, int index, WidgetRef ref) { // Album page if (index == 2) { - ref.read(remoteAlbumProvider.notifier).getAll(); + ref.read(remoteAlbumProvider.notifier).refresh(); } ref.read(hapticFeedbackProvider.notifier).selectionClick(); @@ -192,10 +138,7 @@ void _onNavigationSelected(TabsRouter router, int index, WidgetRef ref) { } class _BottomNavigationBar extends ConsumerWidget { - const _BottomNavigationBar({ - required this.tabsRouter, - required this.destinations, - }); + const _BottomNavigationBar({required this.tabsRouter, required this.destinations}); final List destinations; final TabsRouter tabsRouter; @@ -203,8 +146,7 @@ class _BottomNavigationBar extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final isScreenLandscape = context.orientation == Orientation.landscape; - final isMultiselectEnabled = - ref.watch(multiSelectProvider.select((s) => s.isEnabled)); + final isMultiselectEnabled = ref.watch(multiSelectProvider.select((s) => s.isEnabled)); if (isScreenLandscape || isMultiselectEnabled) { return const SizedBox.shrink(); @@ -212,8 +154,7 @@ class _BottomNavigationBar extends ConsumerWidget { return NavigationBar( selectedIndex: tabsRouter.activeIndex, - onDestinationSelected: (index) => - _onNavigationSelected(tabsRouter, index, ref), + onDestinationSelected: (index) => _onNavigationSelected(tabsRouter, index, ref), destinations: destinations, ); } diff --git a/mobile/lib/pages/editing/crop.page.dart b/mobile/lib/pages/editing/crop.page.dart index f7f459c770..35fd615800 100644 --- a/mobile/lib/pages/editing/crop.page.dart +++ b/mobile/lib/pages/editing/crop.page.dart @@ -32,20 +32,10 @@ class CropImagePage extends HookWidget { leading: CloseButton(color: context.primaryColor), actions: [ IconButton( - icon: Icon( - Icons.done_rounded, - color: context.primaryColor, - size: 24, - ), + icon: Icon(Icons.done_rounded, color: context.primaryColor, size: 24), onPressed: () async { final croppedImage = await cropController.croppedImage(); - context.pushRoute( - EditImageRoute( - asset: asset, - image: croppedImage, - isEdited: true, - ), - ); + context.pushRoute(EditImageRoute(asset: asset, image: croppedImage, isEdited: true)); }, ), ], @@ -60,11 +50,7 @@ class CropImagePage extends HookWidget { padding: const EdgeInsets.only(top: 20), width: constraints.maxWidth * 0.9, height: constraints.maxHeight * 0.6, - child: CropImage( - controller: cropController, - image: image, - gridColor: Colors.white, - ), + child: CropImage(controller: cropController, image: image, gridColor: Colors.white), ), Expanded( child: Container( @@ -81,28 +67,18 @@ class CropImagePage extends HookWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ Padding( - padding: const EdgeInsets.only( - left: 20, - right: 20, - bottom: 10, - ), + padding: const EdgeInsets.only(left: 20, right: 20, bottom: 10), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ IconButton( - icon: Icon( - Icons.rotate_left, - color: context.themeData.iconTheme.color, - ), + icon: Icon(Icons.rotate_left, color: context.themeData.iconTheme.color), onPressed: () { cropController.rotateLeft(); }, ), IconButton( - icon: Icon( - Icons.rotate_right, - color: context.themeData.iconTheme.color, - ), + icon: Icon(Icons.rotate_right, color: context.themeData.iconTheme.color), onPressed: () { cropController.rotateRight(); }, @@ -178,19 +154,14 @@ class _AspectRatioButton extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ IconButton( - icon: Icon( - switch (label) { - 'Free' => Icons.crop_free_rounded, - '1:1' => Icons.crop_square_rounded, - '16:9' => Icons.crop_16_9_rounded, - '3:2' => Icons.crop_3_2_rounded, - '7:5' => Icons.crop_7_5_rounded, - _ => Icons.crop_free_rounded, - }, - color: aspectRatio.value == ratio - ? context.primaryColor - : context.themeData.iconTheme.color, - ), + icon: Icon(switch (label) { + 'Free' => Icons.crop_free_rounded, + '1:1' => Icons.crop_square_rounded, + '16:9' => Icons.crop_16_9_rounded, + '3:2' => Icons.crop_3_2_rounded, + '7:5' => Icons.crop_7_5_rounded, + _ => Icons.crop_free_rounded, + }, color: aspectRatio.value == ratio ? context.primaryColor : context.themeData.iconTheme.color), onPressed: () { cropController.crop = const Rect.fromLTRB(0.1, 0.1, 0.9, 0.9); aspectRatio.value = ratio; diff --git a/mobile/lib/pages/editing/edit.page.dart b/mobile/lib/pages/editing/edit.page.dart index d37941f4fe..c9ab014456 100644 --- a/mobile/lib/pages/editing/edit.page.dart +++ b/mobile/lib/pages/editing/edit.page.dart @@ -29,54 +29,34 @@ class EditImagePage extends ConsumerWidget { final Image image; final bool isEdited; - const EditImagePage({ - super.key, - required this.asset, - required this.image, - required this.isEdited, - }); + const EditImagePage({super.key, required this.asset, required this.image, required this.isEdited}); Future _imageToUint8List(Image image) async { final Completer completer = Completer(); - image.image.resolve(const ImageConfiguration()).addListener( - ImageStreamListener( - (ImageInfo info, bool _) { - info.image - .toByteData(format: ImageByteFormat.png) - .then((byteData) { - if (byteData != null) { - completer.complete(byteData.buffer.asUint8List()); - } else { - completer.completeError('Failed to convert image to bytes'); - } - }); - }, - onError: (exception, stackTrace) => - completer.completeError(exception), - ), + image.image + .resolve(const ImageConfiguration()) + .addListener( + ImageStreamListener((ImageInfo info, bool _) { + info.image.toByteData(format: ImageByteFormat.png).then((byteData) { + if (byteData != null) { + completer.complete(byteData.buffer.asUint8List()); + } else { + completer.completeError('Failed to convert image to bytes'); + } + }); + }, onError: (exception, stackTrace) => completer.completeError(exception)), ); return completer.future; } - Future _saveEditedImage( - BuildContext context, - Asset asset, - Image image, - WidgetRef ref, - ) async { + Future _saveEditedImage(BuildContext context, Asset asset, Image image, WidgetRef ref) async { try { final Uint8List imageData = await _imageToUint8List(image); - await ref.read(fileMediaRepositoryProvider).saveImage( - imageData, - title: "${p.withoutExtension(asset.fileName)}_edited.jpg", - ); + await ref + .read(fileMediaRepositoryProvider) + .saveImage(imageData, title: "${p.withoutExtension(asset.fileName)}_edited.jpg"); await ref.read(albumProvider.notifier).refreshDeviceAlbums(); context.navigator.popUntil((route) => route.isFirst); - ImmichToast.show( - durationInSecond: 3, - context: context, - msg: 'Image Saved!', - gravity: ToastGravity.CENTER, - ); + ImmichToast.show(durationInSecond: 3, context: context, msg: 'Image Saved!', gravity: ToastGravity.CENTER); } catch (e) { ImmichToast.show( durationInSecond: 6, @@ -94,39 +74,23 @@ class EditImagePage extends ConsumerWidget { title: Text("edit".tr()), backgroundColor: context.scaffoldBackgroundColor, leading: IconButton( - icon: Icon( - Icons.close_rounded, - color: context.primaryColor, - size: 24, - ), + icon: Icon(Icons.close_rounded, color: context.primaryColor, size: 24), onPressed: () => context.navigator.popUntil((route) => route.isFirst), ), actions: [ TextButton( - onPressed: isEdited - ? () => _saveEditedImage(context, asset, image, ref) - : null, - child: Text( - "save_to_gallery".tr(), - style: TextStyle( - color: isEdited ? context.primaryColor : Colors.grey, - ), - ), + onPressed: isEdited ? () => _saveEditedImage(context, asset, image, ref) : null, + child: Text("save_to_gallery".tr(), style: TextStyle(color: isEdited ? context.primaryColor : Colors.grey)), ), ], ), backgroundColor: context.scaffoldBackgroundColor, body: Center( child: ConstrainedBox( - constraints: BoxConstraints( - maxHeight: context.height * 0.7, - maxWidth: context.width * 0.9, - ), + constraints: BoxConstraints(maxHeight: context.height * 0.7, maxWidth: context.width * 0.9), child: Container( decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(7), - ), + borderRadius: const BorderRadius.all(Radius.circular(7)), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.2), @@ -137,13 +101,8 @@ class EditImagePage extends ConsumerWidget { ], ), child: ClipRRect( - borderRadius: const BorderRadius.all( - Radius.circular(7), - ), - child: Image( - image: image.image, - fit: BoxFit.contain, - ), + borderRadius: const BorderRadius.all(Radius.circular(7)), + child: Image(image: image.image, fit: BoxFit.contain), ), ), ), @@ -153,9 +112,7 @@ class EditImagePage extends ConsumerWidget { margin: const EdgeInsets.only(bottom: 60, right: 10, left: 10, top: 10), decoration: BoxDecoration( color: context.scaffoldBackgroundColor, - borderRadius: const BorderRadius.all( - Radius.circular(30), - ), + borderRadius: const BorderRadius.all(Radius.circular(30)), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, @@ -164,15 +121,9 @@ class EditImagePage extends ConsumerWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ IconButton( - icon: Icon( - Icons.crop_rotate_rounded, - color: context.themeData.iconTheme.color, - size: 25, - ), + icon: Icon(Icons.crop_rotate_rounded, color: context.themeData.iconTheme.color, size: 25), onPressed: () { - context.pushRoute( - CropImageRoute(asset: asset, image: image), - ); + context.pushRoute(CropImageRoute(asset: asset, image: image)); }, ), Text("crop".tr(), style: context.textTheme.displayMedium), @@ -182,18 +133,9 @@ class EditImagePage extends ConsumerWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ IconButton( - icon: Icon( - Icons.filter, - color: context.themeData.iconTheme.color, - size: 25, - ), + icon: Icon(Icons.filter, color: context.themeData.iconTheme.color, size: 25), onPressed: () { - context.pushRoute( - FilterImageRoute( - asset: asset, - image: image, - ), - ); + context.pushRoute(FilterImageRoute(asset: asset, image: image)); }, ), Text("filter".tr(), style: context.textTheme.displayMedium), diff --git a/mobile/lib/pages/editing/filter.page.dart b/mobile/lib/pages/editing/filter.page.dart index 9af065489d..6d41b4c5b8 100644 --- a/mobile/lib/pages/editing/filter.page.dart +++ b/mobile/lib/pages/editing/filter.page.dart @@ -18,34 +18,23 @@ class FilterImagePage extends HookWidget { final Image image; final Asset asset; - const FilterImagePage({ - super.key, - required this.image, - required this.asset, - }); + const FilterImagePage({super.key, required this.image, required this.asset}); @override Widget build(BuildContext context) { final colorFilter = useState(filters[0]); final selectedFilterIndex = useState(0); - Future createFilteredImage( - ui.Image inputImage, - ColorFilter filter, - ) { + Future createFilteredImage(ui.Image inputImage, ColorFilter filter) { final completer = Completer(); - final size = - Size(inputImage.width.toDouble(), inputImage.height.toDouble()); + final size = Size(inputImage.width.toDouble(), inputImage.height.toDouble()); final recorder = ui.PictureRecorder(); final canvas = Canvas(recorder); final paint = Paint()..colorFilter = filter; canvas.drawImage(inputImage, Offset.zero, paint); - recorder - .endRecording() - .toImage(size.width.round(), size.height.round()) - .then((image) { + recorder.endRecording().toImage(size.width.round(), size.height.round()).then((image) { completer.complete(image); }); @@ -59,16 +48,17 @@ class FilterImagePage extends HookWidget { Future applyFilterAndConvert(ColorFilter filter) async { final completer = Completer(); - image.image.resolve(ImageConfiguration.empty).addListener( - ImageStreamListener((ImageInfo info, bool _) { - completer.complete(info.image); - }), - ); + image.image + .resolve(ImageConfiguration.empty) + .addListener( + ImageStreamListener((ImageInfo info, bool _) { + completer.complete(info.image); + }), + ); final uiImage = await completer.future; final filteredUiImage = await createFilteredImage(uiImage, filter); - final byteData = - await filteredUiImage.toByteData(format: ui.ImageByteFormat.png); + final byteData = await filteredUiImage.toByteData(format: ui.ImageByteFormat.png); final pngBytes = byteData!.buffer.asUint8List(); return Image.memory(pngBytes, fit: BoxFit.contain); @@ -81,21 +71,10 @@ class FilterImagePage extends HookWidget { leading: CloseButton(color: context.primaryColor), actions: [ IconButton( - icon: Icon( - Icons.done_rounded, - color: context.primaryColor, - size: 24, - ), + icon: Icon(Icons.done_rounded, color: context.primaryColor, size: 24), onPressed: () async { - final filteredImage = - await applyFilterAndConvert(colorFilter.value); - context.pushRoute( - EditImageRoute( - asset: asset, - image: filteredImage, - isEdited: true, - ), - ); + final filteredImage = await applyFilterAndConvert(colorFilter.value); + context.pushRoute(EditImageRoute(asset: asset, image: filteredImage, isEdited: true)); }, ), ], @@ -106,10 +85,7 @@ class FilterImagePage extends HookWidget { SizedBox( height: context.height * 0.7, child: Center( - child: ColorFiltered( - colorFilter: colorFilter.value, - child: image, - ), + child: ColorFiltered(colorFilter: colorFilter.value, child: image), ), ), SizedBox( @@ -162,23 +138,14 @@ class _FilterButton extends StatelessWidget { width: 80, height: 80, decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(10), - ), - border: isSelected - ? Border.all(color: context.primaryColor, width: 3) - : null, + borderRadius: const BorderRadius.all(Radius.circular(10)), + border: isSelected ? Border.all(color: context.primaryColor, width: 3) : null, ), child: ClipRRect( - borderRadius: const BorderRadius.all( - Radius.circular(10), - ), + borderRadius: const BorderRadius.all(Radius.circular(10)), child: ColorFiltered( colorFilter: filter, - child: FittedBox( - fit: BoxFit.cover, - child: image, - ), + child: FittedBox(fit: BoxFit.cover, child: image), ), ), ), diff --git a/mobile/lib/pages/library/archive.page.dart b/mobile/lib/pages/library/archive.page.dart index 2b4aa64f3b..8ca1bb9752 100644 --- a/mobile/lib/pages/library/archive.page.dart +++ b/mobile/lib/pages/library/archive.page.dart @@ -16,15 +16,10 @@ class ArchivePage extends HookConsumerWidget { final archiveRenderList = ref.watch(archiveTimelineProvider); final count = archiveRenderList.value?.totalAssets.toString() ?? "?"; return AppBar( - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_rounded)), centerTitle: true, automaticallyImplyLeading: false, - title: const Text( - 'archive_page_title', - ).tr(namedArgs: {'count': count}), + title: const Text('archive_page_title').tr(namedArgs: {'count': count}), ); } diff --git a/mobile/lib/pages/library/favorite.page.dart b/mobile/lib/pages/library/favorite.page.dart index 070693fe4a..649d7727d5 100644 --- a/mobile/lib/pages/library/favorite.page.dart +++ b/mobile/lib/pages/library/favorite.page.dart @@ -14,15 +14,10 @@ class FavoritesPage extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { AppBar buildAppBar() { return AppBar( - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_rounded)), centerTitle: true, automaticallyImplyLeading: false, - title: const Text( - 'favorites', - ).tr(), + title: const Text('favorites').tr(), ); } diff --git a/mobile/lib/pages/library/folder/folder.page.dart b/mobile/lib/pages/library/folder/folder.page.dart index 6ac7d60f9b..2968bca18e 100644 --- a/mobile/lib/pages/library/folder/folder.page.dart +++ b/mobile/lib/pages/library/folder/folder.page.dart @@ -16,14 +16,9 @@ import 'package:immich_mobile/utils/bytes_units.dart'; import 'package:immich_mobile/widgets/asset_grid/thumbnail_image.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; -RecursiveFolder? _findFolderInStructure( - RootFolder rootFolder, - RecursiveFolder targetFolder, -) { +RecursiveFolder? _findFolderInStructure(RootFolder rootFolder, RecursiveFolder targetFolder) { for (final folder in rootFolder.subfolders) { - if (targetFolder.path == '/' && - folder.path.isEmpty && - folder.name == targetFolder.name) { + if (targetFolder.path == '/' && folder.path.isEmpty && folder.name == targetFolder.name) { return folder; } @@ -51,36 +46,26 @@ class FolderPage extends HookConsumerWidget { final currentFolder = useState(folder); final sortOrder = useState(SortOrder.asc); - useEffect( - () { - if (folder == null) { - ref - .read(folderStructureProvider.notifier) - .fetchFolders(sortOrder.value); - } - return null; - }, - [], - ); + useEffect(() { + if (folder == null) { + ref.read(folderStructureProvider.notifier).fetchFolders(sortOrder.value); + } + return null; + }, []); // Update current folder when root structure changes - useEffect( - () { - if (folder != null && folderState.hasValue) { - final updatedFolder = - _findFolderInStructure(folderState.value!, folder!); - if (updatedFolder != null) { - currentFolder.value = updatedFolder; - } + useEffect(() { + if (folder != null && folderState.hasValue) { + final updatedFolder = _findFolderInStructure(folderState.value!, folder!); + if (updatedFolder != null) { + currentFolder.value = updatedFolder; } - return null; - }, - [folderState], - ); + } + return null; + }, [folderState]); void onToggleSortOrder() { - final newOrder = - sortOrder.value == SortOrder.asc ? SortOrder.desc : SortOrder.asc; + final newOrder = sortOrder.value == SortOrder.asc ? SortOrder.desc : SortOrder.asc; ref.read(folderStructureProvider.notifier).fetchFolders(newOrder); @@ -92,38 +77,19 @@ class FolderPage extends HookConsumerWidget { title: Text(currentFolder.value?.name ?? tr("folders")), elevation: 0, centerTitle: false, - actions: [ - IconButton( - icon: const Icon(Icons.swap_vert), - onPressed: onToggleSortOrder, - ), - ], + actions: [IconButton(icon: const Icon(Icons.swap_vert), onPressed: onToggleSortOrder)], ), body: folderState.when( data: (rootFolder) { if (folder == null) { - return FolderContent( - folder: rootFolder, - root: rootFolder, - sortOrder: sortOrder.value, - ); + return FolderContent(folder: rootFolder, root: rootFolder, sortOrder: sortOrder.value); } else { - return FolderContent( - folder: currentFolder.value!, - root: rootFolder, - sortOrder: sortOrder.value, - ); + return FolderContent(folder: currentFolder.value!, root: rootFolder, sortOrder: sortOrder.value); } }, - loading: () => const Center( - child: CircularProgressIndicator(), - ), + loading: () => const Center(child: CircularProgressIndicator()), error: (error, stack) { - ImmichToast.show( - context: context, - msg: "failed_to_load_folder".tr(), - toastType: ToastType.error, - ); + ImmichToast.show(context: context, msg: "failed_to_load_folder".tr(), toastType: ToastType.error); return Center(child: const Text("failed_to_load_folder").tr()); }, ), @@ -136,28 +102,18 @@ class FolderContent extends HookConsumerWidget { final RootFolder root; final SortOrder sortOrder; - const FolderContent({ - super.key, - this.folder, - required this.root, - this.sortOrder = SortOrder.asc, - }); + const FolderContent({super.key, this.folder, required this.root, this.sortOrder = SortOrder.asc}); @override Widget build(BuildContext context, WidgetRef ref) { final folderRenderlist = ref.watch(folderRenderListProvider(folder!)); // Initial asset fetch - useEffect( - () { - if (folder == null) return; - ref - .read(folderRenderListProvider(folder!).notifier) - .fetchAssets(sortOrder); - return null; - }, - [folder], - ); + useEffect(() { + if (folder == null) return; + ref.read(folderRenderListProvider(folder!).notifier).fetchAssets(sortOrder); + return null; + }, [folder]); if (folder == null) { return Center(child: const Text("folder_not_found").tr()); @@ -190,18 +146,12 @@ class FolderContent extends HookConsumerWidget { if (folder!.subfolders.isNotEmpty) ...folder!.subfolders.map( (subfolder) => LargeLeadingTile( - leading: Icon( - Icons.folder, - color: context.primaryColor, - size: 48, - ), + leading: Icon(Icons.folder, color: context.primaryColor, size: 48), title: Text( subfolder.name, softWrap: false, overflow: TextOverflow.ellipsis, - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w600, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600), ), subtitle: subfolder.subfolders.isNotEmpty ? Text( @@ -211,35 +161,24 @@ class FolderContent extends HookConsumerWidget { ), ) : null, - onTap: () => - context.pushRoute(FolderRoute(folder: subfolder)), + onTap: () => context.pushRoute(FolderRoute(folder: subfolder)), ), ), - if (!list.isEmpty && - list.allAssets != null && - list.allAssets!.isNotEmpty) + if (!list.isEmpty && list.allAssets != null && list.allAssets!.isNotEmpty) ...list.allAssets!.map( (asset) => LargeLeadingTile( onTap: () { ref.read(currentAssetProvider.notifier).set(asset); context.pushRoute( - GalleryViewerRoute( - renderList: list, - initialIndex: list.allAssets!.indexOf(asset), - ), + GalleryViewerRoute(renderList: list, initialIndex: list.allAssets!.indexOf(asset)), ); }, leading: ClipRRect( - borderRadius: const BorderRadius.all( - Radius.circular(15), - ), + borderRadius: const BorderRadius.all(Radius.circular(15)), child: SizedBox( width: 80, height: 80, - child: ThumbnailImage( - asset: asset, - showStorageIndicator: false, - ), + child: ThumbnailImage(asset: asset, showStorageIndicator: false), ), ), title: Text( @@ -247,30 +186,20 @@ class FolderContent extends HookConsumerWidget { maxLines: 2, softWrap: false, overflow: TextOverflow.ellipsis, - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w600, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600), ), subtitle: Text( "${asset.exifInfo?.fileSize != null ? formatBytes(asset.exifInfo?.fileSize ?? 0) : ""} • ${DateFormat.yMMMd().format(asset.fileCreatedAt)}", - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), ), ), ], ); }, - loading: () => const Center( - child: CircularProgressIndicator(), - ), + loading: () => const Center(child: CircularProgressIndicator()), error: (error, stack) { - ImmichToast.show( - context: context, - msg: "failed_to_load_assets".tr(), - toastType: ToastType.error, - ); + ImmichToast.show(context: context, msg: "failed_to_load_assets".tr(), toastType: ToastType.error); return Center(child: const Text("failed_to_load_assets").tr()); }, ), @@ -284,11 +213,7 @@ class FolderPath extends StatelessWidget { final RootFolder currentFolder; final RootFolder root; - const FolderPath({ - super.key, - required this.currentFolder, - required this.root, - }); + const FolderPath({super.key, required this.currentFolder, required this.root}); @override Widget build(BuildContext context) { diff --git a/mobile/lib/pages/library/library.page.dart b/mobile/lib/pages/library/library.page.dart index a9ef479e2c..483427d2de 100644 --- a/mobile/lib/pages/library/library.page.dart +++ b/mobile/lib/pages/library/library.page.dart @@ -25,8 +25,7 @@ class LibraryPage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { context.locale; - final trashEnabled = - ref.watch(serverInfoProvider.select((v) => v.serverFeatures.trash)); + final trashEnabled = ref.watch(serverInfoProvider.select((v) => v.serverFeatures.trash)); return Scaffold( appBar: const ImmichAppBar(), @@ -75,17 +74,11 @@ class LibraryPage extends ConsumerWidget { const Wrap( spacing: 8, runSpacing: 8, - children: [ - PeopleCollectionCard(), - PlacesCollectionCard(), - LocalAlbumsCollectionCard(), - ], + children: [PeopleCollectionCard(), PlacesCollectionCard(), LocalAlbumsCollectionCard()], ), const SizedBox(height: 12), const QuickAccessButtons(), - const SizedBox( - height: 32, - ), + const SizedBox(height: 32), ], ), ), @@ -101,13 +94,8 @@ class QuickAccessButtons extends ConsumerWidget { return Container( decoration: BoxDecoration( - border: Border.all( - color: context.colorScheme.onSurface.withAlpha(10), - width: 1, - ), - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), + border: Border.all(color: context.colorScheme.onSurface.withAlpha(10), width: 1), + borderRadius: const BorderRadius.all(Radius.circular(20)), gradient: LinearGradient( colors: [ context.colorScheme.primary.withAlpha(10), @@ -131,41 +119,26 @@ class QuickAccessButtons extends ConsumerWidget { bottomRight: Radius.circular(partners.isEmpty ? 20 : 0), ), ), - leading: const Icon( - Icons.folder_outlined, - size: 26, - ), + leading: const Icon(Icons.folder_outlined, size: 26), title: Text( IntlKeys.folders.tr(), - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500), ), onTap: () => context.pushRoute(FolderRoute()), ), ListTile( - leading: const Icon( - Icons.lock_outline_rounded, - size: 26, - ), + leading: const Icon(Icons.lock_outline_rounded, size: 26), title: Text( IntlKeys.locked_folder.tr(), - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500), ), onTap: () => context.pushRoute(const LockedRoute()), ), ListTile( - leading: const Icon( - Icons.group_outlined, - size: 26, - ), + leading: const Icon(Icons.group_outlined, size: 26), title: Text( IntlKeys.partners.tr(), - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500), ), onTap: () => context.pushRoute(const PartnerRoute()), ), @@ -197,24 +170,13 @@ class PartnerList extends ConsumerWidget { bottomRight: Radius.circular(isLastItem ? 20 : 0), ), ), - contentPadding: const EdgeInsets.only( - left: 12.0, - right: 18.0, - ), + contentPadding: const EdgeInsets.only(left: 12.0, right: 18.0), leading: userAvatar(context, partner, radius: 16), title: const Text( "partner_list_user_photos", - style: TextStyle( - fontWeight: FontWeight.w500, - ), - ).tr( - namedArgs: { - 'user': partner.name, - }, - ), - onTap: () => context.pushRoute( - (PartnerDetailRoute(partner: partner)), - ), + style: TextStyle(fontWeight: FontWeight.w500), + ).tr(namedArgs: {'user': partner.name}), + onTap: () => context.pushRoute((PartnerDetailRoute(partner: partner))), ); }, ); @@ -242,22 +204,15 @@ class PeopleCollectionCard extends ConsumerWidget { height: size, width: size, decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), + borderRadius: const BorderRadius.all(Radius.circular(20)), gradient: LinearGradient( - colors: [ - context.colorScheme.primary.withAlpha(30), - context.colorScheme.primary.withAlpha(25), - ], + colors: [context.colorScheme.primary.withAlpha(30), context.colorScheme.primary.withAlpha(25)], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), ), child: people.widgetWhen( - onLoading: () => const Center( - child: CircularProgressIndicator(), - ), + onLoading: () => const Center(child: CircularProgressIndicator()), onData: (people) { return GridView.count( crossAxisCount: 2, @@ -309,9 +264,7 @@ class LocalAlbumsCollectionCard extends HookConsumerWidget { final size = context.width * widthFactor - 20.0; return GestureDetector( - onTap: () => context.pushRoute( - const LocalAlbumsRoute(), - ), + onTap: () => context.pushRoute(const LocalAlbumsRoute()), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -322,10 +275,7 @@ class LocalAlbumsCollectionCard extends HookConsumerWidget { decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(20)), gradient: LinearGradient( - colors: [ - context.colorScheme.primary.withAlpha(30), - context.colorScheme.primary.withAlpha(25), - ], + colors: [context.colorScheme.primary.withAlpha(30), context.colorScheme.primary.withAlpha(25)], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), @@ -337,10 +287,7 @@ class LocalAlbumsCollectionCard extends HookConsumerWidget { mainAxisSpacing: 8, physics: const NeverScrollableScrollPhysics(), children: albums.take(4).map((album) { - return AlbumThumbnailCard( - album: album, - showTitle: false, - ); + return AlbumThumbnailCard(album: album, showTitle: false); }).toList(), ), ), @@ -374,11 +321,7 @@ class PlacesCollectionCard extends StatelessWidget { final size = context.width * widthFactor - 20.0; return GestureDetector( - onTap: () => context.pushRoute( - PlacesCollectionRoute( - currentLocation: null, - ), - ), + onTap: () => context.pushRoute(PlacesCollectionRoute(currentLocation: null)), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -388,20 +331,14 @@ class PlacesCollectionCard extends StatelessWidget { child: DecoratedBox( decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(20)), - color: - context.colorScheme.secondaryContainer.withAlpha(100), + color: context.colorScheme.secondaryContainer.withAlpha(100), ), child: IgnorePointer( child: MapThumbnail( zoom: 8, - centre: const LatLng( - 21.44950, - -157.91959, - ), + centre: const LatLng(21.44950, -157.91959), showAttribution: false, - themeMode: context.isDarkTheme - ? ThemeMode.dark - : ThemeMode.light, + themeMode: context.isDarkTheme ? ThemeMode.dark : ThemeMode.light, ), ), ), @@ -429,12 +366,7 @@ class ActionButton extends StatelessWidget { final IconData icon; final String label; - const ActionButton({ - super.key, - required this.onPressed, - required this.icon, - required this.label, - }); + const ActionButton({super.key, required this.onPressed, required this.icon, required this.label}); @override Widget build(BuildContext context) { @@ -443,13 +375,7 @@ class ActionButton extends StatelessWidget { onPressed: onPressed, label: Padding( padding: const EdgeInsets.only(left: 4.0), - child: Text( - label, - style: TextStyle( - color: context.colorScheme.onSurface, - fontSize: 15, - ), - ), + child: Text(label, style: TextStyle(color: context.colorScheme.onSurface, fontSize: 15)), ), style: FilledButton.styleFrom( elevation: 0, @@ -458,16 +384,10 @@ class ActionButton extends StatelessWidget { alignment: Alignment.centerLeft, shape: RoundedRectangleBorder( borderRadius: const BorderRadius.all(Radius.circular(25)), - side: BorderSide( - color: context.colorScheme.onSurface.withAlpha(10), - width: 1, - ), + side: BorderSide(color: context.colorScheme.onSurface.withAlpha(10), width: 1), ), ), - icon: Icon( - icon, - color: context.primaryColor, - ), + icon: Icon(icon, color: context.primaryColor), ), ); } diff --git a/mobile/lib/pages/library/local_albums.page.dart b/mobile/lib/pages/library/local_albums.page.dart index 9eceaca205..e52a8326df 100644 --- a/mobile/lib/pages/library/local_albums.page.dart +++ b/mobile/lib/pages/library/local_albums.page.dart @@ -18,9 +18,7 @@ class LocalAlbumsPage extends HookConsumerWidget { final albums = ref.watch(localAlbumsProvider); return Scaffold( - appBar: AppBar( - title: Text('on_this_device'.tr()), - ), + appBar: AppBar(title: Text('on_this_device'.tr())), body: ListView.builder( padding: const EdgeInsets.all(18.0), itemCount: albums.length, @@ -28,34 +26,20 @@ class LocalAlbumsPage extends HookConsumerWidget { return Padding( padding: const EdgeInsets.only(bottom: 8.0), child: LargeLeadingTile( - leadingPadding: const EdgeInsets.only( - right: 16, - ), + leadingPadding: const EdgeInsets.only(right: 16), leading: ClipRRect( borderRadius: const BorderRadius.all(Radius.circular(15)), - child: ImmichThumbnail( - asset: albums[index].thumbnail.value, - width: 80, - height: 80, - ), + child: ImmichThumbnail(asset: albums[index].thumbnail.value, width: 80, height: 80), ), title: Text( albums[index].name, - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w600, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600), ), subtitle: Text( - 'items_count'.t( - context: context, - args: {'count': albums[index].assetCount}, - ), - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + 'items_count'.t(context: context, args: {'count': albums[index].assetCount}), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), - onTap: () => context - .pushRoute(AlbumViewerRoute(albumId: albums[index].id)), + onTap: () => context.pushRoute(AlbumViewerRoute(albumId: albums[index].id)), ), ); }, diff --git a/mobile/lib/pages/library/locked/locked.page.dart b/mobile/lib/pages/library/locked/locked.page.dart index eef12a7107..aea62e0051 100644 --- a/mobile/lib/pages/library/locked/locked.page.dart +++ b/mobile/lib/pages/library/locked/locked.page.dart @@ -19,29 +19,23 @@ class LockedPage extends HookConsumerWidget { final showOverlay = useState(false); final authProviderNotifier = ref.read(authProvider.notifier); // lock the page when it is destroyed - useEffect( - () { - return () { - authProviderNotifier.lockPinCode(); - }; - }, - [], - ); + useEffect(() { + return () { + authProviderNotifier.lockPinCode(); + }; + }, []); - useEffect( - () { - if (context.mounted) { - if (appLifeCycle == AppLifecycleState.resumed) { - showOverlay.value = false; - } else { - showOverlay.value = true; - } + useEffect(() { + if (context.mounted) { + if (appLifeCycle == AppLifecycleState.resumed) { + showOverlay.value = false; + } else { + showOverlay.value = true; } + } - return null; - }, - [appLifeCycle], - ); + return null; + }, [appLifeCycle]); return Scaffold( appBar: ref.watch(multiselectProvider) ? null : const LockPageAppBar(), @@ -51,12 +45,7 @@ class LockedPage extends HookConsumerWidget { renderListProvider: lockedTimelineProvider, topWidget: Padding( padding: const EdgeInsets.all(16.0), - child: Center( - child: Text( - 'no_locked_photos_message'.tr(), - style: context.textTheme.labelLarge, - ), - ), + child: Center(child: Text('no_locked_photos_message'.tr(), style: context.textTheme.labelLarge)), ), editEnabled: false, favoriteEnabled: false, @@ -84,9 +73,7 @@ class LockPageAppBar extends ConsumerWidget implements PreferredSizeWidget { ), centerTitle: true, automaticallyImplyLeading: false, - title: const Text( - 'locked_folder', - ).tr(), + title: const Text('locked_folder').tr(), ); } diff --git a/mobile/lib/pages/library/locked/pin_auth.page.dart b/mobile/lib/pages/library/locked/pin_auth.page.dart index cca0e3b7ac..36befa0016 100644 --- a/mobile/lib/pages/library/locked/pin_auth.page.dart +++ b/mobile/lib/pages/library/locked/pin_auth.page.dart @@ -1,13 +1,14 @@ import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:flutter_hooks/flutter_hooks.dart' show useState; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/providers/local_auth.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/widgets/forms/pin_registration_form.dart'; import 'package:immich_mobile/widgets/forms/pin_verification_form.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; @RoutePage() class PinAuthPage extends HookConsumerWidget { @@ -19,27 +20,25 @@ class PinAuthPage extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final localAuthState = ref.watch(localAuthProvider); final showPinRegistrationForm = useState(createPinCode); + final isBetaTimeline = Store.isBetaTimelineEnabled; Future registerBiometric(String pinCode) async { - final isRegistered = - await ref.read(localAuthProvider.notifier).registerBiometric( - context, - pinCode, - ); + final isRegistered = await ref.read(localAuthProvider.notifier).registerBiometric(context, pinCode); if (isRegistered) { context.showSnackBar( SnackBar( - content: Text( - 'biometric_auth_enabled'.tr(), - style: context.textTheme.labelLarge, - ), + content: Text('biometric_auth_enabled'.tr(), style: context.textTheme.labelLarge), duration: const Duration(seconds: 3), backgroundColor: context.colorScheme.primaryContainer, ), ); - context.replaceRoute(const LockedRoute()); + if (isBetaTimeline) { + context.replaceRoute(const DriftLockedFolderRoute()); + } else { + context.replaceRoute(const LockedRoute()); + } } } @@ -74,27 +73,26 @@ class PinAuthPage extends HookConsumerWidget { } return Scaffold( - appBar: AppBar( - title: Text('locked_folder'.tr()), - ), + appBar: AppBar(title: Text('locked_folder'.tr())), body: ListView( shrinkWrap: true, children: [ Padding( padding: const EdgeInsets.only(top: 36.0), child: showPinRegistrationForm.value - ? Center( - child: PinRegistrationForm( - onDone: () => showPinRegistrationForm.value = false, - ), - ) + ? Center(child: PinRegistrationForm(onDone: () => showPinRegistrationForm.value = false)) : Column( children: [ Center( child: PinVerificationForm( autoFocus: true, - onSuccess: (_) => - context.replaceRoute(const LockedRoute()), + onSuccess: (_) { + if (isBetaTimeline) { + context.replaceRoute(const DriftLockedFolderRoute()); + } else { + context.replaceRoute(const LockedRoute()); + } + }, ), ), const SizedBox(height: 24), @@ -102,17 +100,11 @@ class PinAuthPage extends HookConsumerWidget { Padding( padding: const EdgeInsets.only(right: 16.0), child: TextButton.icon( - icon: const Icon( - Icons.fingerprint, - size: 28, - ), + icon: const Icon(Icons.fingerprint, size: 28), onPressed: enableBiometricAuth, label: Text( 'use_biometric'.tr(), - style: context.textTheme.labelLarge?.copyWith( - color: context.primaryColor, - fontSize: 18, - ), + style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor, fontSize: 18), ), ), ), diff --git a/mobile/lib/pages/library/partner/drift_partner.page.dart b/mobile/lib/pages/library/partner/drift_partner.page.dart new file mode 100644 index 0000000000..834e55ffd4 --- /dev/null +++ b/mobile/lib/pages/library/partner/drift_partner.page.dart @@ -0,0 +1,140 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/people/partner_user_avatar.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/partner.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; +import 'package:immich_mobile/widgets/common/confirm_dialog.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; + +@RoutePage() +class DriftPartnerPage extends HookConsumerWidget { + const DriftPartnerPage({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final potentialPartnersAsync = ref.watch(driftAvailablePartnerProvider); + + addNewUsersHandler() async { + final potentialPartners = potentialPartnersAsync.value; + if (potentialPartners == null || potentialPartners.isEmpty) { + ImmichToast.show(context: context, msg: "partner_page_no_more_users".tr()); + return; + } + + final selectedUser = await showDialog( + context: context, + builder: (context) { + return SimpleDialog( + title: const Text("partner_page_select_partner").tr(), + children: [ + for (PartnerUserDto partner in potentialPartners) + SimpleDialogOption( + onPressed: () => context.pop(partner), + child: Row( + children: [ + Padding( + padding: const EdgeInsets.only(right: 8), + child: PartnerUserAvatar(partner: partner), + ), + Text(partner.name), + ], + ), + ), + ], + ); + }, + ); + if (selectedUser != null) { + await ref.read(partnerUsersProvider.notifier).addPartner(selectedUser); + } + } + + onDeleteUser(PartnerUserDto partner) { + return showDialog( + context: context, + builder: (BuildContext context) { + return ConfirmDialog( + title: "stop_photo_sharing", + content: "partner_page_stop_sharing_content".tr(namedArgs: {'partner': partner.name}), + onOk: () => ref.read(partnerUsersProvider.notifier).removePartner(partner), + ); + }, + ); + } + + return Scaffold( + appBar: AppBar( + title: const Text("partners").t(context: context), + elevation: 0, + centerTitle: false, + actions: [ + IconButton( + onPressed: potentialPartnersAsync.whenOrNull(data: (data) => addNewUsersHandler), + icon: const Icon(Icons.person_add), + tooltip: "add_partner".tr(), + ), + ], + ), + body: _SharedToPartnerList(onAddPartner: addNewUsersHandler, onDeletePartner: onDeleteUser), + ); + } +} + +class _SharedToPartnerList extends ConsumerWidget { + final VoidCallback onAddPartner; + final Function(PartnerUserDto partner) onDeletePartner; + + const _SharedToPartnerList({required this.onAddPartner, required this.onDeletePartner}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final partnerAsync = ref.watch(driftSharedByPartnerProvider); + + return partnerAsync.when( + data: (partners) { + if (partners.isEmpty) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: const Text("partner_page_empty_message", style: TextStyle(fontSize: 14)).tr(), + ), + Align( + alignment: Alignment.center, + child: ElevatedButton.icon( + onPressed: onAddPartner, + icon: const Icon(Icons.person_add), + label: const Text("add_partner").tr(), + ), + ), + ], + ), + ); + } + + return ListView.builder( + itemCount: partners.length, + itemBuilder: (context, index) { + final partner = partners[index]; + return ListTile( + leading: PartnerUserAvatar(partner: partner), + title: Text(partner.name), + subtitle: Text(partner.email), + trailing: IconButton(icon: const Icon(Icons.person_remove), onPressed: () => onDeletePartner(partner)), + ); + }, + ); + }, + loading: () => const Center(child: CircularProgressIndicator()), + error: (error, stack) => Center(child: Text("Error loading partners: $error")), + ); + } +} diff --git a/mobile/lib/pages/library/partner/partner.page.dart b/mobile/lib/pages/library/partner/partner.page.dart index 91b661e7ce..eae4228a2d 100644 --- a/mobile/lib/pages/library/partner/partner.page.dart +++ b/mobile/lib/pages/library/partner/partner.page.dart @@ -22,10 +22,7 @@ class PartnerPage extends HookConsumerWidget { addNewUsersHandler() async { final users = availableUsers.value; if (users == null || users.isEmpty) { - ImmichToast.show( - context: context, - msg: "partner_page_no_more_users".tr(), - ); + ImmichToast.show(context: context, msg: "partner_page_no_more_users".tr()); return; } @@ -40,10 +37,7 @@ class PartnerPage extends HookConsumerWidget { onPressed: () => context.pop(u), child: Row( children: [ - Padding( - padding: const EdgeInsets.only(right: 8), - child: userAvatar(context, u), - ), + Padding(padding: const EdgeInsets.only(right: 8), child: userAvatar(context, u)), Text(u.name), ], ), @@ -53,16 +47,11 @@ class PartnerPage extends HookConsumerWidget { }, ); if (selectedUser != null) { - final ok = - await ref.read(partnerServiceProvider).addPartner(selectedUser); + final ok = await ref.read(partnerServiceProvider).addPartner(selectedUser); if (ok) { ref.invalidate(partnerSharedByProvider); } else { - ImmichToast.show( - context: context, - msg: "partner_page_partner_add_failed".tr(), - toastType: ToastType.error, - ); + ImmichToast.show(context: context, msg: "partner_page_partner_add_failed".tr(), toastType: ToastType.error); } } } @@ -73,8 +62,7 @@ class PartnerPage extends HookConsumerWidget { builder: (BuildContext context) { return ConfirmDialog( title: "stop_photo_sharing", - content: "partner_page_stop_sharing_content" - .tr(namedArgs: {'partner': u.name}), + content: "partner_page_stop_sharing_content".tr(namedArgs: {'partner': u.name}), onOk: () => ref.read(partnerServiceProvider).removePartner(u), ); }, @@ -89,9 +77,7 @@ class PartnerPage extends HookConsumerWidget { padding: const EdgeInsets.only(left: 16.0, top: 16.0), child: Text( "partner_page_shared_to_title", - style: context.textTheme.titleSmall?.copyWith( - color: context.colorScheme.onSurface.withAlpha(200), - ), + style: context.textTheme.titleSmall?.copyWith(color: context.colorScheme.onSurface.withAlpha(200)), ).tr(), ), if (users.isNotEmpty) @@ -101,10 +87,7 @@ class PartnerPage extends HookConsumerWidget { itemBuilder: ((context, index) { return ListTile( leading: userAvatar(context, users[index]), - title: Text( - users[index].email, - style: context.textTheme.bodyLarge, - ), + title: Text(users[index].email, style: context.textTheme.bodyLarge), trailing: IconButton( icon: const Icon(Icons.person_remove), onPressed: () => onDeleteUser(users[index]), @@ -120,17 +103,12 @@ class PartnerPage extends HookConsumerWidget { children: [ Padding( padding: const EdgeInsets.symmetric(vertical: 8), - child: const Text( - "partner_page_empty_message", - style: TextStyle(fontSize: 14), - ).tr(), + child: const Text("partner_page_empty_message", style: TextStyle(fontSize: 14)).tr(), ), Align( alignment: Alignment.center, child: ElevatedButton.icon( - onPressed: availableUsers.whenOrNull( - data: (data) => addNewUsersHandler, - ), + onPressed: availableUsers.whenOrNull(data: (data) => addNewUsersHandler), icon: const Icon(Icons.person_add), label: const Text("add_partner").tr(), ), @@ -149,8 +127,7 @@ class PartnerPage extends HookConsumerWidget { centerTitle: false, actions: [ IconButton( - onPressed: - availableUsers.whenOrNull(data: (data) => addNewUsersHandler), + onPressed: availableUsers.whenOrNull(data: (data) => addNewUsersHandler), icon: const Icon(Icons.person_add), tooltip: "add_partner".tr(), ), diff --git a/mobile/lib/pages/library/partner/partner_detail.page.dart b/mobile/lib/pages/library/partner/partner_detail.page.dart index 94e098b973..1f15dab6a3 100644 --- a/mobile/lib/pages/library/partner/partner_detail.page.dart +++ b/mobile/lib/pages/library/partner/partner_detail.page.dart @@ -22,17 +22,10 @@ class PartnerDetailPage extends HookConsumerWidget { final inTimeline = useState(partner.inTimeline); bool toggleInProcess = false; - useEffect( - () { - Future.microtask( - () async => { - await ref.read(assetProvider.notifier).getAllAsset(), - }, - ); - return null; - }, - [], - ); + useEffect(() { + Future.microtask(() async => {await ref.read(assetProvider.notifier).getAllAsset()}); + return null; + }, []); void toggleInTimeline() async { if (toggleInProcess) return; @@ -66,28 +59,16 @@ class PartnerDetailPage extends HookConsumerWidget { return Scaffold( appBar: ref.watch(multiselectProvider) ? null - : AppBar( - title: Text(partner.name), - elevation: 0, - centerTitle: false, - ), + : AppBar(title: Text(partner.name), elevation: 0, centerTitle: false), body: MultiselectGrid( topWidget: Padding( padding: const EdgeInsets.only(left: 8.0, right: 8.0, top: 16.0), child: Container( decoration: BoxDecoration( - border: Border.all( - color: context.colorScheme.onSurface.withAlpha(10), - width: 1, - ), - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), + border: Border.all(color: context.colorScheme.onSurface.withAlpha(10), width: 1), + borderRadius: const BorderRadius.all(Radius.circular(20)), gradient: LinearGradient( - colors: [ - context.colorScheme.primary.withAlpha(10), - context.colorScheme.primary.withAlpha(15), - ], + colors: [context.colorScheme.primary.withAlpha(10), context.colorScheme.primary.withAlpha(15)], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), @@ -97,18 +78,13 @@ class PartnerDetailPage extends HookConsumerWidget { child: ListTile( title: Text( "Show in timeline", - style: context.textTheme.titleSmall?.copyWith( - color: context.colorScheme.primary, - ), + style: context.textTheme.titleSmall?.copyWith(color: context.colorScheme.primary), ), subtitle: Text( "Show photos and videos from this user in your timeline", style: context.textTheme.bodyMedium, ), - trailing: Switch( - value: inTimeline.value, - onChanged: (_) => toggleInTimeline(), - ), + trailing: Switch(value: inTimeline.value, onChanged: (_) => toggleInTimeline()), ), ), ), diff --git a/mobile/lib/pages/library/people/people_collection.page.dart b/mobile/lib/pages/library/people/people_collection.page.dart index b98e46aabe..375d4d2a96 100644 --- a/mobile/lib/pages/library/people/people_collection.page.dart +++ b/mobile/lib/pages/library/people/people_collection.page.dart @@ -21,10 +21,7 @@ class PeopleCollectionPage extends HookConsumerWidget { final formFocus = useFocusNode(); final ValueNotifier search = useState(null); - showNameEditModel( - String personId, - String personName, - ) { + showNameEditModel(String personId, String personName) { return showDialog( context: context, useRootNavigator: false, @@ -66,9 +63,7 @@ class PeopleCollectionPage extends HookConsumerWidget { data: (people) { if (search.value != null) { people = people.where((person) { - return person.name - .toLowerCase() - .contains(search.value!.toLowerCase()); + return person.name.toLowerCase().contains(search.value!.toLowerCase()); }).toList(); } return GridView.builder( @@ -86,29 +81,20 @@ class PeopleCollectionPage extends HookConsumerWidget { children: [ GestureDetector( onTap: () { - context.pushRoute( - PersonResultRoute( - personId: person.id, - personName: person.name, - ), - ); + context.pushRoute(PersonResultRoute(personId: person.id, personName: person.name)); }, child: Material( shape: const CircleBorder(side: BorderSide.none), elevation: 3, child: CircleAvatar( maxRadius: isTablet ? 120 / 2 : 96 / 2, - backgroundImage: NetworkImage( - getFaceThumbnailUrl(person.id), - headers: headers, - ), + backgroundImage: NetworkImage(getFaceThumbnailUrl(person.id), headers: headers), ), ), ), const SizedBox(height: 12), GestureDetector( - onTap: () => - showNameEditModel(person.id, person.name), + onTap: () => showNameEditModel(person.id, person.name), child: person.name.isEmpty ? Text( 'add_a_name'.tr(), @@ -118,16 +104,11 @@ class PeopleCollectionPage extends HookConsumerWidget { ), ) : Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16.0, - ), + padding: const EdgeInsets.symmetric(horizontal: 16.0), child: Text( person.name, overflow: TextOverflow.ellipsis, - style: - context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500), ), ), ), diff --git a/mobile/lib/pages/library/places/places_collection.page.dart b/mobile/lib/pages/library/places/places_collection.page.dart index b98537d515..73c38a109c 100644 --- a/mobile/lib/pages/library/places/places_collection.page.dart +++ b/mobile/lib/pages/library/places/places_collection.page.dart @@ -59,17 +59,11 @@ class PlacesCollectionPage extends HookConsumerWidget { height: 200, width: context.width, child: MapThumbnail( - onTap: (_, __) => context - .pushRoute(MapRoute(initialLocation: currentLocation)), + onTap: (_, __) => context.pushRoute(MapRoute(initialLocation: currentLocation)), zoom: 8, - centre: currentLocation ?? - const LatLng( - 21.44950, - -157.91959, - ), + centre: currentLocation ?? const LatLng(21.44950, -157.91959), showAttribution: false, - themeMode: - context.isDarkTheme ? ThemeMode.dark : ThemeMode.light, + themeMode: context.isDarkTheme ? ThemeMode.dark : ThemeMode.light, ), ), ), @@ -77,9 +71,7 @@ class PlacesCollectionPage extends HookConsumerWidget { data: (places) { if (search.value != null) { places = places.where((place) { - return place.label - .toLowerCase() - .contains(search.value!.toLowerCase()); + return place.label.toLowerCase().contains(search.value!.toLowerCase()); }).toList(); } return ListView.builder( @@ -110,24 +102,17 @@ class PlaceTile extends StatelessWidget { @override Widget build(BuildContext context) { - final thumbnailUrl = - '${Store.get(StoreKey.serverEndpoint)}/assets/$id/thumbnail'; + final thumbnailUrl = '${Store.get(StoreKey.serverEndpoint)}/assets/$id/thumbnail'; void navigateToPlace() { context.pushRoute( SearchRoute( prefilter: SearchFilter( people: {}, - location: SearchLocationFilter( - city: name, - ), + location: SearchLocationFilter(city: name), camera: SearchCameraFilter(), date: SearchDateFilter(), - display: SearchDisplayFilters( - isNotInAlbum: false, - isArchive: false, - isFavorite: false, - ), + display: SearchDisplayFilters(isNotInAlbum: false, isArchive: false, isFavorite: false), mediaType: AssetType.other, ), ), @@ -136,24 +121,16 @@ class PlaceTile extends StatelessWidget { return LargeLeadingTile( onTap: () => navigateToPlace(), - title: Text( - name, - style: context.textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.w500, - ), - ), + title: Text(name, style: context.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w500)), leading: ClipRRect( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), + borderRadius: const BorderRadius.all(Radius.circular(20)), child: CachedNetworkImage( width: 80, height: 80, fit: BoxFit.cover, imageUrl: thumbnailUrl, httpHeaders: ApiService.getRequestHeaders(), - errorWidget: (context, url, error) => - const Icon(Icons.image_not_supported_outlined), + errorWidget: (context, url, error) => const Icon(Icons.image_not_supported_outlined), ), ), ); diff --git a/mobile/lib/pages/library/shared_link/shared_link.page.dart b/mobile/lib/pages/library/shared_link/shared_link.page.dart index 8873e9b443..66a77fb761 100644 --- a/mobile/lib/pages/library/shared_link/shared_link.page.dart +++ b/mobile/lib/pages/library/shared_link/shared_link.page.dart @@ -17,16 +17,13 @@ class SharedLinkPage extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final sharedLinks = ref.watch(sharedLinksStateProvider); - useEffect( - () { - ref.read(sharedLinksStateProvider.notifier).fetchLinks(); - return () { - if (!context.mounted) return; - ref.invalidate(sharedLinksStateProvider); - }; - }, - [], - ); + useEffect(() { + ref.read(sharedLinksStateProvider.notifier).fetchLinks(); + return () { + if (!context.mounted) return; + ref.invalidate(sharedLinksStateProvider); + }; + }, []); Widget buildNoShares() { return Column( @@ -36,31 +33,19 @@ class SharedLinkPage extends HookConsumerWidget { padding: const EdgeInsets.only(left: 16.0, top: 16.0), child: const Text( "shared_link_manage_links", - style: TextStyle( - fontSize: 14, - color: Colors.grey, - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 14, color: Colors.grey, fontWeight: FontWeight.bold), ).tr(), ), Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), child: Padding( padding: const EdgeInsets.symmetric(vertical: 10), - child: const Text( - "you_dont_have_any_shared_links", - style: TextStyle(fontSize: 14), - ).tr(), + child: const Text("you_dont_have_any_shared_links", style: TextStyle(fontSize: 14)).tr(), ), ), Expanded( child: Center( - child: Icon( - Icons.link_off, - size: 100, - color: - context.themeData.iconTheme.color?.withValues(alpha: 0.5), - ), + child: Icon(Icons.link_off, size: 100, color: context.themeData.iconTheme.color?.withValues(alpha: 0.5)), ), ), ], @@ -75,9 +60,7 @@ class SharedLinkPage extends HookConsumerWidget { padding: const EdgeInsets.only(left: 16.0, top: 16.0, bottom: 30.0), child: Text( "shared_link_manage_links", - style: context.textTheme.labelLarge?.copyWith( - color: context.textTheme.labelLarge?.color?.withAlpha(200), - ), + style: context.textTheme.labelLarge?.copyWith(color: context.textTheme.labelLarge?.color?.withAlpha(200)), ).tr(), ), Expanded( @@ -86,8 +69,7 @@ class SharedLinkPage extends HookConsumerWidget { if (constraints.maxWidth > 600) { // Two column return GridView.builder( - gridDelegate: - const SliverGridDelegateWithFixedCrossAxisCount( + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, mainAxisExtent: 180, ), @@ -113,16 +95,11 @@ class SharedLinkPage extends HookConsumerWidget { } return Scaffold( - appBar: AppBar( - title: const Text("shared_link_app_bar_title").tr(), - elevation: 0, - centerTitle: false, - ), + appBar: AppBar(title: const Text("shared_link_app_bar_title").tr(), elevation: 0, centerTitle: false), body: SafeArea( child: sharedLinks.widgetWhen( onError: (error, stackTrace) => buildNoShares(), - onData: (links) => - links.isNotEmpty ? buildSharesList(links) : buildNoShares(), + onData: (links) => links.isNotEmpty ? buildSharesList(links) : buildNoShares(), ), ), ); diff --git a/mobile/lib/pages/library/shared_link/shared_link_edit.page.dart b/mobile/lib/pages/library/shared_link/shared_link_edit.page.dart index 6c18841089..dcd503335b 100644 --- a/mobile/lib/pages/library/shared_link/shared_link_edit.page.dart +++ b/mobile/lib/pages/library/shared_link/shared_link_edit.page.dart @@ -19,23 +19,16 @@ class SharedLinkEditPage extends HookConsumerWidget { final List? assetsList; final String? albumId; - const SharedLinkEditPage({ - super.key, - this.existingLink, - this.assetsList, - this.albumId, - }); + const SharedLinkEditPage({super.key, this.existingLink, this.assetsList, this.albumId}); @override Widget build(BuildContext context, WidgetRef ref) { const padding = 20.0; final themeData = context.themeData; final colorScheme = context.colorScheme; - final descriptionController = - useTextEditingController(text: existingLink?.description ?? ""); + final descriptionController = useTextEditingController(text: existingLink?.description ?? ""); final descriptionFocusNode = useFocusNode(); - final passwordController = - useTextEditingController(text: existingLink?.password ?? ""); + final passwordController = useTextEditingController(text: existingLink?.password ?? ""); final showMetadata = useState(existingLink?.showMetadata ?? true); final allowDownload = useState(existingLink?.allowDownload ?? true); final allowUpload = useState(existingLink?.allowUpload ?? false); @@ -48,20 +41,11 @@ class SharedLinkEditPage extends HookConsumerWidget { if (existingLink!.type == SharedLinkSource.album) { return Row( children: [ - const Text( - 'public_album', - style: TextStyle(fontWeight: FontWeight.bold), - ).tr(), - const Text( - " | ", - style: TextStyle(fontWeight: FontWeight.bold), - ), + const Text('public_album', style: TextStyle(fontWeight: FontWeight.bold)).tr(), + const Text(" | ", style: TextStyle(fontWeight: FontWeight.bold)), Text( existingLink!.title, - style: TextStyle( - color: colorScheme.primary, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: colorScheme.primary, fontWeight: FontWeight.bold), ), ], ); @@ -70,21 +54,12 @@ class SharedLinkEditPage extends HookConsumerWidget { if (existingLink!.type == SharedLinkSource.individual) { return Row( children: [ - const Text( - 'shared_link_individual_shared', - style: TextStyle(fontWeight: FontWeight.bold), - ).tr(), - const Text( - " | ", - style: TextStyle(fontWeight: FontWeight.bold), - ), + const Text('shared_link_individual_shared', style: TextStyle(fontWeight: FontWeight.bold)).tr(), + const Text(" | ", style: TextStyle(fontWeight: FontWeight.bold)), Expanded( child: Text( existingLink!.description ?? "--", - style: TextStyle( - color: colorScheme.primary, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: colorScheme.primary, fontWeight: FontWeight.bold), overflow: TextOverflow.ellipsis, ), ), @@ -93,10 +68,7 @@ class SharedLinkEditPage extends HookConsumerWidget { } } - return const Text( - "create_link_to_share_description", - style: TextStyle(fontWeight: FontWeight.bold), - ).tr(); + return const Text("create_link_to_share_description", style: TextStyle(fontWeight: FontWeight.bold)).tr(); } Widget buildDescriptionField() { @@ -108,20 +80,12 @@ class SharedLinkEditPage extends HookConsumerWidget { autofocus: false, decoration: InputDecoration( labelText: 'description'.tr(), - labelStyle: TextStyle( - fontWeight: FontWeight.bold, - color: colorScheme.primary, - ), + labelStyle: TextStyle(fontWeight: FontWeight.bold, color: colorScheme.primary), floatingLabelBehavior: FloatingLabelBehavior.always, border: const OutlineInputBorder(), hintText: 'shared_link_edit_description_hint'.tr(), - hintStyle: const TextStyle( - fontWeight: FontWeight.normal, - fontSize: 14, - ), - disabledBorder: OutlineInputBorder( - borderSide: BorderSide(color: Colors.grey.withValues(alpha: 0.5)), - ), + hintStyle: const TextStyle(fontWeight: FontWeight.normal, fontSize: 14), + disabledBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.grey.withValues(alpha: 0.5))), ), onTapOutside: (_) => descriptionFocusNode.unfocus(), ); @@ -134,20 +98,12 @@ class SharedLinkEditPage extends HookConsumerWidget { autofocus: false, decoration: InputDecoration( labelText: 'password'.tr(), - labelStyle: TextStyle( - fontWeight: FontWeight.bold, - color: colorScheme.primary, - ), + labelStyle: TextStyle(fontWeight: FontWeight.bold, color: colorScheme.primary), floatingLabelBehavior: FloatingLabelBehavior.always, border: const OutlineInputBorder(), hintText: 'shared_link_edit_password_hint'.tr(), - hintStyle: const TextStyle( - fontWeight: FontWeight.normal, - fontSize: 14, - ), - disabledBorder: OutlineInputBorder( - borderSide: BorderSide(color: Colors.grey.withValues(alpha: 0.5)), - ), + hintStyle: const TextStyle(fontWeight: FontWeight.normal, fontSize: 14), + disabledBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.grey.withValues(alpha: 0.5))), ), ); } @@ -155,31 +111,22 @@ class SharedLinkEditPage extends HookConsumerWidget { Widget buildShowMetaButton() { return SwitchListTile.adaptive( value: showMetadata.value, - onChanged: newShareLink.value.isEmpty - ? (value) => showMetadata.value = value - : null, + onChanged: newShareLink.value.isEmpty ? (value) => showMetadata.value = value : null, activeColor: colorScheme.primary, dense: true, - title: Text( - "show_metadata", - style: themeData.textTheme.labelLarge - ?.copyWith(fontWeight: FontWeight.bold), - ).tr(), + title: Text("show_metadata", style: themeData.textTheme.labelLarge?.copyWith(fontWeight: FontWeight.bold)).tr(), ); } Widget buildAllowDownloadButton() { return SwitchListTile.adaptive( value: allowDownload.value, - onChanged: newShareLink.value.isEmpty - ? (value) => allowDownload.value = value - : null, + onChanged: newShareLink.value.isEmpty ? (value) => allowDownload.value = value : null, activeColor: colorScheme.primary, dense: true, title: Text( "allow_public_user_to_download", - style: themeData.textTheme.labelLarge - ?.copyWith(fontWeight: FontWeight.bold), + style: themeData.textTheme.labelLarge?.copyWith(fontWeight: FontWeight.bold), ).tr(), ); } @@ -187,15 +134,12 @@ class SharedLinkEditPage extends HookConsumerWidget { Widget buildAllowUploadButton() { return SwitchListTile.adaptive( value: allowUpload.value, - onChanged: newShareLink.value.isEmpty - ? (value) => allowUpload.value = value - : null, + onChanged: newShareLink.value.isEmpty ? (value) => allowUpload.value = value : null, activeColor: colorScheme.primary, dense: true, title: Text( "allow_public_user_to_upload", - style: themeData.textTheme.labelLarge - ?.copyWith(fontWeight: FontWeight.bold), + style: themeData.textTheme.labelLarge?.copyWith(fontWeight: FontWeight.bold), ).tr(), ); } @@ -203,15 +147,12 @@ class SharedLinkEditPage extends HookConsumerWidget { Widget buildEditExpiryButton() { return SwitchListTile.adaptive( value: editExpiry.value, - onChanged: newShareLink.value.isEmpty - ? (value) => editExpiry.value = value - : null, + onChanged: newShareLink.value.isEmpty ? (value) => editExpiry.value = value : null, activeColor: colorScheme.primary, dense: true, title: Text( "change_expiration_time", - style: themeData.textTheme.labelLarge - ?.copyWith(fontWeight: FontWeight.bold), + style: themeData.textTheme.labelLarge?.copyWith(fontWeight: FontWeight.bold), ).tr(), ); } @@ -220,62 +161,43 @@ class SharedLinkEditPage extends HookConsumerWidget { return DropdownMenu( label: Text( "expire_after", - style: TextStyle( - fontWeight: FontWeight.bold, - color: colorScheme.primary, - ), + style: TextStyle(fontWeight: FontWeight.bold, color: colorScheme.primary), ).tr(), enableSearch: false, enableFilter: false, width: context.width - 40, initialSelection: expiryAfter.value, - enabled: newShareLink.value.isEmpty && - (existingLink == null || editExpiry.value), + enabled: newShareLink.value.isEmpty && (existingLink == null || editExpiry.value), onSelected: (value) { expiryAfter.value = value!; }, dropdownMenuEntries: [ - DropdownMenuEntry( - value: 0, - label: "never".tr(), - ), + DropdownMenuEntry(value: 0, label: "never".tr()), DropdownMenuEntry( value: 30, - label: "shared_link_edit_expire_after_option_minutes" - .tr(namedArgs: {'count': "30"}), - ), - DropdownMenuEntry( - value: 60, - label: "shared_link_edit_expire_after_option_hour".tr(), + label: "shared_link_edit_expire_after_option_minutes".tr(namedArgs: {'count': "30"}), ), + DropdownMenuEntry(value: 60, label: "shared_link_edit_expire_after_option_hour".tr()), DropdownMenuEntry( value: 60 * 6, - label: "shared_link_edit_expire_after_option_hours" - .tr(namedArgs: {'count': "6"}), - ), - DropdownMenuEntry( - value: 60 * 24, - label: "shared_link_edit_expire_after_option_day".tr(), + label: "shared_link_edit_expire_after_option_hours".tr(namedArgs: {'count': "6"}), ), + DropdownMenuEntry(value: 60 * 24, label: "shared_link_edit_expire_after_option_day".tr()), DropdownMenuEntry( value: 60 * 24 * 7, - label: "shared_link_edit_expire_after_option_days" - .tr(namedArgs: {'count': "7"}), + label: "shared_link_edit_expire_after_option_days".tr(namedArgs: {'count': "7"}), ), DropdownMenuEntry( value: 60 * 24 * 30, - label: "shared_link_edit_expire_after_option_days" - .tr(namedArgs: {'count': "30"}), + label: "shared_link_edit_expire_after_option_days".tr(namedArgs: {'count': "30"}), ), DropdownMenuEntry( value: 60 * 24 * 30 * 3, - label: "shared_link_edit_expire_after_option_months" - .tr(namedArgs: {'count': "3"}), + label: "shared_link_edit_expire_after_option_months".tr(namedArgs: {'count': "3"}), ), DropdownMenuEntry( value: 60 * 24 * 30 * 12, - label: "shared_link_edit_expire_after_option_year" - .tr(namedArgs: {'count': "1"}), + label: "shared_link_edit_expire_after_option_year".tr(namedArgs: {'count': "1"}), ), ], ); @@ -287,9 +209,7 @@ class SharedLinkEditPage extends HookConsumerWidget { SnackBar( content: Text( "shared_link_clipboard_copied_massage", - style: context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), ).tr(), duration: const Duration(seconds: 2), ), @@ -300,23 +220,14 @@ class SharedLinkEditPage extends HookConsumerWidget { Widget buildNewLinkField() { return Column( children: [ - const Padding( - padding: EdgeInsets.only( - top: 20, - bottom: 20, - ), - child: Divider(), - ), + const Padding(padding: EdgeInsets.only(top: 20, bottom: 20), child: Divider()), TextFormField( readOnly: true, initialValue: newShareLink.value, decoration: InputDecoration( border: const OutlineInputBorder(), enabledBorder: themeData.inputDecorationTheme.focusedBorder, - suffixIcon: IconButton( - onPressed: copyLinkToClipboard, - icon: const Icon(Icons.copy), - ), + suffixIcon: IconButton(onPressed: copyLinkToClipboard, icon: const Icon(Icons.copy)), ), ), Padding( @@ -327,13 +238,7 @@ class SharedLinkEditPage extends HookConsumerWidget { onPressed: () { context.maybePop(); }, - child: const Text( - "done", - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), - ).tr(), + child: const Text("done", style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)).tr(), ), ), ), @@ -346,27 +251,21 @@ class SharedLinkEditPage extends HookConsumerWidget { } Future handleNewLink() async { - final newLink = - await ref.read(sharedLinkServiceProvider).createSharedLink( - albumId: albumId, - assetIds: assetsList, - showMeta: showMetadata.value, - allowDownload: allowDownload.value, - allowUpload: allowUpload.value, - description: descriptionController.text.isEmpty - ? null - : descriptionController.text, - password: passwordController.text.isEmpty - ? null - : passwordController.text, - expiresAt: expiryAfter.value == 0 ? null : calculateExpiry(), - ); + final newLink = await ref + .read(sharedLinkServiceProvider) + .createSharedLink( + albumId: albumId, + assetIds: assetsList, + showMeta: showMetadata.value, + allowDownload: allowDownload.value, + allowUpload: allowUpload.value, + description: descriptionController.text.isEmpty ? null : descriptionController.text, + password: passwordController.text.isEmpty ? null : passwordController.text, + expiresAt: expiryAfter.value == 0 ? null : calculateExpiry(), + ); ref.invalidate(sharedLinksStateProvider); - final externalDomain = ref.read( - serverInfoProvider.select((s) => s.serverConfig.externalDomain), - ); - var serverUrl = - externalDomain.isNotEmpty ? externalDomain : getServerUrl(); + final externalDomain = ref.read(serverInfoProvider.select((s) => s.serverConfig.externalDomain)); + var serverUrl = externalDomain.isNotEmpty ? externalDomain : getServerUrl(); if (serverUrl != null && !serverUrl.endsWith('/')) { serverUrl += '/'; } @@ -417,7 +316,9 @@ class SharedLinkEditPage extends HookConsumerWidget { changeExpiry = true; } - await ref.read(sharedLinkServiceProvider).updateSharedLink( + await ref + .read(sharedLinkServiceProvider) + .updateSharedLink( existingLink!.id, showMeta: meta, allowDownload: download, @@ -433,9 +334,7 @@ class SharedLinkEditPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( - title: Text( - existingLink == null ? "create_link_to_share" : "edit_link", - ).tr(), + title: Text(existingLink == null ? "create_link_to_share" : "edit_link").tr(), elevation: 0, leading: const CloseButton(), centerTitle: false, @@ -443,86 +342,47 @@ class SharedLinkEditPage extends HookConsumerWidget { body: SafeArea( child: ListView( children: [ + Padding(padding: const EdgeInsets.all(padding), child: buildLinkTitle()), + Padding(padding: const EdgeInsets.all(padding), child: buildDescriptionField()), + Padding(padding: const EdgeInsets.all(padding), child: buildPasswordField()), Padding( - padding: const EdgeInsets.all(padding), - child: buildLinkTitle(), - ), - Padding( - padding: const EdgeInsets.all(padding), - child: buildDescriptionField(), - ), - Padding( - padding: const EdgeInsets.all(padding), - child: buildPasswordField(), - ), - Padding( - padding: const EdgeInsets.only( - left: padding, - right: padding, - bottom: padding, - ), + padding: const EdgeInsets.only(left: padding, right: padding, bottom: padding), child: buildShowMetaButton(), ), Padding( - padding: const EdgeInsets.only( - left: padding, - right: padding, - bottom: padding, - ), + padding: const EdgeInsets.only(left: padding, right: padding, bottom: padding), child: buildAllowDownloadButton(), ), Padding( - padding: - const EdgeInsets.only(left: padding, right: 20, bottom: 20), + padding: const EdgeInsets.only(left: padding, right: 20, bottom: 20), child: buildAllowUploadButton(), ), if (existingLink != null) Padding( - padding: const EdgeInsets.only( - left: padding, - right: padding, - bottom: padding, - ), + padding: const EdgeInsets.only(left: padding, right: padding, bottom: padding), child: buildEditExpiryButton(), ), Padding( - padding: const EdgeInsets.only( - left: padding, - right: padding, - bottom: padding, - ), + padding: const EdgeInsets.only(left: padding, right: padding, bottom: padding), child: buildExpiryAfterButton(), ), if (newShareLink.value.isEmpty) Align( alignment: Alignment.bottomRight, child: Padding( - padding: const EdgeInsets.only( - right: padding + 10, - bottom: padding, - ), + padding: const EdgeInsets.only(right: padding + 10, bottom: padding), child: ElevatedButton( - onPressed: - existingLink != null ? handleEditLink : handleNewLink, + onPressed: existingLink != null ? handleEditLink : handleNewLink, child: Text( - existingLink != null - ? "shared_link_edit_submit_button" - : "create_link", - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), + existingLink != null ? "shared_link_edit_submit_button" : "create_link", + style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold), ).tr(), ), ), ), if (newShareLink.value.isNotEmpty) Padding( - padding: const EdgeInsets.only( - left: padding, - right: padding, - bottom: padding, - ), + padding: const EdgeInsets.only(left: padding, right: padding, bottom: padding), child: buildNewLinkField(), ), ], diff --git a/mobile/lib/pages/library/trash.page.dart b/mobile/lib/pages/library/trash.page.dart index c645719974..2279998c2d 100644 --- a/mobile/lib/pages/library/trash.page.dart +++ b/mobile/lib/pages/library/trash.page.dart @@ -24,16 +24,12 @@ class TrashPage extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final trashRenderList = ref.watch(trashTimelineProvider); - final trashDays = - ref.watch(serverInfoProvider.select((v) => v.serverConfig.trashDays)); + final trashDays = ref.watch(serverInfoProvider.select((v) => v.serverConfig.trashDays)); final selectionEnabledHook = useState(false); final selection = useState({}); final processing = useProcessingOverlay(); - void selectionListener( - bool multiselect, - Set selectedAssets, - ) { + void selectionListener(bool multiselect, Set selectedAssets) { selectionEnabledHook.value = multiselect; selection.value = selectedAssets; } @@ -44,11 +40,7 @@ class TrashPage extends HookConsumerWidget { processing.value = false; selectionEnabledHook.value = false; if (context.mounted) { - ImmichToast.show( - context: context, - msg: 'trash_emptied'.tr(), - gravity: ToastGravity.BOTTOM, - ); + ImmichToast.show(context: context, msg: 'trash_emptied'.tr(), gravity: ToastGravity.BOTTOM); } } @@ -68,16 +60,13 @@ class TrashPage extends HookConsumerWidget { processing.value = true; try { if (selection.value.isNotEmpty) { - final isRemoved = await ref - .read(assetProvider.notifier) - .deleteAssets(selection.value, force: true); + final isRemoved = await ref.read(assetProvider.notifier).deleteAssets(selection.value, force: true); if (isRemoved) { if (context.mounted) { ImmichToast.show( context: context, - msg: 'assets_deleted_permanently' - .tr(namedArgs: {'count': "${selection.value.length}"}), + msg: 'assets_deleted_permanently'.tr(namedArgs: {'count': "${selection.value.length}"}), gravity: ToastGravity.BOTTOM, ); } @@ -92,10 +81,7 @@ class TrashPage extends HookConsumerWidget { handlePermanentDelete() async { await showDialog( context: context, - builder: (context) => DeleteDialog( - alert: "delete_dialog_alert_remote", - onDelete: () => onPermanentlyDelete(), - ), + builder: (context) => DeleteDialog(alert: "delete_dialog_alert_remote", onDelete: () => onPermanentlyDelete()), ); } @@ -110,15 +96,12 @@ class TrashPage extends HookConsumerWidget { processing.value = true; try { if (selection.value.isNotEmpty) { - final result = await ref - .read(trashProvider.notifier) - .restoreAssets(selection.value); + final result = await ref.read(trashProvider.notifier).restoreAssets(selection.value); if (result && context.mounted) { ImmichToast.show( context: context, - msg: 'assets_restored_successfully' - .tr(namedArgs: {'count': "${selection.value.length}"}), + msg: 'assets_restored_successfully'.tr(namedArgs: {'count': "${selection.value.length}"}), gravity: ToastGravity.BOTTOM, ); } @@ -131,9 +114,7 @@ class TrashPage extends HookConsumerWidget { String getAppBarTitle(String count) { if (selectionEnabledHook.value) { - return selection.value.isNotEmpty - ? "${selection.value.length}" - : "trash_page_select_assets_btn".tr(); + return selection.value.isNotEmpty ? "${selection.value.length}" : "trash_page_select_assets_btn".tr(); } return 'trash_page_title'.tr(namedArgs: {'count': count}); } @@ -159,14 +140,8 @@ class TrashPage extends HookConsumerWidget { PopupMenuButton( itemBuilder: (context) { return [ - PopupMenuItem( - value: () => selectionEnabledHook.value = true, - child: const Text('select').tr(), - ), - PopupMenuItem( - value: handleEmptyTrash, - child: const Text('empty_trash').tr(), - ), + PopupMenuItem(value: () => selectionEnabledHook.value = true, child: const Text('select').tr()), + PopupMenuItem(value: handleEmptyTrash, child: const Text('empty_trash').tr()), ]; }, onSelected: (fn) => fn(), @@ -187,44 +162,28 @@ class TrashPage extends HookConsumerWidget { mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ TextButton.icon( - icon: Icon( - Icons.delete_forever, - color: Colors.red[400], - ), + icon: Icon(Icons.delete_forever, color: Colors.red[400]), label: Text( - selection.value.isEmpty - ? 'trash_page_delete_all'.tr() - : 'delete'.tr(), - style: TextStyle( - fontSize: 14, - color: Colors.red[400], - fontWeight: FontWeight.bold, - ), + selection.value.isEmpty ? 'trash_page_delete_all'.tr() : 'delete'.tr(), + style: TextStyle(fontSize: 14, color: Colors.red[400], fontWeight: FontWeight.bold), ), onPressed: processing.value ? null : selection.value.isEmpty - ? handleEmptyTrash - : handlePermanentDelete, + ? handleEmptyTrash + : handlePermanentDelete, ), TextButton.icon( - icon: const Icon( - Icons.history_rounded, - ), + icon: const Icon(Icons.history_rounded), label: Text( - selection.value.isEmpty - ? 'trash_page_restore_all'.tr() - : 'restore'.tr(), - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), + selection.value.isEmpty ? 'trash_page_restore_all'.tr() : 'restore'.tr(), + style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold), ), onPressed: processing.value ? null : selection.value.isEmpty - ? handleRestoreAll - : handleRestore, + ? handleRestoreAll + : handleRestore, ), ], ), @@ -241,9 +200,7 @@ class TrashPage extends HookConsumerWidget { ), body: trashRenderList.widgetWhen( onData: (data) => data.isEmpty - ? Center( - child: Text('trash_page_no_assets'.tr()), - ) + ? Center(child: Text('trash_page_no_assets'.tr())) : Stack( children: [ SafeArea( @@ -254,13 +211,8 @@ class TrashPage extends HookConsumerWidget { showMultiSelectIndicator: false, showStack: true, topWidget: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 12, - vertical: 24, - ), - child: const Text( - "trash_page_info", - ).tr(namedArgs: {"days": "$trashDays"}), + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 24), + child: const Text("trash_page_info").tr(namedArgs: {"days": "$trashDays"}), ), ), ), diff --git a/mobile/lib/pages/login/change_password.page.dart b/mobile/lib/pages/login/change_password.page.dart index b05397ee38..248526df1b 100644 --- a/mobile/lib/pages/login/change_password.page.dart +++ b/mobile/lib/pages/login/change_password.page.dart @@ -9,8 +9,6 @@ class ChangePasswordPage extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - return const Scaffold( - body: ChangePasswordForm(), - ); + return const Scaffold(body: ChangePasswordForm()); } } diff --git a/mobile/lib/pages/login/login.page.dart b/mobile/lib/pages/login/login.page.dart index 8045ae649f..e1d551900f 100644 --- a/mobile/lib/pages/login/login.page.dart +++ b/mobile/lib/pages/login/login.page.dart @@ -21,12 +21,10 @@ class LoginPage extends HookConsumerWidget { appVersion.value = packageInfo.version; } - useEffect( - () { - getAppInfo(); - return null; - }, - ); + useEffect(() { + getAppInfo(); + return null; + }); return Scaffold( body: LoginForm(), diff --git a/mobile/lib/pages/onboarding/permission_onboarding.page.dart b/mobile/lib/pages/onboarding/permission_onboarding.page.dart index b0a1b34b06..52d4ac0125 100644 --- a/mobile/lib/pages/onboarding/permission_onboarding.page.dart +++ b/mobile/lib/pages/onboarding/permission_onboarding.page.dart @@ -26,26 +26,18 @@ class PermissionOnboardingPage extends HookConsumerWidget { crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ - Text( - 'permission_onboarding_request', - style: context.textTheme.titleMedium, - textAlign: TextAlign.center, - ).tr(), + Text('permission_onboarding_request', style: context.textTheme.titleMedium, textAlign: TextAlign.center).tr(), const SizedBox(height: 18), ElevatedButton( - onPressed: () => ref - .read(galleryPermissionNotifier.notifier) - .requestGalleryPermission() - .then((permission) async { - if (permission.isGranted) { - // If permission is limited, we will show the limited - // permission page - goToBackup(); - } - }), - child: const Text( - 'continue', - ).tr(), + onPressed: () => + ref.read(galleryPermissionNotifier.notifier).requestGalleryPermission().then((permission) async { + if (permission.isGranted) { + // If permission is limited, we will show the limited + // permission page + goToBackup(); + } + }), + child: const Text('continue').tr(), ), ], ); @@ -64,10 +56,7 @@ class PermissionOnboardingPage extends HookConsumerWidget { textAlign: TextAlign.center, ).tr(), const SizedBox(height: 18), - ElevatedButton( - onPressed: () => goToBackup(), - child: const Text('permission_onboarding_get_started').tr(), - ), + ElevatedButton(onPressed: () => goToBackup(), child: const Text('permission_onboarding_get_started').tr()), ], ); } @@ -80,11 +69,7 @@ class PermissionOnboardingPage extends HookConsumerWidget { crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ - const Icon( - Icons.warning_outlined, - color: Colors.yellow, - size: 48, - ), + const Icon(Icons.warning_outlined, color: Colors.yellow, size: 48), const SizedBox(height: 8), Text( 'permission_onboarding_permission_limited', @@ -94,17 +79,10 @@ class PermissionOnboardingPage extends HookConsumerWidget { const SizedBox(height: 18), ElevatedButton( onPressed: () => openAppSettings(), - child: const Text( - 'permission_onboarding_go_to_settings', - ).tr(), + child: const Text('permission_onboarding_go_to_settings').tr(), ), const SizedBox(height: 8.0), - TextButton( - onPressed: () => goToBackup(), - child: const Text( - 'permission_onboarding_continue_anyway', - ).tr(), - ), + TextButton(onPressed: () => goToBackup(), child: const Text('permission_onboarding_continue_anyway').tr()), ], ); } @@ -114,11 +92,7 @@ class PermissionOnboardingPage extends HookConsumerWidget { crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ - const Icon( - Icons.warning_outlined, - color: Colors.red, - size: 48, - ), + const Icon(Icons.warning_outlined, color: Colors.red, size: 48), const SizedBox(height: 8), Text( 'permission_onboarding_permission_denied', @@ -128,9 +102,7 @@ class PermissionOnboardingPage extends HookConsumerWidget { const SizedBox(height: 18), ElevatedButton( onPressed: () => openAppSettings(), - child: const Text( - 'permission_onboarding_go_to_settings', - ).tr(), + child: const Text('permission_onboarding_go_to_settings').tr(), ), ], ); @@ -139,12 +111,8 @@ class PermissionOnboardingPage extends HookConsumerWidget { final Widget child = switch (permission) { PermissionStatus.limited => buildPermissionLimited(), PermissionStatus.denied => buildRequestPermission(), - PermissionStatus.granted || - PermissionStatus.provisional => - buildPermissionGranted(), - PermissionStatus.restricted || - PermissionStatus.permanentlyDenied => - buildPermissionDenied() + PermissionStatus.granted || PermissionStatus.provisional => buildPermissionGranted(), + PermissionStatus.restricted || PermissionStatus.permanentlyDenied => buildPermissionDenied(), }; return Scaffold( @@ -156,21 +124,13 @@ class PermissionOnboardingPage extends HookConsumerWidget { crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ - const ImmichLogo( - heroTag: 'logo', - ), + const ImmichLogo(heroTag: 'logo'), const ImmichTitleText(), AnimatedSwitcher( duration: const Duration(milliseconds: 500), - child: Padding( - padding: const EdgeInsets.all(18.0), - child: child, - ), - ), - TextButton( - child: const Text('back').tr(), - onPressed: () => context.maybePop(), + child: Padding(padding: const EdgeInsets.all(18.0), child: child), ), + TextButton(child: const Text('back').tr(), onPressed: () => context.maybePop()), ], ), ), diff --git a/mobile/lib/pages/photos/memory.page.dart b/mobile/lib/pages/photos/memory.page.dart index 211472f27a..20bd32a171 100644 --- a/mobile/lib/pages/photos/memory.page.dart +++ b/mobile/lib/pages/photos/memory.page.dart @@ -16,32 +16,24 @@ import 'package:immich_mobile/widgets/memories/memory_epilogue.dart'; import 'package:immich_mobile/widgets/memories/memory_progress_indicator.dart'; @RoutePage() - /// Expects [currentAssetProvider] to be set before navigating to this page class MemoryPage extends HookConsumerWidget { final List memories; final int memoryIndex; - const MemoryPage({ - required this.memories, - required this.memoryIndex, - super.key, - }); + const MemoryPage({required this.memories, required this.memoryIndex, super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final currentMemory = useState(memories[memoryIndex]); final currentAssetPage = useState(0); final currentMemoryIndex = useState(memoryIndex); - final assetProgress = useState( - "${currentAssetPage.value + 1}|${currentMemory.value.assets.length}", - ); + final assetProgress = useState("${currentAssetPage.value + 1}|${currentMemory.value.assets.length}"); const bgColor = Colors.black; final currentAsset = useState(null); /// The list of all of the asset page controllers - final memoryAssetPageControllers = - List.generate(memories.length, (i) => usePageController()); + final memoryAssetPageControllers = List.generate(memories.length, (i) => usePageController()); /// The main vertically scrolling page controller with each list of memories final memoryPageController = usePageController(initialPage: memoryIndex); @@ -56,36 +48,27 @@ class MemoryPage extends HookConsumerWidget { }); toNextMemory() { - memoryPageController.nextPage( - duration: const Duration(milliseconds: 500), - curve: Curves.easeIn, - ); + memoryPageController.nextPage(duration: const Duration(milliseconds: 500), curve: Curves.easeIn); } void toPreviousMemory() { if (currentMemoryIndex.value > 0) { // Move to the previous memory page - memoryPageController.previousPage( - duration: const Duration(milliseconds: 500), - curve: Curves.easeIn, - ); + memoryPageController.previousPage(duration: const Duration(milliseconds: 500), curve: Curves.easeIn); // Wait for the next frame to ensure the page is built SchedulerBinding.instance.addPostFrameCallback((_) { final previousIndex = currentMemoryIndex.value - 1; - final previousMemoryController = - memoryAssetPageControllers[previousIndex]; + final previousMemoryController = memoryAssetPageControllers[previousIndex]; // Ensure the controller is attached if (previousMemoryController.hasClients) { - previousMemoryController - .jumpToPage(memories[previousIndex].assets.length - 1); + previousMemoryController.jumpToPage(memories[previousIndex].assets.length - 1); } else { // Wait for the next frame until it is attached SchedulerBinding.instance.addPostFrameCallback((_) { if (previousMemoryController.hasClients) { - previousMemoryController - .jumpToPage(memories[previousIndex].assets.length - 1); + previousMemoryController.jumpToPage(memories[previousIndex].assets.length - 1); } }); } @@ -96,13 +79,9 @@ class MemoryPage extends HookConsumerWidget { toNextAsset(int currentAssetIndex) { if (currentAssetIndex + 1 < currentMemory.value.assets.length) { // Go to the next asset - PageController controller = - memoryAssetPageControllers[currentMemoryIndex.value]; + PageController controller = memoryAssetPageControllers[currentMemoryIndex.value]; - controller.nextPage( - curve: Curves.easeInOut, - duration: const Duration(milliseconds: 500), - ); + controller.nextPage(curve: Curves.easeInOut, duration: const Duration(milliseconds: 500)); } else { // Go to the next memory since we are at the end of our assets toNextMemory(); @@ -112,13 +91,9 @@ class MemoryPage extends HookConsumerWidget { toPreviousAsset(int currentAssetIndex) { if (currentAssetIndex > 0) { // Go to the previous asset - PageController controller = - memoryAssetPageControllers[currentMemoryIndex.value]; + PageController controller = memoryAssetPageControllers[currentMemoryIndex.value]; - controller.previousPage( - curve: Curves.easeInOut, - duration: const Duration(milliseconds: 500), - ); + controller.previousPage(curve: Curves.easeInOut, duration: const Duration(milliseconds: 500)); } else { // Go to the previous memory since we are at the end of our assets toPreviousMemory(); @@ -126,8 +101,7 @@ class MemoryPage extends HookConsumerWidget { } updateProgressText() { - assetProgress.value = - "${currentAssetPage.value + 1}|${currentMemory.value.assets.length}"; + assetProgress.value = "${currentAssetPage.value + 1}|${currentMemory.value.assets.length}"; } /// Downloads and caches the image for the asset at this [currentMemory]'s index @@ -168,11 +142,7 @@ class MemoryPage extends HookConsumerWidget { // Precache the asset final size = MediaQuery.sizeOf(context); await precacheImage( - ImmichImage.imageProvider( - asset: asset, - width: size.width, - height: size.height, - ), + ImmichImage.imageProvider(asset: asset, width: size.width, height: size.height), context, size: size, ); @@ -180,8 +150,7 @@ class MemoryPage extends HookConsumerWidget { // Precache the next page right away if we are on the first page if (currentAssetPage.value == 0) { - Future.delayed(const Duration(milliseconds: 200)) - .then((_) => precacheAsset(1)); + Future.delayed(const Duration(milliseconds: 200)).then((_) => precacheAsset(1)); } Future onAssetChanged(int otherIndex) async { @@ -212,12 +181,10 @@ class MemoryPage extends HookConsumerWidget { // maxScrollExtend contains the sum of horizontal pixels of all assets for depth = 1 // or sum of vertical pixels of all memories for depth = 0 if (notification is ScrollUpdateNotification) { - final isEpiloguePage = - (memoryPageController.page?.floor() ?? 0) >= memories.length; + final isEpiloguePage = (memoryPageController.page?.floor() ?? 0) >= memories.length; final offset = notification.metrics.pixels; - if (isEpiloguePage && - (offset > notification.metrics.maxScrollExtent + 150)) { + if (isEpiloguePage && (offset > notification.metrics.maxScrollExtent + 150)) { context.maybePop(); return true; } @@ -229,9 +196,7 @@ class MemoryPage extends HookConsumerWidget { backgroundColor: bgColor, body: SafeArea( child: PageView.builder( - physics: const BouncingScrollPhysics( - parent: AlwaysScrollableScrollPhysics(), - ), + physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), scrollDirection: Axis.vertical, controller: memoryPageController, onPageChanged: (pageNumber) { @@ -262,12 +227,7 @@ class MemoryPage extends HookConsumerWidget { return Column( children: [ Padding( - padding: const EdgeInsets.only( - left: 24.0, - right: 24.0, - top: 8.0, - bottom: 2.0, - ), + padding: const EdgeInsets.only(left: 24.0, right: 24.0, top: 8.0, bottom: 2.0), child: AnimatedBuilder( animation: assetController, builder: (context, child) { @@ -287,9 +247,7 @@ class MemoryPage extends HookConsumerWidget { child: Stack( children: [ PageView.builder( - physics: const BouncingScrollPhysics( - parent: AlwaysScrollableScrollPhysics(), - ), + physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), controller: assetController, onPageChanged: onAssetChanged, scrollDirection: Axis.horizontal, @@ -300,11 +258,7 @@ class MemoryPage extends HookConsumerWidget { children: [ Container( color: Colors.black, - child: MemoryCard( - asset: asset, - title: memories[mIndex].title, - showTitle: index == 0, - ), + child: MemoryCard(asset: asset, title: memories[mIndex].title, showTitle: index == 0), ), Positioned.fill( child: Row( @@ -345,28 +299,19 @@ class MemoryPage extends HookConsumerWidget { // turn off full screen mode here // https://github.com/Milad-Akarie/auto_route_library/issues/1799 context.maybePop(); - SystemChrome.setEnabledSystemUIMode( - SystemUiMode.edgeToEdge, - ); + SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); }, shape: const CircleBorder(), color: Colors.white.withValues(alpha: 0.2), elevation: 0, - child: const Icon( - Icons.close_rounded, - color: Colors.white, - ), + child: const Icon(Icons.close_rounded, color: Colors.white), ), ), - if (currentAsset.value != null && - currentAsset.value!.isVideo) + if (currentAsset.value != null && currentAsset.value!.isVideo) Positioned( bottom: 24, right: 32, - child: Icon( - Icons.videocam_outlined, - color: Colors.grey[200], - ), + child: Icon(Icons.videocam_outlined, color: Colors.grey[200]), ), ], ), diff --git a/mobile/lib/pages/photos/photos.page.dart b/mobile/lib/pages/photos/photos.page.dart index 62ac96c8aa..957c32b224 100644 --- a/mobile/lib/pages/photos/photos.page.dart +++ b/mobile/lib/pages/photos/photos.page.dart @@ -29,17 +29,14 @@ class PhotosPage extends HookConsumerWidget { final tipOneOpacity = useState(0.0); final refreshCount = useState(0); - useEffect( - () { - ref.read(websocketProvider.notifier).connect(); - Future(() => ref.read(assetProvider.notifier).getAllAsset()); - Future(() => ref.read(albumProvider.notifier).refreshRemoteAlbums()); - ref.read(serverInfoProvider.notifier).getServerInfo(); + useEffect(() { + ref.read(websocketProvider.notifier).connect(); + Future(() => ref.read(assetProvider.notifier).getAllAsset()); + Future(() => ref.read(albumProvider.notifier).refreshRemoteAlbums()); + ref.read(serverInfoProvider.notifier).getServerInfo(); - return; - }, - [], - ); + return; + }, []); Widget buildLoadingIndicator() { Timer(const Duration(seconds: 2), () => tipOneOpacity.value = 1); @@ -53,9 +50,7 @@ class PhotosPage extends HookConsumerWidget { padding: const EdgeInsets.only(top: 16.0), child: Text( 'home_page_building_timeline', - style: context.textTheme.titleMedium?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.titleMedium?.copyWith(color: context.primaryColor), ).tr(), ), const SizedBox(height: 8), @@ -106,9 +101,7 @@ class PhotosPage extends HookConsumerWidget { return Stack( children: [ MultiselectGrid( - topWidget: (currentUser != null && currentUser.memoryEnabled) - ? const MemoryLane() - : const SizedBox(), + topWidget: (currentUser != null && currentUser.memoryEnabled) ? const MemoryLane() : const SizedBox(), renderListProvider: timelineUsers.length > 1 ? multiUsersTimelineProvider(timelineUsers) : singleUserTimelineProvider(currentUser?.id), @@ -120,9 +113,7 @@ class PhotosPage extends HookConsumerWidget { ), AnimatedPositioned( duration: const Duration(milliseconds: 300), - top: ref.watch(multiselectProvider) - ? -(kToolbarHeight + context.padding.top) - : 0, + top: ref.watch(multiselectProvider) ? -(kToolbarHeight + context.padding.top) : 0, left: 0, right: 0, child: Container( diff --git a/mobile/lib/pages/search/all_motion_videos.page.dart b/mobile/lib/pages/search/all_motion_videos.page.dart index 5d1081db33..60bb8a6cff 100644 --- a/mobile/lib/pages/search/all_motion_videos.page.dart +++ b/mobile/lib/pages/search/all_motion_videos.page.dart @@ -17,16 +17,9 @@ class AllMotionPhotosPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( title: const Text('search_page_motion_photos').tr(), - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), - ), - body: motionPhotos.widgetWhen( - onData: (assets) => ImmichAssetGrid( - assets: assets, - ), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_rounded)), ), + body: motionPhotos.widgetWhen(onData: (assets) => ImmichAssetGrid(assets: assets)), ); } } diff --git a/mobile/lib/pages/search/all_people.page.dart b/mobile/lib/pages/search/all_people.page.dart index 7e2a136721..b2814e6c13 100644 --- a/mobile/lib/pages/search/all_people.page.dart +++ b/mobile/lib/pages/search/all_people.page.dart @@ -17,20 +17,13 @@ class AllPeoplePage extends HookConsumerWidget { return Scaffold( appBar: AppBar( - title: const Text( - 'people', - ).tr(), - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), + title: const Text('people').tr(), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_rounded)), ), body: curatedPeople.widgetWhen( onData: (people) => ExploreGrid( isPeople: true, - curatedContent: people - .map((e) => SearchCuratedContent(label: e.name, id: e.id)) - .toList(), + curatedContent: people.map((e) => SearchCuratedContent(label: e.name, id: e.id)).toList(), ), ), ); diff --git a/mobile/lib/pages/search/all_places.page.dart b/mobile/lib/pages/search/all_places.page.dart index 92521d13cf..c92f87d3ac 100644 --- a/mobile/lib/pages/search/all_places.page.dart +++ b/mobile/lib/pages/search/all_places.page.dart @@ -13,24 +13,14 @@ class AllPlacesPage extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - AsyncValue> places = - ref.watch(getAllPlacesProvider); + AsyncValue> places = ref.watch(getAllPlacesProvider); return Scaffold( appBar: AppBar( - title: const Text( - 'places', - ).tr(), - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), - ), - body: places.widgetWhen( - onData: (data) => ExploreGrid( - curatedContent: data, - ), + title: const Text('places').tr(), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_rounded)), ), + body: places.widgetWhen(onData: (data) => ExploreGrid(curatedContent: data)), ); } } diff --git a/mobile/lib/pages/search/all_videos.page.dart b/mobile/lib/pages/search/all_videos.page.dart index 6d123d9d7f..acad043a58 100644 --- a/mobile/lib/pages/search/all_videos.page.dart +++ b/mobile/lib/pages/search/all_videos.page.dart @@ -14,10 +14,7 @@ class AllVideosPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( title: const Text('videos').tr(), - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_rounded)), ), body: MultiselectGrid(renderListProvider: allVideosTimelineProvider), ); diff --git a/mobile/lib/pages/search/map/map.page.dart b/mobile/lib/pages/search/map/map.page.dart index 9f32b1877b..34522f0f04 100644 --- a/mobile/lib/pages/search/map/map.page.dart +++ b/mobile/lib/pages/search/map/map.page.dart @@ -48,8 +48,7 @@ class MapPage extends HookConsumerWidget { final layerDebouncer = useDebouncer(interval: const Duration(seconds: 1)); final isLoading = useProcessingOverlay(); final scrollController = useScrollController(); - final markerDebouncer = - useDebouncer(interval: const Duration(milliseconds: 800)); + final markerDebouncer = useDebouncer(interval: const Duration(milliseconds: 800)); final selectedAssets = useValueNotifier>({}); const mapZoomToAssetLevel = 12.0; @@ -63,18 +62,11 @@ class MapPage extends HookConsumerWidget { final bounds = await mapController.value!.getVisibleRegion(); final inBounds = markers.value - .where( - (m) => - bounds.contains(LatLng(m.latLng.latitude, m.latLng.longitude)), - ) + .where((m) => bounds.contains(LatLng(m.latLng.latitude, m.latLng.longitude))) .toList(); // Notify bottom sheet to update asset grid only when there are new assets if (markersInBounds.value.length != inBounds.length) { - bottomSheetStreamController.add( - MapAssetsInBoundsUpdated( - inBounds.map((e) => e.assetRemoteId).toList(), - ), - ); + bottomSheetStreamController.add(MapAssetsInBoundsUpdated(inBounds.map((e) => e.assetRemoteId).toList())); } markersInBounds.value = inBounds; } @@ -82,9 +74,7 @@ class MapPage extends HookConsumerWidget { // removes all sources and layers and re-adds them with the updated markers Future reloadLayers() async { if (mapController.value != null) { - layerDebouncer.run( - () => mapController.value!.reloadAllLayersForMarkers(markers.value), - ); + layerDebouncer.run(() => mapController.value!.reloadAllLayersForMarkers(markers.value)); } } @@ -99,16 +89,12 @@ class MapPage extends HookConsumerWidget { } } - useEffect( - () { - final currentAssetLink = - ref.read(currentAssetProvider.notifier).ref.keepAlive(); + useEffect(() { + final currentAssetLink = ref.read(currentAssetProvider.notifier).ref.keepAlive(); - loadMarkers(); - return currentAssetLink.close; - }, - [], - ); + loadMarkers(); + return currentAssetLink.close; + }, []); // Refetch markers when map state is changed ref.listen(mapStateNotifierProvider, (_, current) { @@ -124,17 +110,9 @@ class MapPage extends HookConsumerWidget { }); // updates the selected markers position based on the current map camera - Future updateAssetMarkerPosition( - MapMarker marker, { - bool shouldAnimate = true, - }) async { - final assetPoint = - await mapController.value!.toScreenLocation(marker.latLng); - selectedMarker.value = _AssetMarkerMeta( - point: assetPoint, - marker: marker, - shouldAnimate: shouldAnimate, - ); + Future updateAssetMarkerPosition(MapMarker marker, {bool shouldAnimate = true}) async { + final assetPoint = await mapController.value!.toScreenLocation(marker.latLng); + selectedMarker.value = _AssetMarkerMeta(point: assetPoint, marker: marker, shouldAnimate: shouldAnimate); (assetPoint, marker, shouldAnimate); } @@ -144,11 +122,9 @@ class MapPage extends HookConsumerWidget { if (mapController.value == null) { return; } - final latlngBound = - await mapController.value!.getBoundsFromPoint(point, 50); + final latlngBound = await mapController.value!.getBoundsFromPoint(point, 50); final marker = markersInBounds.value.firstWhereOrNull( - (m) => - latlngBound.contains(LatLng(m.latLng.latitude, m.latLng.longitude)), + (m) => latlngBound.contains(LatLng(m.latLng.latitude, m.latLng.longitude)), ); if (marker != null) { @@ -166,10 +142,7 @@ class MapPage extends HookConsumerWidget { mapController.value = controller; controller.addListener(() { if (controller.isCameraMoving && selectedMarker.value != null) { - updateAssetMarkerPosition( - selectedMarker.value!.marker, - shouldAnimate: false, - ); + updateAssetMarkerPosition(selectedMarker.value!.marker, shouldAnimate: false); } }); } @@ -186,22 +159,13 @@ class MapPage extends HookConsumerWidget { } // Since we only have a single asset, we can just show GroupAssetBy.none - final renderList = await RenderList.fromAssets( - [asset], - GroupAssetsBy.none, - ); + final renderList = await RenderList.fromAssets([asset], GroupAssetsBy.none); ref.read(currentAssetProvider.notifier).set(asset); if (asset.isVideo) { ref.read(showControlsProvider.notifier).show = false; } - context.pushRoute( - GalleryViewerRoute( - initialIndex: 0, - heroOffset: 0, - renderList: renderList, - ), - ); + context.pushRoute(GalleryViewerRoute(initialIndex: 0, heroOffset: 0, renderList: renderList)); } /// BOTTOM SHEET CALLBACKS @@ -211,23 +175,18 @@ class MapPage extends HookConsumerWidget { } void onBottomSheetScrolled(String assetRemoteId) { - final assetMarker = markersInBounds.value - .firstWhereOrNull((m) => m.assetRemoteId == assetRemoteId); + final assetMarker = markersInBounds.value.firstWhereOrNull((m) => m.assetRemoteId == assetRemoteId); if (assetMarker != null) { updateAssetMarkerPosition(assetMarker); } } void onZoomToAsset(String assetRemoteId) { - final assetMarker = markersInBounds.value - .firstWhereOrNull((m) => m.assetRemoteId == assetRemoteId); + final assetMarker = markersInBounds.value.firstWhereOrNull((m) => m.assetRemoteId == assetRemoteId); if (mapController.value != null && assetMarker != null) { // Offset the latitude a little to show the marker just above the viewports center final offset = context.isMobile ? 0.02 : 0; - final latlng = LatLng( - assetMarker.latLng.latitude - offset, - assetMarker.latLng.longitude, - ); + final latlng = LatLng(assetMarker.latLng.latitude - offset, assetMarker.latLng.longitude); mapController.value!.animateCamera( CameraUpdate.newLatLngZoom(latlng, mapZoomToAssetLevel), duration: const Duration(milliseconds: 800), @@ -236,8 +195,7 @@ class MapPage extends HookConsumerWidget { } void onZoomToLocation() async { - final (location, error) = - await MapUtils.checkPermAndGetLocation(context: context); + final (location, error) = await MapUtils.checkPermAndGetLocation(context: context); if (error != null) { if (error == LocationPermission.unableToDetermine && context.mounted) { ImmichToast.show( @@ -252,10 +210,7 @@ class MapPage extends HookConsumerWidget { if (mapController.value != null && location != null) { mapController.value!.animateCamera( - CameraUpdate.newLatLngZoom( - LatLng(location.latitude, location.longitude), - mapZoomToAssetLevel, - ), + CameraUpdate.newLatLngZoom(LatLng(location.latitude, location.longitude), mapZoomToAssetLevel), duration: const Duration(milliseconds: 800), ); } @@ -320,9 +275,7 @@ class MapPage extends HookConsumerWidget { bottom: context.padding.bottom + 16, child: ElevatedButton( onPressed: onZoomToLocation, - style: ElevatedButton.styleFrom( - shape: const CircleBorder(), - ), + style: ElevatedButton.styleFrom(shape: const CircleBorder()), child: const Icon(Icons.my_location), ), ), @@ -353,15 +306,10 @@ class _AssetMarkerMeta { final MapMarker marker; final bool shouldAnimate; - const _AssetMarkerMeta({ - required this.point, - required this.marker, - required this.shouldAnimate, - }); + const _AssetMarkerMeta({required this.point, required this.marker, required this.shouldAnimate}); @override - String toString() => - '_AssetMarkerMeta(point: $point, marker: $marker, shouldAnimate: $shouldAnimate)'; + String toString() => '_AssetMarkerMeta(point: $point, marker: $marker, shouldAnimate: $shouldAnimate)'; } class _MapWithMarker extends StatelessWidget { diff --git a/mobile/lib/pages/search/map/map_location_picker.page.dart b/mobile/lib/pages/search/map/map_location_picker.page.dart index f27deae052..0fe8b769f5 100644 --- a/mobile/lib/pages/search/map/map_location_picker.page.dart +++ b/mobile/lib/pages/search/map/map_location_picker.page.dart @@ -16,10 +16,7 @@ import 'package:immich_mobile/utils/map_utils.dart'; class MapLocationPickerPage extends HookConsumerWidget { final LatLng initialLatLng; - const MapLocationPickerPage({ - super.key, - this.initialLatLng = const LatLng(0, 0), - }); + const MapLocationPickerPage({super.key, this.initialLatLng = const LatLng(0, 0)}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -35,8 +32,7 @@ class MapLocationPickerPage extends HookConsumerWidget { selectedLatLng.value = centre; controller.value?.animateCamera(CameraUpdate.newLatLng(centre)); if (marker.value != null) { - await controller.value - ?.updateSymbol(marker.value!, SymbolOptions(geometry: centre)); + await controller.value?.updateSymbol(marker.value!, SymbolOptions(geometry: centre)); } } @@ -45,15 +41,13 @@ class MapLocationPickerPage extends HookConsumerWidget { } Future getCurrentLocation() async { - var (currentLocation, _) = - await MapUtils.checkPermAndGetLocation(context: context); + var (currentLocation, _) = await MapUtils.checkPermAndGetLocation(context: context); if (currentLocation == null) { return; } - var currentLatLng = - LatLng(currentLocation.latitude, currentLocation.longitude); + var currentLatLng = LatLng(currentLocation.latitude, currentLocation.longitude); selectedLatLng.value = currentLatLng; controller.value?.animateCamera(CameraUpdate.newLatLng(currentLatLng)); } @@ -69,17 +63,12 @@ class MapLocationPickerPage extends HookConsumerWidget { onData: (style) => Container( clipBehavior: Clip.antiAliasWithSaveLayer, decoration: const BoxDecoration( - borderRadius: BorderRadius.only( - bottomLeft: Radius.circular(40), - bottomRight: Radius.circular(40), - ), + borderRadius: BorderRadius.only(bottomLeft: Radius.circular(40), bottomRight: Radius.circular(40)), ), child: MapLibreMap( - initialCameraPosition: - CameraPosition(target: initialLatLng, zoom: 12), + initialCameraPosition: CameraPosition(target: initialLatLng, zoom: 12), styleString: style, - onMapCreated: (mapController) => - controller.value = mapController, + onMapCreated: (mapController) => controller.value = mapController, onStyleLoadedCallback: onStyleLoaded, onMapClick: onMapClick, dragEnabled: false, @@ -113,9 +102,7 @@ class _AppBar extends StatelessWidget implements PreferredSizeWidget { alignment: Alignment.centerLeft, child: ElevatedButton( onPressed: onClose, - style: ElevatedButton.styleFrom( - shape: const CircleBorder(), - ), + style: ElevatedButton.styleFrom(shape: const CircleBorder()), child: const Icon(Icons.arrow_back_ios_new_rounded), ), ), @@ -131,11 +118,7 @@ class _BottomBar extends StatelessWidget { final Function() onUseLocation; final Function() onGetCurrentLocation; - const _BottomBar({ - required this.selectedLatLng, - required this.onUseLocation, - required this.onGetCurrentLocation, - }); + const _BottomBar({required this.selectedLatLng, required this.onUseLocation, required this.onGetCurrentLocation}); @override Widget build(BuildContext context) { @@ -154,9 +137,8 @@ class _BottomBar extends StatelessWidget { const SizedBox(width: 15), ValueListenableBuilder( valueListenable: selectedLatLng, - builder: (_, value, __) => Text( - "${value.latitude.toStringAsFixed(4)}, ${value.longitude.toStringAsFixed(4)}", - ), + builder: (_, value, __) => + Text("${value.latitude.toStringAsFixed(4)}, ${value.longitude.toStringAsFixed(4)}"), ), ], ), @@ -165,13 +147,9 @@ class _BottomBar extends StatelessWidget { children: [ ElevatedButton( onPressed: onUseLocation, - child: - const Text("map_location_picker_page_use_location").tr(), - ), - ElevatedButton( - onPressed: onGetCurrentLocation, - child: const Icon(Icons.my_location), + child: const Text("map_location_picker_page_use_location").tr(), ), + ElevatedButton(onPressed: onGetCurrentLocation, child: const Icon(Icons.my_location)), ], ), ], diff --git a/mobile/lib/pages/search/person_result.page.dart b/mobile/lib/pages/search/person_result.page.dart index 859c7e8a89..7d2e612d25 100644 --- a/mobile/lib/pages/search/person_result.page.dart +++ b/mobile/lib/pages/search/person_result.page.dart @@ -15,11 +15,7 @@ class PersonResultPage extends HookConsumerWidget { final String personId; final String personName; - const PersonResultPage({ - super.key, - required this.personId, - required this.personName, - }); + const PersonResultPage({super.key, required this.personId, required this.personName}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -30,10 +26,7 @@ class PersonResultPage extends HookConsumerWidget { context: context, useRootNavigator: false, builder: (BuildContext context) { - return PersonNameEditForm( - personId: personId, - personName: name.value, - ); + return PersonNameEditForm(personId: personId, personName: name.value); }, ).then((result) { if (result != null && result.success) { @@ -55,10 +48,7 @@ class PersonResultPage extends HookConsumerWidget { children: [ ListTile( leading: const Icon(Icons.edit_outlined), - title: const Text( - 'edit_name', - style: TextStyle(fontWeight: FontWeight.bold), - ).tr(), + title: const Text('edit_name', style: TextStyle(fontWeight: FontWeight.bold)).tr(), onTap: showEditNameDialog, ), ], @@ -75,27 +65,13 @@ class PersonResultPage extends HookConsumerWidget { ? Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - 'add_a_name', - style: context.textTheme.titleMedium?.copyWith( - color: context.primaryColor, - ), - ).tr(), - Text( - 'find_them_fast', - style: context.textTheme.labelLarge, - ).tr(), + Text('add_a_name', style: context.textTheme.titleMedium?.copyWith(color: context.primaryColor)).tr(), + Text('find_them_fast', style: context.textTheme.labelLarge).tr(), ], ) : Column( crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - name.value, - style: context.textTheme.titleLarge, - overflow: TextOverflow.ellipsis, - ), - ], + children: [Text(name.value, style: context.textTheme.titleLarge, overflow: TextOverflow.ellipsis)], ), ); } @@ -103,16 +79,8 @@ class PersonResultPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( title: Text(name.value), - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), - actions: [ - IconButton( - onPressed: buildBottomSheet, - icon: const Icon(Icons.more_vert_rounded), - ), - ], + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_rounded)), + actions: [IconButton(onPressed: buildBottomSheet, icon: const Icon(Icons.more_vert_rounded))], ), body: MultiselectGrid( renderListProvider: personAssetsProvider(personId), @@ -122,16 +90,10 @@ class PersonResultPage extends HookConsumerWidget { children: [ CircleAvatar( radius: 36, - backgroundImage: NetworkImage( - getFaceThumbnailUrl(personId), - headers: ApiService.getRequestHeaders(), - ), + backgroundImage: NetworkImage(getFaceThumbnailUrl(personId), headers: ApiService.getRequestHeaders()), ), Expanded( - child: Padding( - padding: const EdgeInsets.only(left: 16.0, right: 16.0), - child: buildTitleBlock(), - ), + child: Padding(padding: const EdgeInsets.only(left: 16.0, right: 16.0), child: buildTitleBlock()), ), ], ), diff --git a/mobile/lib/pages/search/recently_taken.page.dart b/mobile/lib/pages/search/recently_taken.page.dart index cc1eb7086e..988af2faf0 100644 --- a/mobile/lib/pages/search/recently_taken.page.dart +++ b/mobile/lib/pages/search/recently_taken.page.dart @@ -17,16 +17,9 @@ class RecentlyTakenPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( title: const Text('recently_taken_page_title').tr(), - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.arrow_back_ios_rounded), - ), - ), - body: recents.widgetWhen( - onData: (searchResponse) => ImmichAssetGrid( - assets: searchResponse, - ), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.arrow_back_ios_rounded)), ), + body: recents.widgetWhen(onData: (searchResponse) => ImmichAssetGrid(assets: searchResponse)), ); } } diff --git a/mobile/lib/pages/search/search.page.dart b/mobile/lib/pages/search/search.page.dart index dba3199fac..97205e000c 100644 --- a/mobile/lib/pages/search/search.page.dart +++ b/mobile/lib/pages/search/search.page.dart @@ -41,15 +41,9 @@ class SearchPage extends HookConsumerWidget { location: prefilter?.location ?? SearchLocationFilter(), camera: prefilter?.camera ?? SearchCameraFilter(), date: prefilter?.date ?? SearchDateFilter(), - display: prefilter?.display ?? - SearchDisplayFilters( - isNotInAlbum: false, - isArchive: false, - isFavorite: false, - ), + display: prefilter?.display ?? SearchDisplayFilters(isNotInAlbum: false, isArchive: false, isFavorite: false), mediaType: prefilter?.mediaType ?? AssetType.other, - language: - "${context.locale.languageCode}-${context.locale.countryCode}", + language: "${context.locale.languageCode}-${context.locale.countryCode}", ), ); @@ -66,10 +60,7 @@ class SearchPage extends HookConsumerWidget { SnackBar searchInfoSnackBar(String message) { return SnackBar( - content: Text( - message, - style: context.textTheme.labelLarge, - ), + content: Text(message, style: context.textTheme.labelLarge), showCloseIcon: true, behavior: SnackBarBehavior.fixed, closeIconColor: context.colorScheme.onSurface, @@ -87,14 +78,10 @@ class SearchPage extends HookConsumerWidget { isSearching.value = true; ref.watch(paginatedSearchProvider.notifier).clear(); - final hasResult = await ref - .watch(paginatedSearchProvider.notifier) - .search(filter.value); + final hasResult = await ref.watch(paginatedSearchProvider.notifier).search(filter.value); if (!hasResult) { - context.showSnackBar( - searchInfoSnackBar('search_no_result'.tr()), - ); + context.showSnackBar(searchInfoSnackBar('search_no_result'.tr())); } previousFilter.value = filter.value; @@ -103,14 +90,10 @@ class SearchPage extends HookConsumerWidget { loadMoreSearchResult() async { isSearching.value = true; - final hasResult = await ref - .watch(paginatedSearchProvider.notifier) - .search(filter.value); + final hasResult = await ref.watch(paginatedSearchProvider.notifier).search(filter.value); if (!hasResult) { - context.showSnackBar( - searchInfoSnackBar('search_no_more_result'.tr()), - ); + context.showSnackBar(searchInfoSnackBar('search_no_more_result'.tr())); } isSearching.value = false; @@ -118,39 +101,26 @@ class SearchPage extends HookConsumerWidget { searchPrefilter() { if (prefilter != null) { - Future.delayed( - Duration.zero, - () { - search(); + Future.delayed(Duration.zero, () { + search(); - if (prefilter!.location.city != null) { - locationCurrentFilterWidget.value = Text( - prefilter!.location.city!, - style: context.textTheme.labelLarge, - ); - } - }, - ); + if (prefilter!.location.city != null) { + locationCurrentFilterWidget.value = Text(prefilter!.location.city!, style: context.textTheme.labelLarge); + } + }); } } - useEffect( - () { - Future.microtask( - () => ref.invalidate(paginatedSearchProvider), - ); - searchPrefilter(); + useEffect(() { + Future.microtask(() => ref.invalidate(paginatedSearchProvider)); + searchPrefilter(); - return null; - }, - [], - ); + return null; + }, []); showPeoplePicker() { - handleOnSelect(Set value) { - filter.value = filter.value.copyWith( - people: value, - ); + handleOnSelect(Set value) { + filter.value = filter.value.copyWith(people: value); peopleCurrentFilterWidget.value = Text( value.map((e) => e.name != '' ? e.name : 'no_name'.tr()).join(', '), @@ -159,9 +129,7 @@ class SearchPage extends HookConsumerWidget { } handleClear() { - filter.value = filter.value.copyWith( - people: {}, - ); + filter.value = filter.value.copyWith(people: {}); peopleCurrentFilterWidget.value = null; search(); @@ -177,10 +145,7 @@ class SearchPage extends HookConsumerWidget { expanded: true, onSearch: search, onClear: handleClear, - child: PeoplePicker( - onSelect: handleOnSelect, - filter: filter.value.people, - ), + child: PeoplePicker(onSelect: handleOnSelect, filter: filter.value.people), ), ), ); @@ -189,11 +154,7 @@ class SearchPage extends HookConsumerWidget { showLocationPicker() { handleOnSelect(Map value) { filter.value = filter.value.copyWith( - location: SearchLocationFilter( - country: value['country'], - city: value['city'], - state: value['state'], - ), + location: SearchLocationFilter(country: value['country'], city: value['city'], state: value['state']), ); final locationText = []; @@ -209,16 +170,11 @@ class SearchPage extends HookConsumerWidget { locationText.add(value['city']!); } - locationCurrentFilterWidget.value = Text( - locationText.join(', '), - style: context.textTheme.labelLarge, - ); + locationCurrentFilterWidget.value = Text(locationText.join(', '), style: context.textTheme.labelLarge); } handleClear() { - filter.value = filter.value.copyWith( - location: SearchLocationFilter(), - ); + filter.value = filter.value.copyWith(location: SearchLocationFilter()); locationCurrentFilterWidget.value = null; search(); @@ -235,15 +191,10 @@ class SearchPage extends HookConsumerWidget { child: Padding( padding: const EdgeInsets.symmetric(vertical: 16.0), child: Container( - padding: EdgeInsets.only( - bottom: context.viewInsets.bottom, - ), + padding: EdgeInsets.only(bottom: context.viewInsets.bottom), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: LocationPicker( - onSelected: handleOnSelect, - filter: filter.value.location, - ), + child: LocationPicker(onSelected: handleOnSelect, filter: filter.value.location), ), ), ), @@ -254,10 +205,7 @@ class SearchPage extends HookConsumerWidget { showCameraPicker() { handleOnSelect(Map value) { filter.value = filter.value.copyWith( - camera: SearchCameraFilter( - make: value['make'], - model: value['model'], - ), + camera: SearchCameraFilter(make: value['make'], model: value['model']), ); cameraCurrentFilterWidget.value = Text( @@ -267,9 +215,7 @@ class SearchPage extends HookConsumerWidget { } handleClear() { - filter.value = filter.value.copyWith( - camera: SearchCameraFilter(), - ); + filter.value = filter.value.copyWith(camera: SearchCameraFilter()); cameraCurrentFilterWidget.value = null; search(); @@ -285,10 +231,7 @@ class SearchPage extends HookConsumerWidget { onClear: handleClear, child: Padding( padding: const EdgeInsets.all(16.0), - child: CameraPicker( - onSelect: handleOnSelect, - filter: filter.value.camera, - ), + child: CameraPicker(onSelect: handleOnSelect, filter: filter.value.camera), ), ), ); @@ -320,9 +263,7 @@ class SearchPage extends HookConsumerWidget { ); if (date == null) { - filter.value = filter.value.copyWith( - date: SearchDateFilter(), - ); + filter.value = filter.value.copyWith(date: SearchDateFilter()); dateRangeCurrentFilterWidget.value = null; search(); @@ -332,13 +273,7 @@ class SearchPage extends HookConsumerWidget { filter.value = filter.value.copyWith( date: SearchDateFilter( takenAfter: date.start, - takenBefore: date.end.add( - const Duration( - hours: 23, - minutes: 59, - seconds: 59, - ), - ), + takenBefore: date.end.add(const Duration(hours: 23, minutes: 59, seconds: 59)), ), ); @@ -366,24 +301,20 @@ class SearchPage extends HookConsumerWidget { // MEDIA PICKER showMediaTypePicker() { handleOnSelected(AssetType assetType) { - filter.value = filter.value.copyWith( - mediaType: assetType, - ); + filter.value = filter.value.copyWith(mediaType: assetType); mediaTypeCurrentFilterWidget.value = Text( assetType == AssetType.image ? 'image'.tr() : assetType == AssetType.video - ? 'video'.tr() - : 'all'.tr(), + ? 'video'.tr() + : 'all'.tr(), style: context.textTheme.labelLarge, ); } handleClear() { - filter.value = filter.value.copyWith( - mediaType: AssetType.other, - ); + filter.value = filter.value.copyWith(mediaType: AssetType.other); mediaTypeCurrentFilterWidget.value = null; search(); @@ -395,10 +326,7 @@ class SearchPage extends HookConsumerWidget { title: 'search_filter_media_type_title'.tr(), onSearch: search, onClear: handleClear, - child: MediaTypePicker( - onSelect: handleOnSelected, - filter: filter.value.mediaType, - ), + child: MediaTypePicker(onSelect: handleOnSelected, filter: filter.value.mediaType), ), ); } @@ -410,32 +338,19 @@ class SearchPage extends HookConsumerWidget { value.forEach((key, value) { switch (key) { case DisplayOption.notInAlbum: - filter.value = filter.value.copyWith( - display: filter.value.display.copyWith( - isNotInAlbum: value, - ), - ); + filter.value = filter.value.copyWith(display: filter.value.display.copyWith(isNotInAlbum: value)); if (value) { - filterText - .add('search_filter_display_option_not_in_album'.tr()); + filterText.add('search_filter_display_option_not_in_album'.tr()); } break; case DisplayOption.archive: - filter.value = filter.value.copyWith( - display: filter.value.display.copyWith( - isArchive: value, - ), - ); + filter.value = filter.value.copyWith(display: filter.value.display.copyWith(isArchive: value)); if (value) { filterText.add('archive'.tr()); } break; case DisplayOption.favorite: - filter.value = filter.value.copyWith( - display: filter.value.display.copyWith( - isFavorite: value, - ), - ); + filter.value = filter.value.copyWith(display: filter.value.display.copyWith(isFavorite: value)); if (value) { filterText.add('favorite'.tr()); } @@ -448,19 +363,12 @@ class SearchPage extends HookConsumerWidget { return; } - displayOptionCurrentFilterWidget.value = Text( - filterText.join(', '), - style: context.textTheme.labelLarge, - ); + displayOptionCurrentFilterWidget.value = Text(filterText.join(', '), style: context.textTheme.labelLarge); } handleClear() { filter.value = filter.value.copyWith( - display: SearchDisplayFilters( - isNotInAlbum: false, - isArchive: false, - isFavorite: false, - ), + display: SearchDisplayFilters(isNotInAlbum: false, isArchive: false, isFavorite: false), ); displayOptionCurrentFilterWidget.value = null; @@ -473,10 +381,7 @@ class SearchPage extends HookConsumerWidget { title: 'display_options'.tr(), onSearch: search, onClear: handleClear, - child: DisplayOptionPicker( - onSelect: handleOnSelect, - filter: filter.value.display, - ), + child: DisplayOptionPicker(onSelect: handleOnSelect, filter: filter.value.display), ), ); } @@ -484,27 +389,15 @@ class SearchPage extends HookConsumerWidget { handleTextSubmitted(String value) { switch (textSearchType.value) { case TextSearchType.context: - filter.value = filter.value.copyWith( - filename: '', - context: value, - description: '', - ); + filter.value = filter.value.copyWith(filename: '', context: value, description: ''); break; case TextSearchType.filename: - filter.value = filter.value.copyWith( - filename: value, - context: '', - description: '', - ); + filter.value = filter.value.copyWith(filename: value, context: '', description: ''); break; case TextSearchType.description: - filter.value = filter.value.copyWith( - filename: '', - context: '', - description: value, - ); + filter.value = filter.value.copyWith(filename: '', context: '', description: value); break; } @@ -512,10 +405,10 @@ class SearchPage extends HookConsumerWidget { } IconData getSearchPrefixIcon() => switch (textSearchType.value) { - TextSearchType.context => Icons.image_search_rounded, - TextSearchType.filename => Icons.abc_rounded, - TextSearchType.description => Icons.text_snippet_outlined, - }; + TextSearchType.context => Icons.image_search_rounded, + TextSearchType.filename => Icons.abc_rounded, + TextSearchType.description => Icons.text_snippet_outlined, + }; return Scaffold( resizeToAvoidBottomInset: false, @@ -528,21 +421,11 @@ class SearchPage extends HookConsumerWidget { style: MenuStyle( elevation: const WidgetStatePropertyAll(1), shape: WidgetStateProperty.all( - const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(24), - ), - ), - ), - padding: const WidgetStatePropertyAll( - EdgeInsets.all(4), + const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(24))), ), + padding: const WidgetStatePropertyAll(EdgeInsets.all(4)), ), - builder: ( - BuildContext context, - MenuController controller, - Widget? child, - ) { + builder: (BuildContext context, MenuController controller, Widget? child) { return IconButton( onPressed: () { if (controller.isOpen) { @@ -563,9 +446,7 @@ class SearchPage extends HookConsumerWidget { 'search_by_context'.tr(), style: context.textTheme.bodyLarge?.copyWith( fontWeight: FontWeight.w500, - color: textSearchType.value == TextSearchType.context - ? context.colorScheme.primary - : null, + color: textSearchType.value == TextSearchType.context ? context.colorScheme.primary : null, ), ), selectedColor: context.colorScheme.primary, @@ -583,9 +464,7 @@ class SearchPage extends HookConsumerWidget { 'search_filter_filename'.tr(), style: context.textTheme.bodyLarge?.copyWith( fontWeight: FontWeight.w500, - color: textSearchType.value == TextSearchType.filename - ? context.colorScheme.primary - : null, + color: textSearchType.value == TextSearchType.filename ? context.colorScheme.primary : null, ), ), selectedColor: context.colorScheme.primary, @@ -603,15 +482,11 @@ class SearchPage extends HookConsumerWidget { 'search_by_description'.tr(), style: context.textTheme.bodyLarge?.copyWith( fontWeight: FontWeight.w500, - color: - textSearchType.value == TextSearchType.description - ? context.colorScheme.primary - : null, + color: textSearchType.value == TextSearchType.description ? context.colorScheme.primary : null, ), ), selectedColor: context.colorScheme.primary, - selected: - textSearchType.value == TextSearchType.description, + selected: textSearchType.value == TextSearchType.description, ), onPressed: () { textSearchType.value = TextSearchType.description; @@ -624,13 +499,8 @@ class SearchPage extends HookConsumerWidget { ], title: Container( decoration: BoxDecoration( - border: Border.all( - color: context.colorScheme.onSurface.withAlpha(0), - width: 0, - ), - borderRadius: const BorderRadius.all( - Radius.circular(24), - ), + border: Border.all(color: context.colorScheme.onSurface.withAlpha(0), width: 0), + borderRadius: const BorderRadius.all(Radius.circular(24)), gradient: LinearGradient( colors: [ context.colorScheme.primary.withValues(alpha: 0.075), @@ -645,15 +515,8 @@ class SearchPage extends HookConsumerWidget { hintText: searchHintText.value, key: const Key('search_text_field'), controller: textSearchController, - contentPadding: prefilter != null - ? const EdgeInsets.only(left: 24) - : const EdgeInsets.all(8), - prefixIcon: prefilter != null - ? null - : Icon( - getSearchPrefixIcon(), - color: context.colorScheme.primary, - ), + contentPadding: prefilter != null ? const EdgeInsets.only(left: 24) : const EdgeInsets.all(8), + prefixIcon: prefilter != null ? null : Icon(getSearchPrefixIcon(), color: context.colorScheme.primary), onSubmitted: handleTextSubmitted, focusNode: ref.watch(searchInputFocusProvider), ), @@ -713,14 +576,9 @@ class SearchPage extends HookConsumerWidget { ), ), if (isSearching.value) - const Expanded( - child: Center(child: CircularProgressIndicator()), - ) + const Expanded(child: Center(child: CircularProgressIndicator())) else - SearchResultGrid( - onScrollEnd: loadMoreSearchResult, - isSearching: isSearching.value, - ), + SearchResultGrid(onScrollEnd: loadMoreSearchResult, isSearching: isSearching.value), ], ), ); @@ -731,11 +589,7 @@ class SearchResultGrid extends StatelessWidget { final VoidCallback onScrollEnd; final bool isSearching; - const SearchResultGrid({ - super.key, - required this.onScrollEnd, - this.isSearching = false, - }); + const SearchResultGrid({super.key, required this.onScrollEnd, this.isSearching = false}); @override Widget build(BuildContext context) { @@ -744,17 +598,13 @@ class SearchResultGrid extends StatelessWidget { padding: const EdgeInsets.only(top: 8.0), child: NotificationListener( onNotification: (notification) { - final isBottomSheetNotification = notification.context - ?.findAncestorWidgetOfExactType< - DraggableScrollableSheet>() != - null; + final isBottomSheetNotification = + notification.context?.findAncestorWidgetOfExactType() != null; final metrics = notification.metrics; final isVerticalScroll = metrics.axis == Axis.vertical; - if (metrics.pixels >= metrics.maxScrollExtent && - isVerticalScroll && - !isBottomSheetNotification) { + if (metrics.pixels >= metrics.maxScrollExtent && isVerticalScroll && !isBottomSheetNotification) { onScrollEnd(); } @@ -770,9 +620,7 @@ class SearchResultGrid extends StatelessWidget { dragScrollLabelEnabled: false, emptyIndicator: Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: !isSearching - ? const SearchEmptyContent() - : const SizedBox.shrink(), + child: !isSearching ? const SearchEmptyContent() : const SizedBox.shrink(), ), ), ), @@ -794,19 +642,12 @@ class SearchEmptyContent extends StatelessWidget { const SizedBox(height: 40), Center( child: Image.asset( - context.isDarkTheme - ? 'assets/polaroid-dark.png' - : 'assets/polaroid-light.png', + context.isDarkTheme ? 'assets/polaroid-dark.png' : 'assets/polaroid-light.png', height: 125, ), ), const SizedBox(height: 16), - Center( - child: Text( - 'search_page_search_photos_videos'.tr(), - style: context.textTheme.labelLarge, - ), - ), + Center(child: Text('search_page_search_photos_videos'.tr(), style: context.textTheme.labelLarge)), const SizedBox(height: 32), const QuickLinkList(), ], @@ -822,13 +663,8 @@ class QuickLinkList extends StatelessWidget { Widget build(BuildContext context) { return Container( decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), - border: Border.all( - color: context.colorScheme.outline.withAlpha(10), - width: 1, - ), + borderRadius: const BorderRadius.all(Radius.circular(20)), + border: Border.all(color: context.colorScheme.outline.withAlpha(10), width: 1), gradient: LinearGradient( colors: [ context.colorScheme.primary.withAlpha(10), @@ -892,19 +728,9 @@ class QuickLink extends StatelessWidget { ); return ListTile( - shape: RoundedRectangleBorder( - borderRadius: borderRadius, - ), - leading: Icon( - icon, - size: 26, - ), - title: Text( - title, - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w500, - ), - ), + shape: RoundedRectangleBorder(borderRadius: borderRadius), + leading: Icon(icon, size: 26), + title: Text(title, style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500)), onTap: onTap, ); } diff --git a/mobile/lib/pages/settings/beta_sync_settings.page.dart b/mobile/lib/pages/settings/beta_sync_settings.page.dart new file mode 100644 index 0000000000..992557b7c6 --- /dev/null +++ b/mobile/lib/pages/settings/beta_sync_settings.page.dart @@ -0,0 +1,25 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/widgets/settings/beta_sync_settings/beta_sync_settings.dart'; + +@RoutePage() +class BetaSyncSettingsPage extends StatelessWidget { + const BetaSyncSettingsPage({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + elevation: 0, + title: const Text("beta_sync").t(context: context), + leading: IconButton( + onPressed: () => context.maybePop(true), + splashRadius: 24, + icon: const Icon(Icons.arrow_back_ios_rounded), + ), + ), + body: const BetaSyncSettings(), + ); + } +} diff --git a/mobile/lib/pages/share_intent/share_intent.page.dart b/mobile/lib/pages/share_intent/share_intent.page.dart index 11c114e4a6..9d2dbe80c2 100644 --- a/mobile/lib/pages/share_intent/share_intent.page.dart +++ b/mobile/lib/pages/share_intent/share_intent.page.dart @@ -38,9 +38,7 @@ class ShareIntentPage extends HookConsumerWidget { void upload() async { for (final attachment in candidates) { - await ref - .read(shareIntentUploadProvider.notifier) - .upload(attachment.file); + await ref.read(shareIntentUploadProvider.notifier).upload(attachment.file); } isUploaded.value = true; @@ -62,24 +60,16 @@ class ShareIntentPage extends HookConsumerWidget { appBar: AppBar( title: Column( children: [ - const Text('upload_to_immich').tr( - namedArgs: {'count': candidates.length.toString()}, - ), + const Text('upload_to_immich').tr(namedArgs: {'count': candidates.length.toString()}), Text( currentEndpoint, - style: context.textTheme.labelMedium?.copyWith( - color: context.colorScheme.onSurface.withAlpha(200), - ), + style: context.textTheme.labelMedium?.copyWith(color: context.colorScheme.onSurface.withAlpha(200)), ), ], ), leading: IconButton( onPressed: () { - context.navigateTo( - Store.isBetaTimelineEnabled - ? const TabShellRoute() - : const TabControllerRoute(), - ); + context.navigateTo(Store.isBetaTimelineEnabled ? const TabShellRoute() : const TabControllerRoute()); }, icon: const Icon(Icons.arrow_back), ), @@ -88,16 +78,10 @@ class ShareIntentPage extends HookConsumerWidget { itemCount: attachments.length, itemBuilder: (context, index) { final attachment = attachments[index]; - final target = candidates.firstWhere( - (element) => element.id == attachment.id, - orElse: () => attachment, - ); + final target = candidates.firstWhere((element) => element.id == attachment.id, orElse: () => attachment); return Padding( - padding: const EdgeInsets.symmetric( - vertical: 4.0, - horizontal: 16, - ), + padding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 16), child: LargeLeadingTile( onTap: () => toggleSelection(attachment), disabled: isUploaded.value, @@ -107,21 +91,11 @@ class ShareIntentPage extends HookConsumerWidget { ClipRRect( borderRadius: const BorderRadius.all(Radius.circular(16)), child: attachment.isImage - ? Image.file( - attachment.file, - width: 64, - height: 64, - fit: BoxFit.cover, - ) + ? Image.file(attachment.file, width: 64, height: 64, fit: BoxFit.cover) : const SizedBox( width: 64, height: 64, - child: Center( - child: Icon( - Icons.videocam, - color: Colors.white, - ), - ), + child: Center(child: Icon(Icons.videocam, color: Colors.white)), ), ), if (attachment.isImage) @@ -132,25 +106,13 @@ class ShareIntentPage extends HookConsumerWidget { Icons.image, color: Colors.white, size: 20, - shadows: [ - Shadow( - offset: Offset(0, 0), - blurRadius: 8.0, - color: Colors.black45, - ), - ], + shadows: [Shadow(offset: Offset(0, 0), blurRadius: 8.0, color: Colors.black45)], ), ), ], ), - title: Text( - attachment.fileName, - style: context.textTheme.titleSmall, - ), - subtitle: Text( - attachment.fileSize, - style: context.textTheme.labelLarge, - ), + title: Text(attachment.fileName, style: context.textTheme.titleSmall), + subtitle: Text(attachment.fileSize, style: context.textTheme.labelLarge), trailing: Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), child: UploadStatusIcon( @@ -170,9 +132,7 @@ class ShareIntentPage extends HookConsumerWidget { height: 48, child: ElevatedButton( onPressed: isUploaded.value ? null : upload, - child: isUploaded.value - ? UploadingText(candidates: candidates) - : const Text('upload').tr(), + child: isUploaded.value ? UploadingText(candidates: candidates) : const Text('upload').tr(), ), ), ), @@ -191,22 +151,14 @@ class UploadingText extends StatelessWidget { return element.status == UploadStatus.complete; }).length; - return const Text("shared_intent_upload_button_progress_text").tr( - namedArgs: { - 'current': uploadedCount.toString(), - 'total': candidates.length.toString(), - }, - ); + return const Text( + "shared_intent_upload_button_progress_text", + ).tr(namedArgs: {'current': uploadedCount.toString(), 'total': candidates.length.toString()}); } } class UploadStatusIcon extends StatelessWidget { - const UploadStatusIcon({ - super.key, - required this.status, - required this.selected, - this.progress = 0, - }); + const UploadStatusIcon({super.key, required this.status, required this.selected, this.progress = 0}); final UploadStatus status; final double progress; @@ -224,55 +176,42 @@ class UploadStatusIcon extends StatelessWidget { final statusIcon = switch (status) { UploadStatus.enqueued => Icon( - Icons.check_circle_rounded, - color: context.primaryColor, - semanticLabel: 'enqueued'.tr(), - ), + Icons.check_circle_rounded, + color: context.primaryColor, + semanticLabel: 'enqueued'.tr(), + ), UploadStatus.running => Stack( - alignment: AlignmentDirectional.center, - children: [ - SizedBox( - width: 40, - height: 40, - child: TweenAnimationBuilder( - tween: Tween(begin: 0.0, end: progress), - duration: const Duration(milliseconds: 500), - builder: (context, value, _) => CircularProgressIndicator( - backgroundColor: context.colorScheme.surfaceContainerLow, - strokeWidth: 3, - value: value, - semanticsLabel: 'uploading'.tr(), - ), + alignment: AlignmentDirectional.center, + children: [ + SizedBox( + width: 40, + height: 40, + child: TweenAnimationBuilder( + tween: Tween(begin: 0.0, end: progress), + duration: const Duration(milliseconds: 500), + builder: (context, value, _) => CircularProgressIndicator( + backgroundColor: context.colorScheme.surfaceContainerLow, + strokeWidth: 3, + value: value, + semanticsLabel: 'uploading'.tr(), ), ), - Text( - (progress * 100).toStringAsFixed(0), - style: context.textTheme.labelSmall?.copyWith( - fontWeight: FontWeight.bold, - ), - ), - ], - ), - UploadStatus.complete => Icon( - Icons.check_circle_rounded, - color: Colors.green, - semanticLabel: 'completed'.tr(), - ), - UploadStatus.notFound || UploadStatus.failed => Icon( - Icons.error_rounded, - color: Colors.red, - semanticLabel: 'failed'.tr(), - ), - UploadStatus.canceled => Icon( - Icons.cancel_rounded, - color: Colors.red, - semanticLabel: 'canceled'.tr(), - ), - UploadStatus.waitingtoRetry || UploadStatus.paused => Icon( - Icons.pause_circle_rounded, - color: context.primaryColor, - semanticLabel: 'paused'.tr(), - ), + ), + Text( + (progress * 100).toStringAsFixed(0), + style: context.textTheme.labelSmall?.copyWith(fontWeight: FontWeight.bold), + ), + ], + ), + UploadStatus.complete => Icon(Icons.check_circle_rounded, color: Colors.green, semanticLabel: 'completed'.tr()), + UploadStatus.notFound || + UploadStatus.failed => Icon(Icons.error_rounded, color: Colors.red, semanticLabel: 'failed'.tr()), + UploadStatus.canceled => Icon(Icons.cancel_rounded, color: Colors.red, semanticLabel: 'canceled'.tr()), + UploadStatus.waitingToRetry || UploadStatus.paused => Icon( + Icons.pause_circle_rounded, + color: context.primaryColor, + semanticLabel: 'paused'.tr(), + ), }; return statusIcon; diff --git a/mobile/lib/platform/native_sync_api.g.dart b/mobile/lib/platform/native_sync_api.g.dart index 46599eb016..3cbd08cd68 100644 --- a/mobile/lib/platform/native_sync_api.g.dart +++ b/mobile/lib/platform/native_sync_api.g.dart @@ -17,15 +17,14 @@ PlatformException _createConnectionError(String channelName) { bool _deepEquals(Object? a, Object? b) { if (a is List && b is List) { - return a.length == b.length && - a.indexed - .every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1])); + return a.length == b.length && a.indexed.every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1])); } if (a is Map && b is Map) { return a.length == b.length && - a.entries.every((MapEntry entry) => - (b as Map).containsKey(entry.key) && - _deepEquals(entry.value, b[entry.key])); + a.entries.every( + (MapEntry entry) => + (b as Map).containsKey(entry.key) && _deepEquals(entry.value, b[entry.key]), + ); } return a == b; } @@ -41,6 +40,7 @@ class PlatformAsset { this.height, required this.durationInSeconds, required this.orientation, + required this.isFavorite, }); String id; @@ -61,18 +61,10 @@ class PlatformAsset { int orientation; + bool isFavorite; + List _toList() { - return [ - id, - name, - type, - createdAt, - updatedAt, - width, - height, - durationInSeconds, - orientation, - ]; + return [id, name, type, createdAt, updatedAt, width, height, durationInSeconds, orientation, isFavorite]; } Object encode() { @@ -91,6 +83,7 @@ class PlatformAsset { height: result[6] as int?, durationInSeconds: result[7]! as int, orientation: result[8]! as int, + isFavorite: result[9]! as bool, ); } @@ -131,13 +124,7 @@ class PlatformAlbum { int assetCount; List _toList() { - return [ - id, - name, - updatedAt, - isCloud, - assetCount, - ]; + return [id, name, updatedAt, isCloud, assetCount]; } Object encode() { @@ -173,12 +160,7 @@ class PlatformAlbum { } class SyncDelta { - SyncDelta({ - required this.hasChanges, - required this.updates, - required this.deletes, - required this.assetAlbums, - }); + SyncDelta({required this.hasChanges, required this.updates, required this.deletes, required this.assetAlbums}); bool hasChanges; @@ -189,12 +171,7 @@ class SyncDelta { Map> assetAlbums; List _toList() { - return [ - hasChanges, - updates, - deletes, - assetAlbums, - ]; + return [hasChanges, updates, deletes, assetAlbums]; } Object encode() { @@ -207,8 +184,7 @@ class SyncDelta { hasChanges: result[0]! as bool, updates: (result[1] as List?)!.cast(), deletes: (result[2] as List?)!.cast(), - assetAlbums: - (result[3] as Map?)!.cast>(), + assetAlbums: (result[3] as Map?)!.cast>(), ); } @@ -269,11 +245,9 @@ class NativeSyncApi { /// Constructor for [NativeSyncApi]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - NativeSyncApi( - {BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) - : pigeonVar_binaryMessenger = binaryMessenger, - pigeonVar_messageChannelSuffix = - messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + NativeSyncApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; final BinaryMessenger? pigeonVar_binaryMessenger; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); @@ -283,15 +257,13 @@ class NativeSyncApi { Future shouldFullSync() async { final String pigeonVar_channelName = 'dev.flutter.pigeon.immich_mobile.NativeSyncApi.shouldFullSync$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture as List?; + final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -313,15 +285,13 @@ class NativeSyncApi { Future getMediaChanges() async { final String pigeonVar_channelName = 'dev.flutter.pigeon.immich_mobile.NativeSyncApi.getMediaChanges$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture as List?; + final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -343,15 +313,13 @@ class NativeSyncApi { Future checkpointSync() async { final String pigeonVar_channelName = 'dev.flutter.pigeon.immich_mobile.NativeSyncApi.checkpointSync$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture as List?; + final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -368,15 +336,13 @@ class NativeSyncApi { Future clearSyncCheckpoint() async { final String pigeonVar_channelName = 'dev.flutter.pigeon.immich_mobile.NativeSyncApi.clearSyncCheckpoint$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture as List?; + final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -393,16 +359,13 @@ class NativeSyncApi { Future> getAssetIdsForAlbum(String albumId) async { final String pigeonVar_channelName = 'dev.flutter.pigeon.immich_mobile.NativeSyncApi.getAssetIdsForAlbum$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final Future pigeonVar_sendFuture = - pigeonVar_channel.send([albumId]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture as List?; + final Future pigeonVar_sendFuture = pigeonVar_channel.send([albumId]); + final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -424,15 +387,13 @@ class NativeSyncApi { Future> getAlbums() async { final String pigeonVar_channelName = 'dev.flutter.pigeon.immich_mobile.NativeSyncApi.getAlbums$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture as List?; + final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -454,16 +415,13 @@ class NativeSyncApi { Future getAssetsCountSince(String albumId, int timestamp) async { final String pigeonVar_channelName = 'dev.flutter.pigeon.immich_mobile.NativeSyncApi.getAssetsCountSince$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final Future pigeonVar_sendFuture = - pigeonVar_channel.send([albumId, timestamp]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture as List?; + final Future pigeonVar_sendFuture = pigeonVar_channel.send([albumId, timestamp]); + final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -482,20 +440,16 @@ class NativeSyncApi { } } - Future> getAssetsForAlbum(String albumId, - {int? updatedTimeCond}) async { + Future> getAssetsForAlbum(String albumId, {int? updatedTimeCond}) async { final String pigeonVar_channelName = 'dev.flutter.pigeon.immich_mobile.NativeSyncApi.getAssetsForAlbum$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final Future pigeonVar_sendFuture = - pigeonVar_channel.send([albumId, updatedTimeCond]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture as List?; + final Future pigeonVar_sendFuture = pigeonVar_channel.send([albumId, updatedTimeCond]); + final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { @@ -517,16 +471,13 @@ class NativeSyncApi { Future> hashPaths(List paths) async { final String pigeonVar_channelName = 'dev.flutter.pigeon.immich_mobile.NativeSyncApi.hashPaths$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final Future pigeonVar_sendFuture = - pigeonVar_channel.send([paths]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture as List?; + final Future pigeonVar_sendFuture = pigeonVar_channel.send([paths]); + final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { throw _createConnectionError(pigeonVar_channelName); } else if (pigeonVar_replyList.length > 1) { diff --git a/mobile/lib/presentation/pages/dev/feat_in_development.page.dart b/mobile/lib/presentation/pages/dev/feat_in_development.page.dart index 2815b2db83..2ab1eeaaa9 100644 --- a/mobile/lib/presentation/pages/dev/feat_in_development.page.dart +++ b/mobile/lib/presentation/pages/dev/feat_in_development.page.dart @@ -31,27 +31,18 @@ final _features = [ return Future.value(); } - final assets = - await ref.read(remoteAssetRepositoryProvider).getSome(user.id); + final assets = await ref.read(remoteAssetRepositoryProvider).getSome(user.id); final selectedAssets = await ctx.pushRoute>( - DriftAssetSelectionTimelineRoute( - lockedSelectionAssets: assets.toSet(), - ), + DriftAssetSelectionTimelineRoute(lockedSelectionAssets: assets.toSet()), ); - DLog.log( - "Selected ${selectedAssets?.length ?? 0} assets", - ); + DLog.log("Selected ${selectedAssets?.length ?? 0} assets"); return Future.value(); }, ), - _Feature( - name: '', - icon: Icons.vertical_align_center_sharp, - onTap: (_, __) => Future.value(), - ), + _Feature(name: '', icon: Icons.vertical_align_center_sharp, onTap: (_, __) => Future.value()), _Feature( name: 'Sync Local', icon: Icons.photo_album_rounded, @@ -75,15 +66,9 @@ final _features = [ _Feature( name: 'WAL Checkpoint', icon: Icons.save_rounded, - onTap: (_, ref) => ref - .read(driftProvider) - .customStatement("pragma wal_checkpoint(truncate)"), - ), - _Feature( - name: '', - icon: Icons.vertical_align_center_sharp, - onTap: (_, __) => Future.value(), + onTap: (_, ref) => ref.read(driftProvider).customStatement("pragma wal_checkpoint(truncate)"), ), + _Feature(name: '', icon: Icons.vertical_align_center_sharp, onTap: (_, __) => Future.value()), _Feature( name: 'Clear Delta Checkpoint', icon: Icons.delete_rounded, @@ -91,6 +76,7 @@ final _features = [ ), _Feature( name: 'Clear Local Data', + style: const TextStyle(color: Colors.orange, fontWeight: FontWeight.bold), icon: Icons.delete_forever_rounded, onTap: (_, ref) async { final db = ref.read(driftProvider); @@ -101,6 +87,7 @@ final _features = [ ), _Feature( name: 'Clear Remote Data', + style: const TextStyle(color: Colors.orange, fontWeight: FontWeight.bold), icon: Icons.delete_sweep_rounded, onTap: (_, ref) async { final db = ref.read(driftProvider); @@ -112,21 +99,26 @@ final _features = [ await db.memoryEntity.deleteAll(); await db.memoryAssetEntity.deleteAll(); await db.stackEntity.deleteAll(); + await db.personEntity.deleteAll(); + await db.assetFaceEntity.deleteAll(); }, ), _Feature( name: 'Local Media Summary', + style: const TextStyle(color: Colors.indigo, fontWeight: FontWeight.bold), icon: Icons.table_chart_rounded, onTap: (ctx, _) => ctx.pushRoute(const LocalMediaSummaryRoute()), ), _Feature( name: 'Remote Media Summary', + style: const TextStyle(color: Colors.indigo, fontWeight: FontWeight.bold), icon: Icons.summarize_rounded, onTap: (ctx, _) => ctx.pushRoute(const RemoteMediaSummaryRoute()), ), _Feature( name: 'Reset Sqlite', icon: Icons.table_view_rounded, + style: const TextStyle(color: Colors.red, fontWeight: FontWeight.bold), onTap: (_, ref) async { final drift = ref.read(driftProvider); // ignore: invalid_use_of_protected_member, invalid_use_of_visible_for_testing_member @@ -146,10 +138,7 @@ class FeatInDevPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: const Text('Features in Development'), - centerTitle: true, - ), + appBar: AppBar(title: const Text('Features in Development'), centerTitle: true), body: Column( children: [ Flexible( @@ -159,7 +148,7 @@ class FeatInDevPage extends StatelessWidget { final feat = _features[index]; return Consumer( builder: (ctx, ref, _) => ListTile( - title: Text(feat.name), + title: Text(feat.name, style: feat.style), trailing: Icon(feat.icon), visualDensity: VisualDensity.compact, onTap: () => unawaited(feat.onTap(ctx, ref)), @@ -178,14 +167,11 @@ class FeatInDevPage extends StatelessWidget { } class _Feature { - const _Feature({ - required this.name, - required this.icon, - required this.onTap, - }); + const _Feature({required this.name, required this.icon, required this.onTap, this.style}); final String name; final IconData icon; + final TextStyle? style; final Future Function(BuildContext, WidgetRef _) onTap; } @@ -220,18 +206,11 @@ class _DevLogs extends StatelessWidget { return ListTile( title: Text( logMessage.message, - style: TextStyle( - color: ctx.colorScheme.onSurface, - fontSize: 14.0, - overflow: TextOverflow.ellipsis, - ), + style: TextStyle(color: ctx.colorScheme.onSurface, fontSize: 14.0, overflow: TextOverflow.ellipsis), ), subtitle: Text( "at ${DateFormat("HH:mm:ss.SSS").format(logMessage.createdAt)}", - style: TextStyle( - color: ctx.colorScheme.onSurfaceSecondary, - fontSize: 12.0, - ), + style: TextStyle(color: ctx.colorScheme.onSurfaceSecondary, fontSize: 12.0), ), dense: true, visualDensity: VisualDensity.compact, diff --git a/mobile/lib/presentation/pages/dev/media_stat.page.dart b/mobile/lib/presentation/pages/dev/media_stat.page.dart index e61dcdf90d..b48d8fc307 100644 --- a/mobile/lib/presentation/pages/dev/media_stat.page.dart +++ b/mobile/lib/presentation/pages/dev/media_stat.page.dart @@ -21,12 +21,7 @@ class _Summary extends StatelessWidget { final Future countFuture; final void Function()? onTap; - const _Summary({ - required this.name, - required this.countFuture, - this.leading, - this.onTap, - }); + const _Summary({required this.name, required this.countFuture, this.leading, this.onTap}); @override Widget build(BuildContext context) { @@ -40,31 +35,17 @@ class _Summary extends StatelessWidget { } else if (snapshot.hasError) { subtitle = const Icon(Icons.error_rounded); } else { - subtitle = Text( - '${snapshot.data ?? 0}', - style: ctx.textTheme.bodyLarge, - ); + subtitle = Text('${snapshot.data ?? 0}', style: ctx.textTheme.bodyLarge); } - return ListTile( - leading: leading, - title: Text(name), - trailing: subtitle, - onTap: onTap, - ); + return ListTile(leading: leading, title: Text(name), trailing: subtitle, onTap: onTap); }, ); } } final _localStats = [ - _Stat( - name: 'Local Assets', - load: (db) => db.managers.localAssetEntity.count(), - ), - _Stat( - name: 'Local Albums', - load: (db) => db.managers.localAlbumEntity.count(), - ), + _Stat(name: 'Local Assets', load: (db) => db.managers.localAssetEntity.count()), + _Stat(name: 'Local Albums', load: (db) => db.managers.localAlbumEntity.count()), ]; @RoutePage() @@ -97,10 +78,7 @@ class LocalMediaSummaryPage extends StatelessWidget { const Divider(), Padding( padding: const EdgeInsets.only(left: 15), - child: Text( - "Album summary", - style: ctx.textTheme.titleMedium, - ), + child: Text("Album summary", style: ctx.textTheme.titleMedium), ), ], ), @@ -124,9 +102,7 @@ class LocalMediaSummaryPage extends StatelessWidget { leading: const Icon(Icons.photo_album_rounded), name: album.name, countFuture: countFuture, - onTap: () => context.router.push( - LocalTimelineRoute(album: album), - ), + onTap: () => context.router.push(LocalTimelineRoute(album: album)), ); }, itemCount: albums.length, @@ -142,30 +118,14 @@ class LocalMediaSummaryPage extends StatelessWidget { } final _remoteStats = [ - _Stat( - name: 'Remote Assets', - load: (db) => db.managers.remoteAssetEntity.count(), - ), - _Stat( - name: 'Exif Entities', - load: (db) => db.managers.remoteExifEntity.count(), - ), - _Stat( - name: 'Remote Albums', - load: (db) => db.managers.remoteAlbumEntity.count(), - ), - _Stat( - name: 'Memories', - load: (db) => db.managers.memoryEntity.count(), - ), - _Stat( - name: 'Memories Assets', - load: (db) => db.managers.memoryAssetEntity.count(), - ), - _Stat( - name: 'Stacks', - load: (db) => db.managers.stackEntity.count(), - ), + _Stat(name: 'Remote Assets', load: (db) => db.managers.remoteAssetEntity.count()), + _Stat(name: 'Exif Entities', load: (db) => db.managers.remoteExifEntity.count()), + _Stat(name: 'Remote Albums', load: (db) => db.managers.remoteAlbumEntity.count()), + _Stat(name: 'Memories', load: (db) => db.managers.memoryEntity.count()), + _Stat(name: 'Memories Assets', load: (db) => db.managers.memoryAssetEntity.count()), + _Stat(name: 'Stacks', load: (db) => db.managers.stackEntity.count()), + _Stat(name: 'People', load: (db) => db.managers.personEntity.count()), + _Stat(name: 'AssetFaces', load: (db) => db.managers.assetFaceEntity.count()), ]; @RoutePage() @@ -198,10 +158,7 @@ class RemoteMediaSummaryPage extends StatelessWidget { const Divider(), Padding( padding: const EdgeInsets.only(left: 15), - child: Text( - "Album summary", - style: ctx.textTheme.titleMedium, - ), + child: Text("Album summary", style: ctx.textTheme.titleMedium), ), ], ), @@ -225,9 +182,7 @@ class RemoteMediaSummaryPage extends StatelessWidget { leading: const Icon(Icons.photo_album_rounded), name: album.name, countFuture: countFuture, - onTap: () => context.router.push( - RemoteAlbumRoute(album: album), - ), + onTap: () => context.router.push(RemoteAlbumRoute(album: album)), ); }, itemCount: albums.length, diff --git a/mobile/lib/presentation/pages/drift_album.page.dart b/mobile/lib/presentation/pages/drift_album.page.dart index d59f734c79..0835c741ad 100644 --- a/mobile/lib/presentation/pages/drift_album.page.dart +++ b/mobile/lib/presentation/pages/drift_album.page.dart @@ -1,24 +1,13 @@ import 'dart:async'; -import 'dart:math'; import 'package:auto_route/auto_route.dart'; -import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/domain/models/album/album.model.dart'; -import 'package:immich_mobile/extensions/build_context_extensions.dart'; -import 'package:immich_mobile/extensions/theme_extensions.dart'; -import 'package:immich_mobile/extensions/translate_extensions.dart'; -import 'package:immich_mobile/models/albums/album_search.model.dart'; -import 'package:immich_mobile/pages/common/large_leading_tile.dart'; -import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart'; +import 'package:immich_mobile/presentation/widgets/album/album_selector.widget.dart'; import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart'; -import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/routing/router.dart'; -import 'package:immich_mobile/utils/remote_album.utils.dart'; import 'package:immich_mobile/widgets/common/immich_sliver_app_bar.dart'; -import 'package:immich_mobile/widgets/common/search_field.dart'; @RoutePage() class DriftAlbumsPage extends ConsumerStatefulWidget { @@ -29,770 +18,37 @@ class DriftAlbumsPage extends ConsumerStatefulWidget { } class _DriftAlbumsPageState extends ConsumerState { - bool isGrid = false; - final searchController = TextEditingController(); - QuickFilterMode filterMode = QuickFilterMode.all; - final searchFocusNode = FocusNode(); - - @override - void initState() { - super.initState(); - - // Load albums when component mounts - WidgetsBinding.instance.addPostFrameCallback((_) { - ref.read(remoteAlbumProvider.notifier).getAll(); - }); - - searchController.addListener(() { - onSearch(searchController.text, filterMode); - }); - } - - void onSearch(String searchTerm, QuickFilterMode sortMode) { - final userId = ref.watch(currentUserProvider)?.id; - ref - .read(remoteAlbumProvider.notifier) - .searchAlbums(searchTerm, userId, sortMode); - } - Future onRefresh() async { await ref.read(remoteAlbumProvider.notifier).refresh(); } - void toggleViewMode() { - setState(() { - isGrid = !isGrid; - }); - } - - void changeFilter(QuickFilterMode sortMode) { - setState(() { - filterMode = sortMode; - }); - } - - void clearSearch() { - setState(() { - filterMode = QuickFilterMode.all; - searchController.clear(); - ref.read(remoteAlbumProvider.notifier).clearSearch(); - }); - } - - @override - void dispose() { - searchController.dispose(); - searchFocusNode.dispose(); - super.dispose(); - } - @override Widget build(BuildContext context) { - final albumState = ref.watch(remoteAlbumProvider); - final albums = albumState.filteredAlbums; - final isLoading = albumState.isLoading; - final error = albumState.error; - final userId = ref.watch(currentUserProvider)?.id; - return RefreshIndicator( onRefresh: onRefresh, + edgeOffset: 100, child: CustomScrollView( slivers: [ ImmichSliverAppBar( + snap: false, + floating: false, + pinned: true, actions: [ IconButton( - icon: const Icon( - Icons.add_rounded, - size: 28, - ), - onPressed: () => context.pushRoute( - const DriftCreateAlbumRoute(), - ), + icon: const Icon(Icons.add_rounded, size: 28), + onPressed: () => context.pushRoute(const DriftCreateAlbumRoute()), ), ], showUploadButton: false, ), - _SearchBar( - searchController: searchController, - searchFocusNode: searchFocusNode, - onSearch: onSearch, - filterMode: filterMode, - onClearSearch: clearSearch, + AlbumSelector( + onAlbumSelected: (album) { + ref.read(currentRemoteAlbumProvider.notifier).setAlbum(album); + context.router.push(RemoteAlbumRoute(album: album)); + }, ), - _QuickFilterButtonRow( - filterMode: filterMode, - onChangeFilter: changeFilter, - onSearch: onSearch, - searchController: searchController, - ), - _QuickSortAndViewMode( - isGrid: isGrid, - onToggleViewMode: toggleViewMode, - ), - isGrid - ? _AlbumGrid( - albums: albums, - userId: userId, - isLoading: isLoading, - error: error, - ) - : _AlbumList( - albums: albums, - userId: userId, - isLoading: isLoading, - error: error, - ), ], ), ); } } - -class _SortButton extends ConsumerStatefulWidget { - const _SortButton(); - - @override - ConsumerState<_SortButton> createState() => _SortButtonState(); -} - -class _SortButtonState extends ConsumerState<_SortButton> { - RemoteAlbumSortMode albumSortOption = RemoteAlbumSortMode.lastModified; - bool albumSortIsReverse = true; - - void onMenuTapped(RemoteAlbumSortMode sortMode) { - final selected = albumSortOption == sortMode; - // Switch direction - if (selected) { - setState(() { - albumSortIsReverse = !albumSortIsReverse; - }); - ref.read(remoteAlbumProvider.notifier).sortFilteredAlbums( - sortMode, - isReverse: albumSortIsReverse, - ); - } else { - setState(() { - albumSortOption = sortMode; - }); - ref.read(remoteAlbumProvider.notifier).sortFilteredAlbums( - sortMode, - isReverse: albumSortIsReverse, - ); - } - } - - @override - Widget build(BuildContext context) { - return MenuAnchor( - style: MenuStyle( - elevation: const WidgetStatePropertyAll(1), - shape: WidgetStateProperty.all( - const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(24), - ), - ), - ), - padding: const WidgetStatePropertyAll( - EdgeInsets.all(4), - ), - ), - consumeOutsideTap: true, - menuChildren: RemoteAlbumSortMode.values - .map( - (sortMode) => MenuItemButton( - leadingIcon: albumSortOption == sortMode - ? albumSortIsReverse - ? Icon( - Icons.keyboard_arrow_down, - color: albumSortOption == sortMode - ? context.colorScheme.onPrimary - : context.colorScheme.onSurface, - ) - : Icon( - Icons.keyboard_arrow_up_rounded, - color: albumSortOption == sortMode - ? context.colorScheme.onPrimary - : context.colorScheme.onSurface, - ) - : const Icon(Icons.abc, color: Colors.transparent), - onPressed: () => onMenuTapped(sortMode), - style: ButtonStyle( - padding: WidgetStateProperty.all( - const EdgeInsets.fromLTRB(16, 16, 32, 16), - ), - backgroundColor: WidgetStateProperty.all( - albumSortOption == sortMode - ? context.colorScheme.primary - : Colors.transparent, - ), - shape: WidgetStateProperty.all( - const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(24), - ), - ), - ), - ), - child: Text( - sortMode.key.t(context: context), - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w600, - color: albumSortOption == sortMode - ? context.colorScheme.onPrimary - : context.colorScheme.onSurface.withAlpha(185), - ), - ), - ), - ) - .toList(), - builder: (context, controller, child) { - return GestureDetector( - onTap: () { - if (controller.isOpen) { - controller.close(); - } else { - controller.open(); - } - }, - child: Row( - children: [ - Padding( - padding: const EdgeInsets.only(right: 5), - child: albumSortIsReverse - ? const Icon( - Icons.keyboard_arrow_down, - ) - : const Icon( - Icons.keyboard_arrow_up_rounded, - ), - ), - Text( - albumSortOption.key.t(context: context), - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - color: context.colorScheme.onSurface.withAlpha(225), - ), - ), - ], - ), - ); - }, - ); - } -} - -class _SearchBar extends StatelessWidget { - const _SearchBar({ - required this.searchController, - required this.searchFocusNode, - required this.onSearch, - required this.filterMode, - required this.onClearSearch, - }); - - final TextEditingController searchController; - final FocusNode searchFocusNode; - final void Function(String, QuickFilterMode) onSearch; - final QuickFilterMode filterMode; - final VoidCallback onClearSearch; - - @override - Widget build(BuildContext context) { - return SliverPadding( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), - sliver: SliverToBoxAdapter( - child: Container( - decoration: BoxDecoration( - border: Border.all( - color: context.colorScheme.onSurface.withAlpha(0), - width: 0, - ), - borderRadius: const BorderRadius.all( - Radius.circular(24), - ), - gradient: LinearGradient( - colors: [ - context.colorScheme.primary.withValues(alpha: 0.075), - context.colorScheme.primary.withValues(alpha: 0.09), - context.colorScheme.primary.withValues(alpha: 0.075), - ], - begin: Alignment.topLeft, - end: Alignment.bottomRight, - transform: const GradientRotation(0.5 * pi), - ), - ), - child: SearchField( - autofocus: false, - contentPadding: const EdgeInsets.all(16), - hintText: 'search_albums'.tr(), - prefixIcon: const Icon(Icons.search_rounded), - suffixIcon: searchController.text.isNotEmpty - ? IconButton( - icon: const Icon(Icons.clear_rounded), - onPressed: onClearSearch, - ) - : null, - controller: searchController, - onChanged: (_) => onSearch(searchController.text, filterMode), - focusNode: searchFocusNode, - onTapOutside: (_) => searchFocusNode.unfocus(), - ), - ), - ), - ); - } -} - -class _QuickFilterButtonRow extends StatelessWidget { - const _QuickFilterButtonRow({ - required this.filterMode, - required this.onChangeFilter, - required this.onSearch, - required this.searchController, - }); - - final QuickFilterMode filterMode; - final void Function(QuickFilterMode) onChangeFilter; - final void Function(String, QuickFilterMode) onSearch; - final TextEditingController searchController; - - @override - Widget build(BuildContext context) { - return SliverPadding( - padding: const EdgeInsets.symmetric(horizontal: 16), - sliver: SliverToBoxAdapter( - child: Wrap( - spacing: 4, - runSpacing: 4, - children: [ - _QuickFilterButton( - label: 'all'.tr(), - isSelected: filterMode == QuickFilterMode.all, - onTap: () { - onChangeFilter(QuickFilterMode.all); - onSearch(searchController.text, QuickFilterMode.all); - }, - ), - _QuickFilterButton( - label: 'shared_with_me'.tr(), - isSelected: filterMode == QuickFilterMode.sharedWithMe, - onTap: () { - onChangeFilter(QuickFilterMode.sharedWithMe); - onSearch( - searchController.text, - QuickFilterMode.sharedWithMe, - ); - }, - ), - _QuickFilterButton( - label: 'my_albums'.tr(), - isSelected: filterMode == QuickFilterMode.myAlbums, - onTap: () { - onChangeFilter(QuickFilterMode.myAlbums); - onSearch( - searchController.text, - QuickFilterMode.myAlbums, - ); - }, - ), - ], - ), - ), - ); - } -} - -class _QuickFilterButton extends StatelessWidget { - const _QuickFilterButton({ - required this.isSelected, - required this.onTap, - required this.label, - }); - - final bool isSelected; - final VoidCallback onTap; - final String label; - - @override - Widget build(BuildContext context) { - return TextButton( - onPressed: onTap, - style: ButtonStyle( - backgroundColor: WidgetStateProperty.all( - isSelected ? context.colorScheme.primary : Colors.transparent, - ), - shape: WidgetStateProperty.all( - RoundedRectangleBorder( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), - side: BorderSide( - color: context.colorScheme.onSurface.withAlpha(25), - width: 1, - ), - ), - ), - ), - child: Text( - label, - style: TextStyle( - color: isSelected - ? context.colorScheme.onPrimary - : context.colorScheme.onSurface, - fontSize: 14, - ), - ), - ); - } -} - -class _QuickSortAndViewMode extends StatelessWidget { - const _QuickSortAndViewMode({ - required this.isGrid, - required this.onToggleViewMode, - }); - - final bool isGrid; - final VoidCallback onToggleViewMode; - - @override - Widget build(BuildContext context) { - return SliverPadding( - padding: const EdgeInsets.symmetric(horizontal: 16), - sliver: SliverToBoxAdapter( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const _SortButton(), - IconButton( - icon: Icon( - isGrid ? Icons.view_list_outlined : Icons.grid_view_outlined, - size: 24, - ), - onPressed: onToggleViewMode, - ), - ], - ), - ), - ); - } -} - -class _AlbumList extends ConsumerWidget { - const _AlbumList({ - required this.isLoading, - required this.error, - required this.albums, - required this.userId, - }); - - final bool isLoading; - final String? error; - final List albums; - final String? userId; - - @override - Widget build(BuildContext context, WidgetRef ref) { - if (isLoading) { - return const SliverToBoxAdapter( - child: Center( - child: Padding( - padding: EdgeInsets.all(20.0), - child: CircularProgressIndicator(), - ), - ), - ); - } - - if (error != null) { - return SliverToBoxAdapter( - child: Center( - child: Padding( - padding: const EdgeInsets.all(20.0), - child: Text( - 'Error loading albums: $error', - style: TextStyle( - color: context.colorScheme.error, - ), - ), - ), - ), - ); - } - - if (albums.isEmpty) { - return const SliverToBoxAdapter( - child: Center( - child: Padding( - padding: EdgeInsets.all(20.0), - child: Text('No albums found'), - ), - ), - ); - } - - return SliverPadding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), - sliver: SliverList.builder( - itemBuilder: (_, index) { - final album = albums[index]; - - return Padding( - padding: const EdgeInsets.only( - bottom: 8.0, - ), - child: LargeLeadingTile( - title: Text( - album.name, - maxLines: 2, - overflow: TextOverflow.ellipsis, - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w600, - ), - ), - subtitle: Text( - '${'items_count'.t( - context: context, - args: { - 'count': album.assetCount, - }, - )} • ${album.ownerId != userId ? 'shared_by_user'.t( - context: context, - args: { - 'user': album.ownerName, - }, - ) : 'owned'.t(context: context)}', - overflow: TextOverflow.ellipsis, - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), - ), - onTap: () { - ref.read(currentRemoteAlbumProvider.notifier).setAlbum(album); - context.router.push( - RemoteAlbumRoute(album: album), - ); - }, - leadingPadding: const EdgeInsets.only( - right: 16, - ), - leading: album.thumbnailAssetId != null - ? ClipRRect( - borderRadius: const BorderRadius.all( - Radius.circular(15), - ), - child: SizedBox( - width: 80, - height: 80, - child: Thumbnail( - remoteId: album.thumbnailAssetId, - ), - ), - ) - : SizedBox( - width: 80, - height: 80, - child: Container( - decoration: BoxDecoration( - color: context.colorScheme.surfaceContainer, - borderRadius: - const BorderRadius.all(Radius.circular(16)), - border: Border.all( - color: context.colorScheme.outline.withAlpha(50), - width: 1, - ), - ), - child: const Icon( - Icons.photo_album_rounded, - size: 24, - color: Colors.grey, - ), - ), - ), - ), - ); - }, - itemCount: albums.length, - ), - ); - } -} - -class _AlbumGrid extends StatelessWidget { - const _AlbumGrid({ - required this.albums, - required this.userId, - required this.isLoading, - required this.error, - }); - - final List albums; - final String? userId; - final bool isLoading; - final String? error; - - @override - Widget build(BuildContext context) { - if (isLoading) { - return const SliverToBoxAdapter( - child: Center( - child: Padding( - padding: EdgeInsets.all(20.0), - child: CircularProgressIndicator(), - ), - ), - ); - } - - if (error != null) { - return SliverToBoxAdapter( - child: Center( - child: Padding( - padding: const EdgeInsets.all(20.0), - child: Text( - 'Error loading albums: $error', - style: TextStyle( - color: context.colorScheme.error, - ), - ), - ), - ), - ); - } - - if (albums.isEmpty) { - return const SliverToBoxAdapter( - child: Center( - child: Padding( - padding: EdgeInsets.all(20.0), - child: Text('No albums found'), - ), - ), - ); - } - - return SliverPadding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), - sliver: SliverGrid( - gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( - maxCrossAxisExtent: 250, - mainAxisSpacing: 4, - crossAxisSpacing: 4, - childAspectRatio: .7, - ), - delegate: SliverChildBuilderDelegate( - (context, index) { - final album = albums[index]; - return _GridAlbumCard( - album: album, - userId: userId, - ); - }, - childCount: albums.length, - ), - ), - ); - } -} - -class _GridAlbumCard extends ConsumerWidget { - const _GridAlbumCard({ - required this.album, - required this.userId, - }); - - final RemoteAlbum album; - final String? userId; - - @override - Widget build(BuildContext context, WidgetRef ref) { - return GestureDetector( - onTap: () { - ref.read(currentRemoteAlbumProvider.notifier).setAlbum(album); - context.router.push( - RemoteAlbumRoute(album: album), - ); - }, - child: Card( - elevation: 0, - color: context.colorScheme.surfaceBright, - shape: RoundedRectangleBorder( - borderRadius: const BorderRadius.all( - Radius.circular(16), - ), - side: BorderSide( - color: context.colorScheme.onSurface.withAlpha(25), - width: 1, - ), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - flex: 2, - child: ClipRRect( - borderRadius: const BorderRadius.vertical( - top: Radius.circular(15), - ), - child: SizedBox( - width: double.infinity, - child: album.thumbnailAssetId != null - ? Thumbnail( - remoteId: album.thumbnailAssetId, - ) - : Container( - color: context.colorScheme.surfaceContainerHighest, - child: const Icon( - Icons.photo_album_rounded, - size: 40, - color: Colors.grey, - ), - ), - ), - ), - ), - Expanded( - flex: 1, - child: Padding( - padding: const EdgeInsets.all(12.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Text( - album.name, - maxLines: 2, - overflow: TextOverflow.ellipsis, - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w600, - ), - ), - Text( - '${'items_count'.t( - context: context, - args: { - 'count': album.assetCount, - }, - )} • ${album.ownerId != userId ? 'shared_by_user'.t( - context: context, - args: { - 'user': album.ownerName, - }, - ) : 'owned'.t(context: context)}', - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: context.textTheme.labelMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), - ), - ], - ), - ), - ), - ], - ), - ), - ); - } -} diff --git a/mobile/lib/presentation/pages/drift_archive.page.dart b/mobile/lib/presentation/pages/drift_archive.page.dart index 90b8dcb646..ee8417bcad 100644 --- a/mobile/lib/presentation/pages/drift_archive.page.dart +++ b/mobile/lib/presentation/pages/drift_archive.page.dart @@ -16,19 +16,16 @@ class DriftArchivePage extends StatelessWidget { Widget build(BuildContext context) { return ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final user = ref.watch(currentUserProvider); - if (user == null) { - throw Exception('User must be logged in to access archive'); - } + timelineServiceProvider.overrideWith((ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to access archive'); + } - final timelineService = - ref.watch(timelineFactoryProvider).archive(user.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + final timelineService = ref.watch(timelineFactoryProvider).archive(user.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], child: Timeline( appBar: MesmerizingSliverAppBar( diff --git a/mobile/lib/presentation/pages/drift_asset_selection_timeline.page.dart b/mobile/lib/presentation/pages/drift_asset_selection_timeline.page.dart index 7ac378e4f5..19f813cdb5 100644 --- a/mobile/lib/presentation/pages/drift_asset_selection_timeline.page.dart +++ b/mobile/lib/presentation/pages/drift_asset_selection_timeline.page.dart @@ -10,10 +10,7 @@ import 'package:immich_mobile/providers/user.provider.dart'; @RoutePage() class DriftAssetSelectionTimelinePage extends ConsumerWidget { final Set lockedSelectionAssets; - const DriftAssetSelectionTimelinePage({ - super.key, - this.lockedSelectionAssets = const {}, - }); + const DriftAssetSelectionTimelinePage({super.key, this.lockedSelectionAssets = const {}}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -21,28 +18,19 @@ class DriftAssetSelectionTimelinePage extends ConsumerWidget { overrides: [ multiSelectProvider.overrideWith( () => MultiSelectNotifier( - MultiSelectState( - selectedAssets: {}, - lockedSelectionAssets: lockedSelectionAssets, - forceEnable: true, - ), + MultiSelectState(selectedAssets: {}, lockedSelectionAssets: lockedSelectionAssets, forceEnable: true), ), ), - timelineServiceProvider.overrideWith( - (ref) { - final user = ref.watch(currentUserProvider); - if (user == null) { - throw Exception( - 'User must be logged in to access asset selection timeline', - ); - } + timelineServiceProvider.overrideWith((ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to access asset selection timeline'); + } - final timelineService = - ref.watch(timelineFactoryProvider).remoteAssets(user.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + final timelineService = ref.watch(timelineFactoryProvider).remoteAssets(user.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], child: const Timeline(), ); diff --git a/mobile/lib/presentation/pages/drift_create_album.page.dart b/mobile/lib/presentation/pages/drift_create_album.page.dart index f6ba98f61c..bac23b45c7 100644 --- a/mobile/lib/presentation/pages/drift_create_album.page.dart +++ b/mobile/lib/presentation/pages/drift_create_album.page.dart @@ -15,8 +15,7 @@ class DriftCreateAlbumPage extends ConsumerStatefulWidget { const DriftCreateAlbumPage({super.key}); @override - ConsumerState createState() => - _DriftCreateAlbumPageState(); + ConsumerState createState() => _DriftCreateAlbumPageState(); } class _DriftCreateAlbumPageState extends ConsumerState { @@ -71,12 +70,7 @@ class _DriftCreateAlbumPageState extends ConsumerState { Widget _buildContent() { if (selectedAssets.isEmpty) { - return SliverList( - delegate: SliverChildListDelegate([ - _buildEmptyState(), - _buildSelectPhotosButton(), - ]), - ); + return SliverList(delegate: SliverChildListDelegate([_buildEmptyState(), _buildSelectPhotosButton()])); } else { return _buildSelectedImageGrid(); } @@ -85,10 +79,7 @@ class _DriftCreateAlbumPageState extends ConsumerState { Widget _buildEmptyState() { return Padding( padding: const EdgeInsets.only(top: 0, left: 18), - child: Text( - 'create_shared_album_page_share_add_assets', - style: context.textTheme.labelLarge, - ).t(), + child: Text('create_shared_album_page_share_add_assets', style: context.textTheme.labelLarge).t(), ); } @@ -98,27 +89,17 @@ class _DriftCreateAlbumPageState extends ConsumerState { child: FilledButton.icon( style: FilledButton.styleFrom( alignment: Alignment.centerLeft, - padding: const EdgeInsets.symmetric( - vertical: 24.0, - horizontal: 16.0, - ), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10.0)), - ), + padding: const EdgeInsets.symmetric(vertical: 24.0, horizontal: 16.0), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10.0))), backgroundColor: context.colorScheme.surfaceContainerHigh, ), onPressed: onSelectPhotos, icon: Icon(Icons.add_rounded, color: context.primaryColor), label: Padding( - padding: const EdgeInsets.only( - left: 8.0, - ), + padding: const EdgeInsets.only(left: 8.0), child: Text( 'create_shared_album_page_share_select_photos', - style: context.textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.w600, - color: context.primaryColor, - ), + style: context.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w600, color: context.primaryColor), ).t(), ), ), @@ -134,16 +115,13 @@ class _DriftCreateAlbumPageState extends ConsumerState { crossAxisSpacing: 1.0, mainAxisSpacing: 1.0, ), - delegate: SliverChildBuilderDelegate( - (context, index) { - final asset = selectedAssets.elementAt(index); - return GestureDetector( - onTap: onBackgroundTapped, - child: Thumbnail(asset: asset), - ); - }, - childCount: selectedAssets.length, - ), + delegate: SliverChildBuilderDelegate((context, index) { + final asset = selectedAssets.elementAt(index); + return GestureDetector( + onTap: onBackgroundTapped, + child: Thumbnail(asset: asset), + ); + }, childCount: selectedAssets.length), ), ); } @@ -163,9 +141,7 @@ class _DriftCreateAlbumPageState extends ConsumerState { Future onSelectPhotos() async { final assets = await context.pushRoute>( - DriftAssetSelectionTimelineRoute( - lockedSelectionAssets: selectedAssets, - ), + DriftAssetSelectionTimelineRoute(lockedSelectionAssets: selectedAssets), ); if (assets == null || assets.isEmpty) { @@ -184,16 +160,15 @@ class _DriftCreateAlbumPageState extends ConsumerState { if (title.isEmpty) { if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('create_album_title_required'.t()), - backgroundColor: context.colorScheme.error, - ), + SnackBar(content: Text('create_album_title_required'.t()), backgroundColor: context.colorScheme.error), ); } return; } - final album = await ref.watch(remoteAlbumProvider.notifier).createAlbum( + final album = await ref + .watch(remoteAlbumProvider.notifier) + .createAlbum( title: title, description: albumDescriptionController.text.trim(), assetIds: selectedAssets.map((asset) { @@ -204,18 +179,13 @@ class _DriftCreateAlbumPageState extends ConsumerState { if (album != null) { ref.read(currentRemoteAlbumProvider.notifier).setAlbum(album); - context.replaceRoute( - RemoteAlbumRoute(album: album), - ); + context.replaceRoute(RemoteAlbumRoute(album: album)); } } Widget buildTitleInputField() { return Padding( - padding: const EdgeInsets.only( - right: 10.0, - left: 10.0, - ), + padding: const EdgeInsets.only(right: 10.0, left: 10.0), child: _AlbumTitleTextField( focusNode: albumTitleTextFieldFocusNode, textController: albumTitleController, @@ -231,11 +201,7 @@ class _DriftCreateAlbumPageState extends ConsumerState { Widget buildDescriptionInputField() { return Padding( - padding: const EdgeInsets.only( - right: 10.0, - left: 10.0, - top: 8, - ), + padding: const EdgeInsets.only(right: 10.0, left: 10.0, top: 8), child: _AlbumViewerEditableDescription( textController: albumDescriptionController, focusNode: albumDescriptionTextFieldFocusNode, @@ -245,11 +211,7 @@ class _DriftCreateAlbumPageState extends ConsumerState { Widget buildControlButton() { return Padding( - padding: const EdgeInsets.only( - left: 12.0, - top: 8.0, - bottom: 8.0, - ), + padding: const EdgeInsets.only(left: 12.0, top: 8.0, bottom: 8.0), child: SizedBox( height: 42.0, child: ListView( @@ -273,10 +235,7 @@ class _DriftCreateAlbumPageState extends ConsumerState { elevation: 0, centerTitle: false, backgroundColor: context.scaffoldBackgroundColor, - leading: IconButton( - onPressed: () => context.maybePop(), - icon: const Icon(Icons.close_rounded), - ), + leading: IconButton(onPressed: () => context.maybePop(), icon: const Icon(Icons.close_rounded)), title: const Text('create_album').t(), actions: [ TextButton( @@ -285,9 +244,7 @@ class _DriftCreateAlbumPageState extends ConsumerState { 'create'.t(), style: TextStyle( fontWeight: FontWeight.bold, - color: _canCreateAlbum - ? context.primaryColor - : context.themeData.disabledColor, + color: _canCreateAlbum ? context.primaryColor : context.themeData.disabledColor, ), ), ), @@ -295,12 +252,7 @@ class _DriftCreateAlbumPageState extends ConsumerState { ), body: GestureDetector( onTap: onBackgroundTapped, - child: CustomScrollView( - slivers: [ - _buildSliverAppBar(), - _buildContent(), - ], - ), + child: CustomScrollView(slivers: [_buildSliverAppBar(), _buildContent()]), ), ); } @@ -344,48 +296,31 @@ class _AlbumTitleTextFieldState extends State<_AlbumTitleTextField> { Widget build(BuildContext context) { return TextField( focusNode: widget.focusNode, - style: TextStyle( - fontSize: 28.0, - color: context.colorScheme.onSurface, - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 28.0, color: context.colorScheme.onSurface, fontWeight: FontWeight.bold), controller: widget.textController, onTap: () { - if (widget.textController.text == - 'create_album_page_untitled'.t(context: context)) { + if (widget.textController.text == 'create_album_page_untitled'.t(context: context)) { widget.textController.clear(); } }, decoration: InputDecoration( - contentPadding: const EdgeInsets.symmetric( - horizontal: 8.0, - vertical: 16.0, - ), + contentPadding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 16.0), suffixIcon: widget.textController.text.isNotEmpty && widget.isFocus ? IconButton( onPressed: () { widget.textController.clear(); }, - icon: Icon( - Icons.cancel_rounded, - color: context.primaryColor, - ), + icon: Icon(Icons.cancel_rounded, color: context.primaryColor), splashRadius: 10.0, ) : null, enabledBorder: const OutlineInputBorder( borderSide: BorderSide(color: Colors.transparent), - borderRadius: BorderRadius.all( - Radius.circular(16.0), - ), + borderRadius: BorderRadius.all(Radius.circular(16.0)), ), focusedBorder: OutlineInputBorder( - borderSide: BorderSide( - color: context.primaryColor.withValues(alpha: 0.3), - ), - borderRadius: const BorderRadius.all( - Radius.circular(16.0), - ), + borderSide: BorderSide(color: context.primaryColor.withValues(alpha: 0.3)), + borderRadius: const BorderRadius.all(Radius.circular(16.0)), ), hintText: 'add_a_title'.t(), hintStyle: context.themeData.inputDecorationTheme.hintStyle?.copyWith( @@ -402,21 +337,16 @@ class _AlbumTitleTextFieldState extends State<_AlbumTitleTextField> { } class _AlbumViewerEditableDescription extends StatefulWidget { - const _AlbumViewerEditableDescription({ - required this.textController, - required this.focusNode, - }); + const _AlbumViewerEditableDescription({required this.textController, required this.focusNode}); final TextEditingController textController; final FocusNode focusNode; @override - State<_AlbumViewerEditableDescription> createState() => - _AlbumViewerEditableDescriptionState(); + State<_AlbumViewerEditableDescription> createState() => _AlbumViewerEditableDescriptionState(); } -class _AlbumViewerEditableDescriptionState - extends State<_AlbumViewerEditableDescription> { +class _AlbumViewerEditableDescriptionState extends State<_AlbumViewerEditableDescription> { @override void initState() { super.initState(); @@ -454,38 +384,23 @@ class _AlbumViewerEditableDescriptionState minLines: 1, controller: widget.textController, decoration: InputDecoration( - contentPadding: const EdgeInsets.symmetric( - horizontal: 12.0, - vertical: 16.0, - ), - suffixIcon: - widget.focusNode.hasFocus && widget.textController.text.isNotEmpty - ? IconButton( - onPressed: () { - widget.textController.clear(); - }, - icon: Icon( - Icons.cancel_rounded, - color: context.primaryColor, - ), - splashRadius: 10.0, - ) - : null, + contentPadding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 16.0), + suffixIcon: widget.focusNode.hasFocus && widget.textController.text.isNotEmpty + ? IconButton( + onPressed: () { + widget.textController.clear(); + }, + icon: Icon(Icons.cancel_rounded, color: context.primaryColor), + splashRadius: 10.0, + ) + : null, enabledBorder: OutlineInputBorder( - borderSide: BorderSide( - color: context.colorScheme.outline.withValues(alpha: 0.3), - ), - borderRadius: const BorderRadius.all( - Radius.circular(16.0), - ), + borderSide: BorderSide(color: context.colorScheme.outline.withValues(alpha: 0.3)), + borderRadius: const BorderRadius.all(Radius.circular(16.0)), ), focusedBorder: OutlineInputBorder( - borderSide: BorderSide( - color: context.primaryColor.withValues(alpha: 0.3), - ), - borderRadius: const BorderRadius.all( - Radius.circular(16.0), - ), + borderSide: BorderSide(color: context.primaryColor.withValues(alpha: 0.3)), + borderRadius: const BorderRadius.all(Radius.circular(16.0)), ), hintStyle: context.themeData.inputDecorationTheme.hintStyle?.copyWith( fontSize: 16.0, diff --git a/mobile/lib/presentation/pages/drift_favorite.page.dart b/mobile/lib/presentation/pages/drift_favorite.page.dart index 90a273f93b..2bc3f26363 100644 --- a/mobile/lib/presentation/pages/drift_favorite.page.dart +++ b/mobile/lib/presentation/pages/drift_favorite.page.dart @@ -16,19 +16,16 @@ class DriftFavoritePage extends StatelessWidget { Widget build(BuildContext context) { return ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final user = ref.watch(currentUserProvider); - if (user == null) { - throw Exception('User must be logged in to access favorite'); - } + timelineServiceProvider.overrideWith((ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to access favorite'); + } - final timelineService = - ref.watch(timelineFactoryProvider).favorite(user.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + final timelineService = ref.watch(timelineFactoryProvider).favorite(user.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], child: Timeline( appBar: MesmerizingSliverAppBar( diff --git a/mobile/lib/presentation/pages/drift_library.page.dart b/mobile/lib/presentation/pages/drift_library.page.dart index 552733980e..af7014bbdc 100644 --- a/mobile/lib/presentation/pages/drift_library.page.dart +++ b/mobile/lib/presentation/pages/drift_library.page.dart @@ -6,15 +6,15 @@ import 'package:immich_mobile/extensions/asyncvalue_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/images/local_album_thumbnail.widget.dart'; +import 'package:immich_mobile/presentation/widgets/people/partner_user_avatar.widget.dart'; import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; -import 'package:immich_mobile/providers/partner.provider.dart'; -import 'package:immich_mobile/providers/search/people.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/partner.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/people.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/services/api.service.dart'; import 'package:immich_mobile/utils/image_url_builder.dart'; import 'package:immich_mobile/widgets/common/immich_sliver_app_bar.dart'; -import 'package:immich_mobile/widgets/common/user_avatar.dart'; import 'package:immich_mobile/widgets/map/map_thumbnail.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; @@ -27,12 +27,7 @@ class DriftLibraryPage extends ConsumerWidget { return const Scaffold( body: CustomScrollView( slivers: [ - ImmichSliverAppBar( - snap: false, - floating: false, - pinned: true, - showUploadButton: false, - ), + ImmichSliverAppBar(snap: false, floating: false, pinned: true, showUploadButton: false), _ActionButtonGrid(), _CollectionCards(), _QuickAccessButtonList(), @@ -47,9 +42,7 @@ class _ActionButtonGrid extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final isTrashEnable = ref.watch( - serverInfoProvider.select((state) => state.serverFeatures.trash), - ); + final isTrashEnable = ref.watch(serverInfoProvider.select((state) => state.serverFeatures.trash)); return SliverPadding( padding: const EdgeInsets.only(left: 16, top: 16, right: 16, bottom: 12), @@ -79,9 +72,7 @@ class _ActionButtonGrid extends ConsumerWidget { onTap: () => context.pushRoute(const SharedLinkRoute()), label: 'shared_links'.t(context: context), ), - isTrashEnable - ? const SizedBox(width: 8) - : const SizedBox.shrink(), + isTrashEnable ? const SizedBox(width: 8) : const SizedBox.shrink(), isTrashEnable ? _ActionButton( icon: Icons.delete_outline_rounded, @@ -99,11 +90,7 @@ class _ActionButtonGrid extends ConsumerWidget { } class _ActionButton extends StatelessWidget { - const _ActionButton({ - required this.icon, - required this.onTap, - required this.label, - }); + const _ActionButton({required this.icon, required this.onTap, required this.label}); final IconData icon; final VoidCallback onTap; @@ -116,13 +103,7 @@ class _ActionButton extends StatelessWidget { onPressed: onTap, label: Padding( padding: const EdgeInsets.only(left: 4.0), - child: Text( - label, - style: TextStyle( - color: context.colorScheme.onSurface, - fontSize: 15, - ), - ), + child: Text(label, style: TextStyle(color: context.colorScheme.onSurface, fontSize: 15)), ), style: FilledButton.styleFrom( elevation: 0, @@ -131,16 +112,10 @@ class _ActionButton extends StatelessWidget { alignment: Alignment.centerLeft, shape: RoundedRectangleBorder( borderRadius: const BorderRadius.all(Radius.circular(25)), - side: BorderSide( - color: context.colorScheme.onSurface.withAlpha(10), - width: 1, - ), + side: BorderSide(color: context.colorScheme.onSurface.withAlpha(10), width: 1), ), ), - icon: Icon( - icon, - color: context.primaryColor, - ), + icon: Icon(icon, color: context.primaryColor), ), ); } @@ -157,11 +132,7 @@ class _CollectionCards extends StatelessWidget { child: Wrap( spacing: 8, runSpacing: 8, - children: [ - _PeopleCollectionCard(), - _PlacesCollectionCard(), - _LocalAlbumsCollectionCard(), - ], + children: [_PeopleCollectionCard(), _PlacesCollectionCard(), _LocalAlbumsCollectionCard()], ), ), ); @@ -173,7 +144,7 @@ class _PeopleCollectionCard extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final people = ref.watch(getAllPeopleProvider); + final people = ref.watch(driftGetAllPeopleProvider); return LayoutBuilder( builder: (context, constraints) { @@ -182,7 +153,7 @@ class _PeopleCollectionCard extends ConsumerWidget { final size = context.width * widthFactor - 20.0; return GestureDetector( - onTap: () => context.pushRoute(const PeopleCollectionRoute()), + onTap: () => context.pushRoute(const DriftPeopleCollectionRoute()), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -190,22 +161,15 @@ class _PeopleCollectionCard extends ConsumerWidget { height: size, width: size, decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), + borderRadius: const BorderRadius.all(Radius.circular(20)), gradient: LinearGradient( - colors: [ - context.colorScheme.primary.withAlpha(30), - context.colorScheme.primary.withAlpha(25), - ], + colors: [context.colorScheme.primary.withAlpha(30), context.colorScheme.primary.withAlpha(25)], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), ), child: people.widgetWhen( - onLoading: () => const Center( - child: CircularProgressIndicator(), - ), + onLoading: () => const Center(child: CircularProgressIndicator()), onData: (people) { return GridView.count( crossAxisCount: 2, @@ -255,9 +219,7 @@ class _PlacesCollectionCard extends StatelessWidget { final size = context.width * widthFactor - 20.0; return GestureDetector( - onTap: () => context.pushRoute( - DriftPlaceRoute(currentLocation: null), - ), + onTap: () => context.pushRoute(DriftPlaceRoute(currentLocation: null)), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -267,20 +229,14 @@ class _PlacesCollectionCard extends StatelessWidget { child: DecoratedBox( decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(20)), - color: - context.colorScheme.secondaryContainer.withAlpha(100), + color: context.colorScheme.secondaryContainer.withAlpha(100), ), child: IgnorePointer( child: MapThumbnail( zoom: 8, - centre: const LatLng( - 21.44950, - -157.91959, - ), + centre: const LatLng(21.44950, -157.91959), showAttribution: false, - themeMode: context.isDarkTheme - ? ThemeMode.dark - : ThemeMode.light, + themeMode: context.isDarkTheme ? ThemeMode.dark : ThemeMode.light, ), ), ), @@ -328,10 +284,7 @@ class _LocalAlbumsCollectionCard extends ConsumerWidget { decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(20)), gradient: LinearGradient( - colors: [ - context.colorScheme.primary.withAlpha(30), - context.colorScheme.primary.withAlpha(25), - ], + colors: [context.colorScheme.primary.withAlpha(30), context.colorScheme.primary.withAlpha(25)], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), @@ -345,24 +298,14 @@ class _LocalAlbumsCollectionCard extends ConsumerWidget { children: albums.when( data: (data) { return data.take(4).map((album) { - return LocalAlbumThumbnail( - albumId: album.id, - ); + return LocalAlbumThumbnail(albumId: album.id); }).toList(); }, error: (error, _) { - return [ - Center( - child: Text('Error: $error'), - ), - ]; + return [Center(child: Text('Error: $error'))]; }, loading: () { - return [ - const Center( - child: CircularProgressIndicator(), - ), - ]; + return [const Center(child: CircularProgressIndicator())]; }, ), ), @@ -391,20 +334,16 @@ class _QuickAccessButtonList extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final partners = ref.watch(partnerSharedWithProvider); + final partnerSharedWithAsync = ref.watch(driftSharedWithPartnerProvider); + final partners = partnerSharedWithAsync.valueOrNull ?? []; return SliverPadding( padding: const EdgeInsets.only(left: 16, top: 12, right: 16, bottom: 32), sliver: SliverToBoxAdapter( child: Container( decoration: BoxDecoration( - border: Border.all( - color: context.colorScheme.onSurface.withAlpha(10), - width: 1, - ), - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), + border: Border.all(color: context.colorScheme.onSurface.withAlpha(10), width: 1), + borderRadius: const BorderRadius.all(Radius.circular(20)), gradient: LinearGradient( colors: [ context.colorScheme.primary.withAlpha(10), @@ -429,44 +368,28 @@ class _QuickAccessButtonList extends ConsumerWidget { bottomRight: Radius.circular(partners.isEmpty ? 20 : 0), ), ), - leading: const Icon( - Icons.folder_outlined, - size: 26, - ), + leading: const Icon(Icons.folder_outlined, size: 26), title: Text( 'folders'.t(context: context), - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500), ), onTap: () => context.pushRoute(FolderRoute()), ), ListTile( - leading: const Icon( - Icons.lock_outline_rounded, - size: 26, - ), + leading: const Icon(Icons.lock_outline_rounded, size: 26), title: Text( 'locked_folder'.t(context: context), - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500), ), - // TODO: PIN code is needed onTap: () => context.pushRoute(const DriftLockedFolderRoute()), ), ListTile( - leading: const Icon( - Icons.group_outlined, - size: 26, - ), + leading: const Icon(Icons.group_outlined, size: 26), title: Text( 'partners'.t(context: context), - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500), ), - onTap: () => context.pushRoute(const PartnerRoute()), + onTap: () => context.pushRoute(const DriftPartnerRoute()), ), _PartnerList(partners: partners), ], @@ -480,7 +403,7 @@ class _QuickAccessButtonList extends ConsumerWidget { class _PartnerList extends StatelessWidget { const _PartnerList({required this.partners}); - final List partners; + final List partners; @override Widget build(BuildContext context) { @@ -499,20 +422,13 @@ class _PartnerList extends StatelessWidget { bottomRight: Radius.circular(isLastItem ? 20 : 0), ), ), - contentPadding: const EdgeInsets.only( - left: 12.0, - right: 18.0, - ), - leading: userAvatar(context, partner, radius: 16), + contentPadding: const EdgeInsets.only(left: 12.0, right: 18.0), + leading: PartnerUserAvatar(partner: partner), title: const Text( "partner_list_user_photos", - style: TextStyle( - fontWeight: FontWeight.w500, - ), + style: TextStyle(fontWeight: FontWeight.w500), ).t(context: context, args: {'user': partner.name}), - onTap: () => context.pushRoute( - DriftPartnerDetailRoute(partner: partner), - ), + onTap: () => context.pushRoute(DriftPartnerDetailRoute(partner: partner)), ); }, ); diff --git a/mobile/lib/presentation/pages/drift_local_album.page.dart b/mobile/lib/presentation/pages/drift_local_album.page.dart index 0ad9abd2fa..536d9d82e2 100644 --- a/mobile/lib/presentation/pages/drift_local_album.page.dart +++ b/mobile/lib/presentation/pages/drift_local_album.page.dart @@ -16,14 +16,7 @@ class DriftLocalAlbumsPage extends StatelessWidget { @override Widget build(BuildContext context) { - return const Scaffold( - body: CustomScrollView( - slivers: [ - LocalAlbumsSliverAppBar(), - _AlbumList(), - ], - ), - ); + return const Scaffold(body: CustomScrollView(slivers: [LocalAlbumsSliverAppBar(), _AlbumList()])); } } @@ -37,10 +30,7 @@ class _AlbumList extends ConsumerWidget { return albums.when( loading: () => const SliverToBoxAdapter( child: Center( - child: Padding( - padding: EdgeInsets.all(20.0), - child: CircularProgressIndicator(), - ), + child: Padding(padding: EdgeInsets.all(20.0), child: CircularProgressIndicator()), ), ), error: (error, stack) => SliverToBoxAdapter( @@ -49,9 +39,7 @@ class _AlbumList extends ConsumerWidget { padding: const EdgeInsets.all(20.0), child: Text( 'Error loading albums: $error, stack: $stack', - style: TextStyle( - color: context.colorScheme.error, - ), + style: TextStyle(color: context.colorScheme.error), ), ), ), @@ -60,10 +48,7 @@ class _AlbumList extends ConsumerWidget { if (albums.isEmpty) { return const SliverToBoxAdapter( child: Center( - child: Padding( - padding: EdgeInsets.all(20.0), - child: Text('No albums found'), - ), + child: Padding(padding: EdgeInsets.all(20.0), child: Text('No albums found')), ), ); } @@ -77,33 +62,14 @@ class _AlbumList extends ConsumerWidget { return Padding( padding: const EdgeInsets.only(bottom: 8.0), child: LargeLeadingTile( - leadingPadding: const EdgeInsets.only( - right: 16, - ), - leading: SizedBox( - width: 80, - height: 80, - child: LocalAlbumThumbnail( - albumId: album.id, - ), - ), - title: Text( - album.name, - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w600, - ), - ), + leadingPadding: const EdgeInsets.only(right: 16), + leading: SizedBox(width: 80, height: 80, child: LocalAlbumThumbnail(albumId: album.id)), + title: Text(album.name, style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600)), subtitle: Text( - 'items_count'.t( - context: context, - args: {'count': album.assetCount}, - ), - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + 'items_count'.t(context: context, args: {'count': album.assetCount}), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), - onTap: () => - context.pushRoute(LocalTimelineRoute(album: album)), + onTap: () => context.pushRoute(LocalTimelineRoute(album: album)), ), ); }, diff --git a/mobile/lib/presentation/pages/drift_locked_folder.page.dart b/mobile/lib/presentation/pages/drift_locked_folder.page.dart index 9b42cdb103..b19e8468ca 100644 --- a/mobile/lib/presentation/pages/drift_locked_folder.page.dart +++ b/mobile/lib/presentation/pages/drift_locked_folder.page.dart @@ -4,38 +4,67 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/bottom_sheet/locked_folder_bottom_sheet.widget.dart'; import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart'; +import 'package:immich_mobile/providers/auth.provider.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/widgets/common/mesmerizing_sliver_app_bar.dart'; @RoutePage() -class DriftLockedFolderPage extends StatelessWidget { +class DriftLockedFolderPage extends ConsumerStatefulWidget { const DriftLockedFolderPage({super.key}); + @override + ConsumerState createState() => _DriftLockedFolderPageState(); +} + +class _DriftLockedFolderPageState extends ConsumerState with WidgetsBindingObserver { + bool _showOverlay = false; + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addObserver(this); + } + + @override + void dispose() { + WidgetsBinding.instance.removeObserver(this); + super.dispose(); + } + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + if (mounted) { + setState(() { + _showOverlay = state != AppLifecycleState.resumed; + }); + } + } + @override Widget build(BuildContext context) { return ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final user = ref.watch(currentUserProvider); - if (user == null) { - throw Exception('User must be logged in to access locked folder'); - } + timelineServiceProvider.overrideWith((ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to access locked folder'); + } - final timelineService = - ref.watch(timelineFactoryProvider).lockedFolder(user.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + final timelineService = ref.watch(timelineFactoryProvider).lockedFolder(user.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], - child: Timeline( - appBar: MesmerizingSliverAppBar( - title: 'locked_folder'.t(context: context), - ), - bottomSheet: const LockedFolderBottomSheet(), - ), + child: _showOverlay + ? const SizedBox() + : PopScope( + onPopInvokedWithResult: (didPop, _) => didPop ? ref.read(authProvider.notifier).lockPinCode() : null, + child: Timeline( + appBar: MesmerizingSliverAppBar(title: 'locked_folder'.t(context: context)), + bottomSheet: const LockedFolderBottomSheet(), + ), + ), ); } } diff --git a/mobile/lib/presentation/pages/drift_memory.page.dart b/mobile/lib/presentation/pages/drift_memory.page.dart index 7da2d1a4c7..55e5d24ecb 100644 --- a/mobile/lib/presentation/pages/drift_memory.page.dart +++ b/mobile/lib/presentation/pages/drift_memory.page.dart @@ -22,26 +22,19 @@ class DriftMemoryPage extends HookConsumerWidget { final List memories; final int memoryIndex; - const DriftMemoryPage({ - required this.memories, - required this.memoryIndex, - super.key, - }); + const DriftMemoryPage({required this.memories, required this.memoryIndex, super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final currentMemory = useState(memories[memoryIndex]); final currentAssetPage = useState(0); final currentMemoryIndex = useState(memoryIndex); - final assetProgress = useState( - "${currentAssetPage.value + 1}|${currentMemory.value.assets.length}", - ); + final assetProgress = useState("${currentAssetPage.value + 1}|${currentMemory.value.assets.length}"); const bgColor = Colors.black; final currentAsset = useState(null); /// The list of all of the asset page controllers - final memoryAssetPageControllers = - List.generate(memories.length, (i) => usePageController()); + final memoryAssetPageControllers = List.generate(memories.length, (i) => usePageController()); /// The main vertically scrolling page controller with each list of memories final memoryPageController = usePageController(initialPage: memoryIndex); @@ -56,36 +49,27 @@ class DriftMemoryPage extends HookConsumerWidget { }); toNextMemory() { - memoryPageController.nextPage( - duration: const Duration(milliseconds: 500), - curve: Curves.easeIn, - ); + memoryPageController.nextPage(duration: const Duration(milliseconds: 500), curve: Curves.easeIn); } void toPreviousMemory() { if (currentMemoryIndex.value > 0) { // Move to the previous memory page - memoryPageController.previousPage( - duration: const Duration(milliseconds: 500), - curve: Curves.easeIn, - ); + memoryPageController.previousPage(duration: const Duration(milliseconds: 500), curve: Curves.easeIn); // Wait for the next frame to ensure the page is built SchedulerBinding.instance.addPostFrameCallback((_) { final previousIndex = currentMemoryIndex.value - 1; - final previousMemoryController = - memoryAssetPageControllers[previousIndex]; + final previousMemoryController = memoryAssetPageControllers[previousIndex]; // Ensure the controller is attached if (previousMemoryController.hasClients) { - previousMemoryController - .jumpToPage(memories[previousIndex].assets.length - 1); + previousMemoryController.jumpToPage(memories[previousIndex].assets.length - 1); } else { // Wait for the next frame until it is attached SchedulerBinding.instance.addPostFrameCallback((_) { if (previousMemoryController.hasClients) { - previousMemoryController - .jumpToPage(memories[previousIndex].assets.length - 1); + previousMemoryController.jumpToPage(memories[previousIndex].assets.length - 1); } }); } @@ -96,13 +80,9 @@ class DriftMemoryPage extends HookConsumerWidget { toNextAsset(int currentAssetIndex) { if (currentAssetIndex + 1 < currentMemory.value.assets.length) { // Go to the next asset - PageController controller = - memoryAssetPageControllers[currentMemoryIndex.value]; + PageController controller = memoryAssetPageControllers[currentMemoryIndex.value]; - controller.nextPage( - curve: Curves.easeInOut, - duration: const Duration(milliseconds: 500), - ); + controller.nextPage(curve: Curves.easeInOut, duration: const Duration(milliseconds: 500)); } else { // Go to the next memory since we are at the end of our assets toNextMemory(); @@ -112,13 +92,9 @@ class DriftMemoryPage extends HookConsumerWidget { toPreviousAsset(int currentAssetIndex) { if (currentAssetIndex > 0) { // Go to the previous asset - PageController controller = - memoryAssetPageControllers[currentMemoryIndex.value]; + PageController controller = memoryAssetPageControllers[currentMemoryIndex.value]; - controller.previousPage( - curve: Curves.easeInOut, - duration: const Duration(milliseconds: 500), - ); + controller.previousPage(curve: Curves.easeInOut, duration: const Duration(milliseconds: 500)); } else { // Go to the previous memory since we are at the end of our assets toPreviousMemory(); @@ -126,8 +102,7 @@ class DriftMemoryPage extends HookConsumerWidget { } updateProgressText() { - assetProgress.value = - "${currentAssetPage.value + 1}|${currentMemory.value.assets.length}"; + assetProgress.value = "${currentAssetPage.value + 1}|${currentMemory.value.assets.length}"; } /// Downloads and caches the image for the asset at this [currentMemory]'s index @@ -167,20 +142,12 @@ class DriftMemoryPage extends HookConsumerWidget { // Precache the asset final size = MediaQuery.sizeOf(context); - await precacheImage( - getFullImageProvider( - asset, - size: Size(size.width, size.height), - ), - context, - size: size, - ); + await precacheImage(getFullImageProvider(asset, size: Size(size.width, size.height)), context, size: size); } // Precache the next page right away if we are on the first page if (currentAssetPage.value == 0) { - Future.delayed(const Duration(milliseconds: 200)) - .then((_) => precacheAsset(1)); + Future.delayed(const Duration(milliseconds: 200)).then((_) => precacheAsset(1)); } Future onAssetChanged(int otherIndex) async { @@ -212,12 +179,10 @@ class DriftMemoryPage extends HookConsumerWidget { // maxScrollExtend contains the sum of horizontal pixels of all assets for depth = 1 // or sum of vertical pixels of all memories for depth = 0 if (notification is ScrollUpdateNotification) { - final isEpiloguePage = - (memoryPageController.page?.floor() ?? 0) >= memories.length; + final isEpiloguePage = (memoryPageController.page?.floor() ?? 0) >= memories.length; final offset = notification.metrics.pixels; - if (isEpiloguePage && - (offset > notification.metrics.maxScrollExtent + 150)) { + if (isEpiloguePage && (offset > notification.metrics.maxScrollExtent + 150)) { context.maybePop(); return true; } @@ -229,9 +194,7 @@ class DriftMemoryPage extends HookConsumerWidget { backgroundColor: bgColor, body: SafeArea( child: PageView.builder( - physics: const BouncingScrollPhysics( - parent: AlwaysScrollableScrollPhysics(), - ), + physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), scrollDirection: Axis.vertical, controller: memoryPageController, onPageChanged: (pageNumber) { @@ -259,23 +222,13 @@ class DriftMemoryPage extends HookConsumerWidget { } final yearsAgo = DateTime.now().year - memories[mIndex].data.year; - final title = 'years_ago'.t( - context: context, - args: { - 'years': yearsAgo.toString(), - }, - ); + final title = 'years_ago'.t(context: context, args: {'years': yearsAgo.toString()}); // Build horizontal page final assetController = memoryAssetPageControllers[mIndex]; return Column( children: [ Padding( - padding: const EdgeInsets.only( - left: 24.0, - right: 24.0, - top: 8.0, - bottom: 2.0, - ), + padding: const EdgeInsets.only(left: 24.0, right: 24.0, top: 8.0, bottom: 2.0), child: AnimatedBuilder( animation: assetController, builder: (context, child) { @@ -295,9 +248,7 @@ class DriftMemoryPage extends HookConsumerWidget { child: Stack( children: [ PageView.builder( - physics: const BouncingScrollPhysics( - parent: AlwaysScrollableScrollPhysics(), - ), + physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), controller: assetController, onPageChanged: onAssetChanged, scrollDirection: Axis.horizontal, @@ -308,11 +259,7 @@ class DriftMemoryPage extends HookConsumerWidget { children: [ Container( color: Colors.black, - child: DriftMemoryCard( - asset: asset, - title: title, - showTitle: index == 0, - ), + child: DriftMemoryCard(asset: asset, title: title, showTitle: index == 0), ), Positioned.fill( child: Row( @@ -353,36 +300,24 @@ class DriftMemoryPage extends HookConsumerWidget { // turn off full screen mode here // https://github.com/Milad-Akarie/auto_route_library/issues/1799 context.maybePop(); - SystemChrome.setEnabledSystemUIMode( - SystemUiMode.edgeToEdge, - ); + SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); }, shape: const CircleBorder(), color: Colors.white.withValues(alpha: 0.2), elevation: 0, - child: const Icon( - Icons.close_rounded, - color: Colors.white, - ), + child: const Icon(Icons.close_rounded, color: Colors.white), ), ), - if (currentAsset.value != null && - currentAsset.value!.isVideo) + if (currentAsset.value != null && currentAsset.value!.isVideo) Positioned( bottom: 24, right: 32, - child: Icon( - Icons.videocam_outlined, - color: Colors.grey[200], - ), + child: Icon(Icons.videocam_outlined, color: Colors.grey[200]), ), ], ), ), - DriftMemoryBottomInfo( - memory: memories[mIndex], - title: title, - ), + DriftMemoryBottomInfo(memory: memories[mIndex], title: title), ], ); }, diff --git a/mobile/lib/presentation/pages/drift_partner_detail.page.dart b/mobile/lib/presentation/pages/drift_partner_detail.page.dart index baae893d39..95c5b008b3 100644 --- a/mobile/lib/presentation/pages/drift_partner_detail.page.dart +++ b/mobile/lib/presentation/pages/drift_partner_detail.page.dart @@ -6,41 +6,30 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/presentation/widgets/bottom_sheet/partner_detail_bottom_sheet.widget.dart'; import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; import 'package:immich_mobile/widgets/common/mesmerizing_sliver_app_bar.dart'; @RoutePage() class DriftPartnerDetailPage extends StatelessWidget { - final UserDto partner; + final PartnerUserDto partner; - const DriftPartnerDetailPage({ - super.key, - required this.partner, - }); + const DriftPartnerDetailPage({super.key, required this.partner}); @override Widget build(BuildContext context) { return ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final timelineService = - ref.watch(timelineFactoryProvider).remoteAssets(partner.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + timelineServiceProvider.overrideWith((ref) { + final timelineService = ref.watch(timelineFactoryProvider).remoteAssets(partner.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], child: Timeline( - appBar: MesmerizingSliverAppBar( - title: partner.name, - icon: Icons.person_outline, - ), - topSliverWidget: _InfoBox( - onTap: () => { - // TODO: Create DriftUserProvider/DriftUserService to handle this action - }, - inTimeline: partner.inTimeline, - ), + appBar: MesmerizingSliverAppBar(title: partner.name, icon: Icons.person_outline), + topSliverWidget: _InfoBox(partner: partner), topSliverWidgetHeight: 110, bottomSheet: const PartnerDetailBottomSheet(), ), @@ -48,14 +37,47 @@ class DriftPartnerDetailPage extends StatelessWidget { } } -class _InfoBox extends StatelessWidget { - final VoidCallback onTap; - final bool inTimeline; +class _InfoBox extends ConsumerStatefulWidget { + final PartnerUserDto partner; - const _InfoBox({ - required this.onTap, - required this.inTimeline, - }); + const _InfoBox({required this.partner}); + + @override + ConsumerState<_InfoBox> createState() => _InfoBoxState(); +} + +class _InfoBoxState extends ConsumerState<_InfoBox> { + bool _inTimeline = false; + + @override + void initState() { + super.initState(); + _inTimeline = widget.partner.inTimeline; + } + + _toggleInTimeline() async { + final user = ref.read(currentUserProvider); + if (user == null) { + return; + } + + try { + await ref.read(partnerUsersProvider.notifier).toggleShowInTimeline(widget.partner.id, user.id); + + setState(() { + _inTimeline = !_inTimeline; + }); + } catch (error, stack) { + debugPrint("Failed to toggle in timeline: $error $stack"); + ImmichToast.show( + context: context, + toastType: ToastType.error, + durationInSecond: 1, + msg: "Failed to toggle the timeline setting", + ); + return; + } + } @override Widget build(BuildContext context) { @@ -66,18 +88,10 @@ class _InfoBox extends StatelessWidget { padding: const EdgeInsets.only(left: 8.0, right: 8.0, top: 16.0), child: Container( decoration: BoxDecoration( - border: Border.all( - color: context.colorScheme.onSurface.withAlpha(10), - width: 1, - ), - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), + border: Border.all(color: context.colorScheme.onSurface.withAlpha(10), width: 1), + borderRadius: const BorderRadius.all(Radius.circular(20)), gradient: LinearGradient( - colors: [ - context.colorScheme.primary.withAlpha(10), - context.colorScheme.primary.withAlpha(15), - ], + colors: [context.colorScheme.primary.withAlpha(10), context.colorScheme.primary.withAlpha(15)], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), @@ -87,18 +101,13 @@ class _InfoBox extends StatelessWidget { child: ListTile( title: Text( "Show in timeline", - style: context.textTheme.titleSmall?.copyWith( - color: context.colorScheme.primary, - ), + style: context.textTheme.titleSmall?.copyWith(color: context.colorScheme.primary), ), subtitle: Text( "Show photos and videos from this user in your timeline", style: context.textTheme.bodyMedium, ), - trailing: Switch( - value: inTimeline, - onChanged: (_) => onTap(), - ), + trailing: Switch(value: _inTimeline, onChanged: (_) => _toggleInTimeline()), ), ), ), diff --git a/mobile/lib/presentation/pages/drift_people_collection.page.dart b/mobile/lib/presentation/pages/drift_people_collection.page.dart new file mode 100644 index 0000000000..ca4e20aad0 --- /dev/null +++ b/mobile/lib/presentation/pages/drift_people_collection.page.dart @@ -0,0 +1,130 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/providers/infrastructure/people.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/services/api.service.dart'; +import 'package:immich_mobile/utils/image_url_builder.dart'; +import 'package:immich_mobile/utils/people.utils.dart'; +import 'package:immich_mobile/widgets/common/search_field.dart'; + +@RoutePage() +class DriftPeopleCollectionPage extends ConsumerStatefulWidget { + const DriftPeopleCollectionPage({super.key}); + + @override + ConsumerState createState() => _DriftPeopleCollectionPageState(); +} + +class _DriftPeopleCollectionPageState extends ConsumerState { + final FocusNode _formFocus = FocusNode(); + String? _search; + + @override + void dispose() { + _formFocus.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final people = ref.watch(driftGetAllPeopleProvider); + final headers = ApiService.getRequestHeaders(); + + return LayoutBuilder( + builder: (context, constraints) { + final isTablet = constraints.maxWidth > 600; + final isPortrait = context.orientation == Orientation.portrait; + + return Scaffold( + appBar: AppBar( + automaticallyImplyLeading: _search == null, + title: _search != null + ? SearchField( + focusNode: _formFocus, + onTapOutside: (_) => _formFocus.unfocus(), + onChanged: (value) => setState(() => _search = value), + filled: true, + hintText: 'filter_people'.tr(), + autofocus: true, + ) + : Text('people'.tr()), + actions: [ + IconButton( + icon: Icon(_search != null ? Icons.close : Icons.search), + onPressed: () { + setState(() => _search = _search == null ? '' : null); + }, + ), + ], + ), + body: SafeArea( + child: people.when( + data: (people) { + if (_search != null) { + people = people.where((person) { + return person.name.toLowerCase().contains(_search!.toLowerCase()); + }).toList(); + } + return GridView.builder( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: isTablet ? 6 : 3, + childAspectRatio: 0.85, + mainAxisSpacing: isPortrait && isTablet ? 36 : 0, + ), + padding: const EdgeInsets.symmetric(vertical: 32), + itemCount: people.length, + itemBuilder: (context, index) { + final person = people[index]; + + return Column( + children: [ + GestureDetector( + onTap: () { + context.pushRoute(DriftPersonRoute(person: person)); + }, + child: Material( + shape: const CircleBorder(side: BorderSide.none), + elevation: 3, + child: CircleAvatar( + maxRadius: isTablet ? 100 / 2 : 96 / 2, + backgroundImage: NetworkImage(getFaceThumbnailUrl(person.id), headers: headers), + ), + ), + ), + const SizedBox(height: 12), + GestureDetector( + onTap: () => showNameEditModal(context, person), + child: person.name.isEmpty + ? Text( + 'add_a_name'.tr(), + style: context.textTheme.titleSmall?.copyWith( + fontWeight: FontWeight.w500, + color: context.colorScheme.primary, + ), + ) + : Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Text( + person.name, + overflow: TextOverflow.ellipsis, + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500), + ), + ), + ), + ], + ); + }, + ); + }, + error: (error, stack) => const Text("error"), + loading: () => const Center(child: CircularProgressIndicator()), + ), + ), + ); + }, + ); + } +} diff --git a/mobile/lib/presentation/pages/drift_person.page.dart b/mobile/lib/presentation/pages/drift_person.page.dart new file mode 100644 index 0000000000..f3146505ae --- /dev/null +++ b/mobile/lib/presentation/pages/drift_person.page.dart @@ -0,0 +1,97 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/people/person_option_sheet.widget.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:immich_mobile/utils/people.utils.dart'; +import 'package:immich_mobile/widgets/common/person_sliver_app_bar.dart'; + +@RoutePage() +class DriftPersonPage extends ConsumerStatefulWidget { + final DriftPerson person; + + const DriftPersonPage({super.key, required this.person}); + + @override + ConsumerState createState() => _DriftPersonPageState(); +} + +class _DriftPersonPageState extends ConsumerState { + late DriftPerson _person; + + @override + initState() { + super.initState(); + _person = widget.person; + } + + Future handleEditName(BuildContext context) async { + final newName = await showNameEditModal(context, _person); + + if (newName != null && newName.isNotEmpty) { + setState(() { + _person = _person.copyWith(name: newName); + }); + } + } + + Future handleEditBirthday(BuildContext context) async { + final birthday = await showBirthdayEditModal(context, _person); + + if (birthday != null) { + setState(() { + _person = _person.copyWith(birthDate: birthday); + }); + } + } + + void showOptionSheet(BuildContext context) { + showModalBottomSheet( + context: context, + backgroundColor: context.colorScheme.surface, + isScrollControlled: false, + builder: (context) { + return PersonOptionSheet( + onEditName: () async { + await handleEditName(context); + context.pop(); + }, + onEditBirthday: () async { + await handleEditBirthday(context); + context.pop(); + }, + ); + }, + ); + } + + @override + Widget build(BuildContext context) { + return ProviderScope( + overrides: [ + timelineServiceProvider.overrideWith((ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to view person timeline'); + } + + final timelineService = ref.watch(timelineFactoryProvider).person(user.id, _person.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), + ], + child: Timeline( + appBar: PersonSliverAppBar( + person: _person, + onNameTap: () => handleEditName(context), + onBirthdayTap: () => handleEditBirthday(context), + onShowOptions: () => showOptionSheet(context), + ), + ), + ); + } +} diff --git a/mobile/lib/presentation/pages/drift_place.page.dart b/mobile/lib/presentation/pages/drift_place.page.dart index 5e5b932f60..e85bb90d54 100644 --- a/mobile/lib/presentation/pages/drift_place.page.dart +++ b/mobile/lib/presentation/pages/drift_place.page.dart @@ -22,12 +22,17 @@ class DriftPlacePage extends StatelessWidget { final ValueNotifier search = ValueNotifier(null); return Scaffold( - body: CustomScrollView( - slivers: [ - _PlaceSliverAppBar(search: search), - _Map(search: search, currentLocation: currentLocation), - _PlaceList(search: search), - ], + body: ValueListenableBuilder( + valueListenable: search, + builder: (context, searchValue, child) { + return CustomScrollView( + slivers: [ + _PlaceSliverAppBar(search: search), + _Map(search: search, currentLocation: currentLocation), + _PlaceList(search: search), + ], + ); + }, ), ); } @@ -47,9 +52,7 @@ class _PlaceSliverAppBar extends StatelessWidget { pinned: true, snap: false, backgroundColor: context.colorScheme.surfaceContainer, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(5)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))), automaticallyImplyLeading: search.value == null, centerTitle: true, title: search.value != null @@ -91,22 +94,16 @@ class _Map extends StatelessWidget { width: context.width, // TODO: migrate to DriftMapRoute after merging #19898 child: MapThumbnail( - onTap: (_, __) => context - .pushRoute(MapRoute(initialLocation: currentLocation)), + onTap: (_, __) => context.pushRoute(MapRoute(initialLocation: currentLocation)), zoom: 8, - centre: currentLocation ?? - const LatLng( - 21.44950, - -157.91959, - ), + centre: currentLocation ?? const LatLng(21.44950, -157.91959), showAttribution: false, - themeMode: - context.isDarkTheme ? ThemeMode.dark : ThemeMode.light, + themeMode: context.isDarkTheme ? ThemeMode.dark : ThemeMode.light, ), ), ), ) - : const SizedBox.shrink(); + : const SliverToBoxAdapter(child: SizedBox.shrink()); } } @@ -122,10 +119,7 @@ class _PlaceList extends ConsumerWidget { return places.when( loading: () => const SliverToBoxAdapter( child: Center( - child: Padding( - padding: EdgeInsets.all(20.0), - child: CircularProgressIndicator(), - ), + child: Padding(padding: EdgeInsets.all(20.0), child: CircularProgressIndicator()), ), ), error: (error, stack) => SliverToBoxAdapter( @@ -134,9 +128,7 @@ class _PlaceList extends ConsumerWidget { padding: const EdgeInsets.all(20.0), child: Text( 'Error loading places: $error, stack: $stack', - style: TextStyle( - color: context.colorScheme.error, - ), + style: TextStyle(color: context.colorScheme.error), ), ), ), @@ -169,21 +161,10 @@ class _PlaceTile extends StatelessWidget { Widget build(BuildContext context) { return LargeLeadingTile( onTap: () => context.pushRoute(DriftPlaceDetailRoute(place: place.$1)), - title: Text( - place.$1, - style: context.textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.w500, - ), - ), + title: Text(place.$1, style: context.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w500)), leading: ClipRRect( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), - child: Thumbnail( - size: const Size(80, 80), - fit: BoxFit.cover, - remoteId: place.$2, - ), + borderRadius: const BorderRadius.all(Radius.circular(20)), + child: Thumbnail(size: const Size(80, 80), fit: BoxFit.cover, remoteId: place.$2), ), ); } diff --git a/mobile/lib/presentation/pages/drift_place_detail.page.dart b/mobile/lib/presentation/pages/drift_place_detail.page.dart index 9999d35297..0f50227945 100644 --- a/mobile/lib/presentation/pages/drift_place_detail.page.dart +++ b/mobile/lib/presentation/pages/drift_place_detail.page.dart @@ -9,29 +9,20 @@ import 'package:immich_mobile/widgets/common/mesmerizing_sliver_app_bar.dart'; class DriftPlaceDetailPage extends StatelessWidget { final String place; - const DriftPlaceDetailPage({ - super.key, - required this.place, - }); + const DriftPlaceDetailPage({super.key, required this.place}); @override Widget build(BuildContext context) { return ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final timelineService = - ref.watch(timelineFactoryProvider).place(place); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + timelineServiceProvider.overrideWith((ref) { + final timelineService = ref.watch(timelineFactoryProvider).place(place); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], child: Timeline( - appBar: MesmerizingSliverAppBar( - title: place, - icon: Icons.location_on, - ), + appBar: MesmerizingSliverAppBar(title: place, icon: Icons.location_on), ), ); } diff --git a/mobile/lib/presentation/pages/drift_recently_taken.page.dart b/mobile/lib/presentation/pages/drift_recently_taken.page.dart index e2972fad56..ceb121b124 100644 --- a/mobile/lib/presentation/pages/drift_recently_taken.page.dart +++ b/mobile/lib/presentation/pages/drift_recently_taken.page.dart @@ -1,9 +1,11 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/widgets.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:immich_mobile/widgets/common/mesmerizing_sliver_app_bar.dart'; @RoutePage() class DriftRecentlyTakenPage extends StatelessWidget { @@ -13,23 +15,18 @@ class DriftRecentlyTakenPage extends StatelessWidget { Widget build(BuildContext context) { return ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final user = ref.watch(currentUserProvider); - if (user == null) { - throw Exception( - 'User must be logged in to access recently taken', - ); - } + timelineServiceProvider.overrideWith((ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to access recently taken'); + } - final timelineService = - ref.watch(timelineFactoryProvider).remoteAssets(user.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + final timelineService = ref.watch(timelineFactoryProvider).remoteAssets(user.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], - child: const Timeline(), + child: Timeline(appBar: MesmerizingSliverAppBar(title: 'recently_taken'.t())), ); } } diff --git a/mobile/lib/presentation/pages/drift_remote_album.page.dart b/mobile/lib/presentation/pages/drift_remote_album.page.dart index 6b68bfcd4e..f1f1cfda92 100644 --- a/mobile/lib/presentation/pages/drift_remote_album.page.dart +++ b/mobile/lib/presentation/pages/drift_remote_album.page.dart @@ -2,7 +2,6 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; - import 'package:immich_mobile/domain/models/album/album.model.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; @@ -22,37 +21,35 @@ import 'package:immich_mobile/widgets/common/remote_album_sliver_app_bar.dart'; class RemoteAlbumPage extends ConsumerStatefulWidget { final RemoteAlbum album; - const RemoteAlbumPage({ - super.key, - required this.album, - }); + const RemoteAlbumPage({super.key, required this.album}); @override ConsumerState createState() => _RemoteAlbumPageState(); } class _RemoteAlbumPageState extends ConsumerState { + late RemoteAlbum _album; @override void initState() { super.initState(); + _album = widget.album; } Future addAssets(BuildContext context) async { - final albumAssets = - await ref.read(remoteAlbumProvider.notifier).getAssets(widget.album.id); + final albumAssets = await ref.read(remoteAlbumProvider.notifier).getAssets(_album.id); final newAssets = await context.pushRoute>( - DriftAssetSelectionTimelineRoute( - lockedSelectionAssets: albumAssets.toSet(), - ), + DriftAssetSelectionTimelineRoute(lockedSelectionAssets: albumAssets.toSet()), ); if (newAssets == null || newAssets.isEmpty) { return; } - final added = await ref.read(remoteAlbumProvider.notifier).addAssets( - widget.album.id, + final added = await ref + .read(remoteAlbumProvider.notifier) + .addAssets( + _album.id, newAssets.map((asset) { final remoteAsset = asset as RemoteAsset; return remoteAsset.id; @@ -62,45 +59,31 @@ class _RemoteAlbumPageState extends ConsumerState { if (added > 0) { ImmichToast.show( context: context, - msg: "assets_added_to_album_count".t( - context: context, - args: { - 'count': added.toString(), - }, - ), + msg: "assets_added_to_album_count".t(context: context, args: {'count': added.toString()}), toastType: ToastType.success, ); } } Future addUsers(BuildContext context) async { - final newUsers = await context.pushRoute>( - DriftUserSelectionRoute(album: widget.album), - ); + final newUsers = await context.pushRoute>(DriftUserSelectionRoute(album: _album)); if (newUsers == null || newUsers.isEmpty) { return; } try { - await ref - .read(remoteAlbumProvider.notifier) - .addUsers(widget.album.id, newUsers); + await ref.read(remoteAlbumProvider.notifier).addUsers(_album.id, newUsers); if (newUsers.isNotEmpty) { ImmichToast.show( context: context, - msg: "users_added_to_album_count".t( - context: context, - args: { - 'count': newUsers.length, - }, - ), + msg: "users_added_to_album_count".t(context: context, args: {'count': newUsers.length}), toastType: ToastType.success, ); } - ref.invalidate(remoteAlbumSharedUsersProvider(widget.album.id)); + ref.invalidate(remoteAlbumSharedUsersProvider(_album.id)); } catch (e) { ImmichToast.show( context: context, @@ -111,9 +94,7 @@ class _RemoteAlbumPageState extends ConsumerState { } Future toggleAlbumOrder() async { - await ref.read(remoteAlbumProvider.notifier).toggleAlbumOrder( - widget.album.id, - ); + await ref.read(remoteAlbumProvider.notifier).toggleAlbumOrder(_album.id); ref.invalidate(timelineServiceProvider); } @@ -127,16 +108,9 @@ class _RemoteAlbumPageState extends ConsumerState { content: Column( mainAxisSize: MainAxisSize.min, children: [ - Text( - 'album_delete_confirmation'.t( - context: context, - args: {'album': widget.album.name}, - ), - ), + Text('album_delete_confirmation'.t(context: context, args: {'album': _album.name})), const SizedBox(height: 8), - Text( - 'album_delete_confirmation_description'.t(context: context), - ), + Text('album_delete_confirmation_description'.t(context: context)), ], ), actions: [ @@ -146,9 +120,7 @@ class _RemoteAlbumPageState extends ConsumerState { ), TextButton( onPressed: () => Navigator.of(context).pop(true), - style: TextButton.styleFrom( - foregroundColor: Theme.of(context).colorScheme.error, - ), + style: TextButton.styleFrom(foregroundColor: Theme.of(context).colorScheme.error), child: Text('delete_album'.t(context: context)), ), ], @@ -158,14 +130,11 @@ class _RemoteAlbumPageState extends ConsumerState { if (confirmed == true) { try { - await ref - .read(remoteAlbumProvider.notifier) - .deleteAlbum(widget.album.id); + await ref.read(remoteAlbumProvider.notifier).deleteAlbum(_album.id); ImmichToast.show( context: context, - msg: 'library_deleted' - .t(context: context), // Using existing success message + msg: 'album_deleted'.t(context: context), toastType: ToastType.success, ); @@ -184,17 +153,20 @@ class _RemoteAlbumPageState extends ConsumerState { final result = await showDialog<_EditAlbumData?>( context: context, barrierDismissible: true, - builder: (context) => _EditAlbumDialog(album: widget.album), + builder: (context) => _EditAlbumDialog(album: _album), ); if (result != null && context.mounted) { + setState(() { + _album = _album.copyWith(name: result.name, description: result.description ?? ''); + }); HapticFeedback.mediumImpact(); } } void showOptionSheet(BuildContext context) { final user = ref.watch(currentUserProvider); - final isOwner = user != null ? user.id == widget.album.ownerId : false; + final isOwner = user != null ? user.id == _album.ownerId : false; showModalBottomSheet( context: context, @@ -237,15 +209,11 @@ class _RemoteAlbumPageState extends ConsumerState { Widget build(BuildContext context) { return ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final timelineService = ref - .watch(timelineFactoryProvider) - .remoteAlbum(albumId: widget.album.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + timelineServiceProvider.overrideWith((ref) { + final timelineService = ref.watch(timelineFactoryProvider).remoteAlbum(albumId: _album.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], child: Timeline( appBar: RemoteAlbumSliverAppBar( @@ -254,9 +222,7 @@ class _RemoteAlbumPageState extends ConsumerState { onToggleAlbumOrder: () => toggleAlbumOrder(), onEditTitle: () => showEditTitleAndDescription(context), ), - bottomSheet: RemoteAlbumBottomSheet( - album: widget.album, - ), + bottomSheet: RemoteAlbumBottomSheet(album: _album), ), ); } @@ -266,18 +232,13 @@ class _EditAlbumData { final String name; final String? description; - const _EditAlbumData({ - required this.name, - this.description, - }); + const _EditAlbumData({required this.name, this.description}); } class _EditAlbumDialog extends ConsumerStatefulWidget { final RemoteAlbum album; - const _EditAlbumDialog({ - required this.album, - }); + const _EditAlbumDialog({required this.album}); @override ConsumerState<_EditAlbumDialog> createState() => _EditAlbumDialogState(); @@ -311,19 +272,14 @@ class _EditAlbumDialogState extends ConsumerState<_EditAlbumDialog> { final newTitle = titleController.text.trim(); final newDescription = descriptionController.text.trim(); - await ref.read(remoteAlbumProvider.notifier).updateAlbum( - widget.album.id, - name: newTitle, - description: newDescription.isEmpty ? null : newDescription, - ); + await ref + .read(remoteAlbumProvider.notifier) + .updateAlbum(widget.album.id, name: newTitle, description: newDescription.isEmpty ? null : newDescription); if (mounted) { - Navigator.of(context).pop( - _EditAlbumData( - name: newTitle, - description: newDescription.isEmpty ? null : newDescription, - ), - ); + Navigator.of( + context, + ).pop(_EditAlbumData(name: newTitle, description: newDescription.isEmpty ? null : newDescription)); } } catch (e) { if (mounted) { @@ -340,11 +296,7 @@ class _EditAlbumDialogState extends ConsumerState<_EditAlbumDialog> { Widget build(BuildContext context) { return Dialog( insetPadding: const EdgeInsets.all(24), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(16), - ), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16))), child: SingleChildScrollView( child: Container( padding: const EdgeInsets.all(16), @@ -357,16 +309,9 @@ class _EditAlbumDialogState extends ConsumerState<_EditAlbumDialog> { children: [ Row( children: [ - Icon( - Icons.edit_outlined, - color: context.colorScheme.primary, - size: 24, - ), + Icon(Icons.edit_outlined, color: context.colorScheme.primary, size: 24), const SizedBox(width: 12), - Text( - 'edit_album'.t(context: context), - style: context.textTheme.titleMedium, - ), + Text('edit_album'.t(context: context), style: context.textTheme.titleMedium), ], ), const SizedBox(height: 24), @@ -374,9 +319,7 @@ class _EditAlbumDialogState extends ConsumerState<_EditAlbumDialog> { // Album Name Text( 'album_name'.t(context: context).toUpperCase(), - style: context.textTheme.labelSmall?.copyWith( - fontWeight: FontWeight.w600, - ), + style: context.textTheme.labelSmall?.copyWith(fontWeight: FontWeight.w600), ), const SizedBox(height: 4), TextFormField( @@ -384,9 +327,7 @@ class _EditAlbumDialogState extends ConsumerState<_EditAlbumDialog> { maxLines: 1, textCapitalization: TextCapitalization.sentences, decoration: InputDecoration( - border: const OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(12)), - ), + border: const OutlineInputBorder(borderRadius: BorderRadius.all(Radius.circular(12))), filled: true, fillColor: context.colorScheme.surface, ), @@ -403,9 +344,7 @@ class _EditAlbumDialogState extends ConsumerState<_EditAlbumDialog> { // Description Text( 'description'.t(context: context).toUpperCase(), - style: context.textTheme.labelSmall?.copyWith( - fontWeight: FontWeight.w600, - ), + style: context.textTheme.labelSmall?.copyWith(fontWeight: FontWeight.w600), ), const SizedBox(height: 4), TextFormField( @@ -413,11 +352,7 @@ class _EditAlbumDialogState extends ConsumerState<_EditAlbumDialog> { maxLines: 4, textCapitalization: TextCapitalization.sentences, decoration: InputDecoration( - border: const OutlineInputBorder( - borderRadius: BorderRadius.all( - Radius.circular(12), - ), - ), + border: const OutlineInputBorder(borderRadius: BorderRadius.all(Radius.circular(12))), filled: true, fillColor: context.colorScheme.surface, ), diff --git a/mobile/lib/presentation/pages/drift_trash.page.dart b/mobile/lib/presentation/pages/drift_trash.page.dart index 9cd2fac760..4d18d12d01 100644 --- a/mobile/lib/presentation/pages/drift_trash.page.dart +++ b/mobile/lib/presentation/pages/drift_trash.page.dart @@ -2,8 +2,10 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/bottom_sheet/trash_bottom_sheet.widget.dart'; import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; +import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; @RoutePage() @@ -14,21 +16,19 @@ class DriftTrashPage extends StatelessWidget { Widget build(BuildContext context) { return ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final user = ref.watch(currentUserProvider); - if (user == null) { - throw Exception('User must be logged in to access trash'); - } + timelineServiceProvider.overrideWith((ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to access trash'); + } - final timelineService = - ref.watch(timelineFactoryProvider).trash(user.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + final timelineService = ref.watch(timelineFactoryProvider).trash(user.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], child: Timeline( + showStorageIndicator: true, appBar: SliverAppBar( title: Text('trash'.t(context: context)), floating: true, @@ -37,6 +37,23 @@ class DriftTrashPage extends StatelessWidget { centerTitle: true, elevation: 0, ), + topSliverWidgetHeight: 24, + topSliverWidget: Consumer( + builder: (context, ref, child) { + final trashDays = ref.watch(serverInfoProvider.select((v) => v.serverConfig.trashDays)); + + return SliverPadding( + padding: const EdgeInsets.all(16.0), + sliver: SliverToBoxAdapter( + child: SizedBox( + height: 24.0, + child: const Text("trash_page_info").t(context: context, args: {"days": "$trashDays"}), + ), + ), + ); + }, + ), + bottomSheet: const TrashBottomBar(), ), ); } diff --git a/mobile/lib/presentation/pages/drift_user_selection.page.dart b/mobile/lib/presentation/pages/drift_user_selection.page.dart index 6f5c4c3e2b..5bd32aaf81 100644 --- a/mobile/lib/presentation/pages/drift_user_selection.page.dart +++ b/mobile/lib/presentation/pages/drift_user_selection.page.dart @@ -14,8 +14,7 @@ import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/widgets/common/user_circle_avatar.dart'; // TODO: Refactor this provider when we have user provider/service/repository pattern in place -final driftUsersProvider = - FutureProvider.autoDispose>((ref) async { +final driftUsersProvider = FutureProvider.autoDispose>((ref) async { final drift = ref.watch(driftProvider); final currentUser = ref.watch(currentUserProvider); @@ -28,15 +27,14 @@ final driftUsersProvider = name: entity.name, email: entity.email, isAdmin: entity.isAdmin, - profileImagePath: entity.profileImagePath, updatedAt: entity.updatedAt, - quotaSizeInBytes: entity.quotaSizeInBytes ?? 0, - quotaUsageInBytes: entity.quotaUsageInBytes, isPartnerSharedBy: false, isPartnerSharedWith: false, avatarColor: AvatarColor.primary, memoryEnabled: true, inTimeline: true, + profileChangedAt: entity.profileChangedAt, + hasProfileImage: entity.hasProfileImage, ), ) .toList(); @@ -50,15 +48,11 @@ final driftUsersProvider = class DriftUserSelectionPage extends HookConsumerWidget { final RemoteAlbum album; - const DriftUserSelectionPage({ - super.key, - required this.album, - }); + const DriftUserSelectionPage({super.key, required this.album}); @override Widget build(BuildContext context, WidgetRef ref) { - final AsyncValue> suggestedShareUsers = - ref.watch(driftUsersProvider); + final AsyncValue> suggestedShareUsers = ref.watch(driftUsersProvider); final sharedUsersList = useState>({}); addNewUsersHandler() { @@ -67,17 +61,9 @@ class DriftUserSelectionPage extends HookConsumerWidget { buildTileIcon(UserDto user) { if (sharedUsersList.value.contains(user)) { - return CircleAvatar( - backgroundColor: context.primaryColor, - child: const Icon( - Icons.check_rounded, - size: 25, - ), - ); + return CircleAvatar(backgroundColor: context.primaryColor, child: const Icon(Icons.check_rounded, size: 25)); } else { - return UserCircleAvatar( - user: user, - ); + return UserCircleAvatar(user: user); } } @@ -90,31 +76,19 @@ class DriftUserSelectionPage extends HookConsumerWidget { padding: const EdgeInsets.symmetric(horizontal: 8.0), child: Chip( backgroundColor: context.primaryColor.withValues(alpha: 0.15), - label: Text( - user.name, - style: const TextStyle( - fontSize: 12, - fontWeight: FontWeight.bold, - ), - ), + label: Text(user.name, style: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold)), ), ), ); } return ListView( children: [ - Wrap( - children: [...usersChip], - ), + Wrap(children: [...usersChip]), Padding( padding: const EdgeInsets.all(16.0), child: Text( 'suggestions'.tr(), - style: const TextStyle( - fontSize: 14, - color: Colors.grey, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(fontSize: 14, color: Colors.grey, fontWeight: FontWeight.bold), ), ), ListView.builder( @@ -124,31 +98,15 @@ class DriftUserSelectionPage extends HookConsumerWidget { return ListTile( leading: buildTileIcon(users[index]), dense: true, - title: Text( - users[index].name, - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), - ), - subtitle: Text( - users[index].email, - style: const TextStyle( - fontSize: 12, - ), - ), + title: Text(users[index].name, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), + subtitle: Text(users[index].email, style: const TextStyle(fontSize: 12)), onTap: () { if (sharedUsersList.value.contains(users[index])) { sharedUsersList.value = sharedUsersList.value - .where( - (selectedUser) => selectedUser.id != users[index].id, - ) + .where((selectedUser) => selectedUser.id != users[index].id) .toSet(); } else { - sharedUsersList.value = { - ...sharedUsersList.value, - users[index], - }; + sharedUsersList.value = {...sharedUsersList.value, users[index]}; } }, ); @@ -161,9 +119,7 @@ class DriftUserSelectionPage extends HookConsumerWidget { return Scaffold( appBar: AppBar( - title: const Text( - 'invite_to_album', - ).tr(), + title: const Text('invite_to_album').tr(), elevation: 0, centerTitle: false, leading: IconButton( @@ -174,28 +130,21 @@ class DriftUserSelectionPage extends HookConsumerWidget { ), actions: [ TextButton( - onPressed: - sharedUsersList.value.isEmpty ? null : addNewUsersHandler, - child: const Text( - "add", - style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold), - ).tr(), + onPressed: sharedUsersList.value.isEmpty ? null : addNewUsersHandler, + child: const Text("add", style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)).tr(), ), ], ), body: suggestedShareUsers.widgetWhen( onData: (users) { // Get shared users for this album from the database - final sharedUsers = - ref.watch(remoteAlbumSharedUsersProvider(album.id)); + final sharedUsers = ref.watch(remoteAlbumSharedUsersProvider(album.id)); return sharedUsers.when( data: (albumSharedUsers) { // Filter out users that are already shared with this album and the owner final filteredUsers = users.where((user) { - return !albumSharedUsers - .any((sharedUser) => sharedUser.id == user.id) && - user.id != album.ownerId; + return !albumSharedUsers.any((sharedUser) => sharedUser.id == user.id) && user.id != album.ownerId; }).toList(); return buildUserList(filteredUsers); @@ -203,8 +152,7 @@ class DriftUserSelectionPage extends HookConsumerWidget { loading: () => const Center(child: CircularProgressIndicator()), error: (error, stack) { // If we can't load shared users, just filter out the owner - final filteredUsers = - users.where((user) => user.id != album.ownerId).toList(); + final filteredUsers = users.where((user) => user.id != album.ownerId).toList(); return buildUserList(filteredUsers); }, ); diff --git a/mobile/lib/presentation/pages/drift_video.page.dart b/mobile/lib/presentation/pages/drift_video.page.dart index 488d027177..eef05acdce 100644 --- a/mobile/lib/presentation/pages/drift_video.page.dart +++ b/mobile/lib/presentation/pages/drift_video.page.dart @@ -1,9 +1,11 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/widgets.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:immich_mobile/widgets/common/mesmerizing_sliver_app_bar.dart'; @RoutePage() class DriftVideoPage extends StatelessWidget { @@ -13,21 +15,18 @@ class DriftVideoPage extends StatelessWidget { Widget build(BuildContext context) { return ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final user = ref.watch(currentUserProvider); - if (user == null) { - throw Exception('User must be logged in to video'); - } + timelineServiceProvider.overrideWith((ref) { + final user = ref.watch(currentUserProvider); + if (user == null) { + throw Exception('User must be logged in to video'); + } - final timelineService = - ref.watch(timelineFactoryProvider).video(user.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + final timelineService = ref.watch(timelineFactoryProvider).video(user.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], - child: const Timeline(), + child: Timeline(appBar: MesmerizingSliverAppBar(title: 'videos'.t())), ); } } diff --git a/mobile/lib/presentation/pages/local_timeline.page.dart b/mobile/lib/presentation/pages/local_timeline.page.dart index fd4e44616b..67bc17cb37 100644 --- a/mobile/lib/presentation/pages/local_timeline.page.dart +++ b/mobile/lib/presentation/pages/local_timeline.page.dart @@ -17,15 +17,11 @@ class LocalTimelinePage extends StatelessWidget { Widget build(BuildContext context) { return ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final timelineService = ref - .watch(timelineFactoryProvider) - .localAlbum(albumId: album.id); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + timelineServiceProvider.overrideWith((ref) { + final timelineService = ref.watch(timelineFactoryProvider).localAlbum(albumId: album.id); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], child: Timeline( appBar: MesmerizingSliverAppBar(title: album.name), diff --git a/mobile/lib/presentation/pages/search/drift_search.page.dart b/mobile/lib/presentation/pages/search/drift_search.page.dart index 5a5eb44b3b..f61fad5484 100644 --- a/mobile/lib/presentation/pages/search/drift_search.page.dart +++ b/mobile/lib/presentation/pages/search/drift_search.page.dart @@ -36,8 +36,7 @@ class DriftSearchPage extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final textSearchType = useState(TextSearchType.context); - final searchHintText = - useState('sunrise_on_the_beach'.t(context: context)); + final searchHintText = useState('sunrise_on_the_beach'.t(context: context)); final textSearchController = useTextEditingController(); final filter = useState( SearchFilter( @@ -45,15 +44,9 @@ class DriftSearchPage extends HookConsumerWidget { location: preFilter?.location ?? SearchLocationFilter(), camera: preFilter?.camera ?? SearchCameraFilter(), date: preFilter?.date ?? SearchDateFilter(), - display: preFilter?.display ?? - SearchDisplayFilters( - isNotInAlbum: false, - isArchive: false, - isFavorite: false, - ), + display: preFilter?.display ?? SearchDisplayFilters(isNotInAlbum: false, isArchive: false, isFavorite: false), mediaType: preFilter?.mediaType ?? AssetType.other, - language: - "${context.locale.languageCode}-${context.locale.countryCode}", + language: "${context.locale.languageCode}-${context.locale.countryCode}", ), ); @@ -70,10 +63,7 @@ class DriftSearchPage extends HookConsumerWidget { SnackBar searchInfoSnackBar(String message) { return SnackBar( - content: Text( - message, - style: context.textTheme.labelLarge, - ), + content: Text(message, style: context.textTheme.labelLarge), showCloseIcon: true, behavior: SnackBarBehavior.fixed, closeIconColor: context.colorScheme.onSurface, @@ -91,14 +81,10 @@ class DriftSearchPage extends HookConsumerWidget { isSearching.value = true; ref.watch(paginatedSearchProvider.notifier).clear(); - final hasResult = await ref - .watch(paginatedSearchProvider.notifier) - .search(filter.value); + final hasResult = await ref.watch(paginatedSearchProvider.notifier).search(filter.value); if (!hasResult) { - context.showSnackBar( - searchInfoSnackBar('search_no_result'.t(context: context)), - ); + context.showSnackBar(searchInfoSnackBar('search_no_result'.t(context: context))); } previousFilter.value = filter.value; @@ -107,14 +93,10 @@ class DriftSearchPage extends HookConsumerWidget { loadMoreSearchResult() async { isSearching.value = true; - final hasResult = await ref - .watch(paginatedSearchProvider.notifier) - .search(filter.value); + final hasResult = await ref.watch(paginatedSearchProvider.notifier).search(filter.value); if (!hasResult) { - context.showSnackBar( - searchInfoSnackBar('search_no_more_result'.t(context: context)), - ); + context.showSnackBar(searchInfoSnackBar('search_no_more_result'.t(context: context))); } isSearching.value = false; @@ -122,52 +104,35 @@ class DriftSearchPage extends HookConsumerWidget { searchPreFilter() { if (preFilter != null) { - Future.delayed( - Duration.zero, - () { - search(); + Future.delayed(Duration.zero, () { + search(); - if (preFilter!.location.city != null) { - locationCurrentFilterWidget.value = Text( - preFilter!.location.city!, - style: context.textTheme.labelLarge, - ); - } - }, - ); + if (preFilter!.location.city != null) { + locationCurrentFilterWidget.value = Text(preFilter!.location.city!, style: context.textTheme.labelLarge); + } + }); } } - useEffect( - () { - Future.microtask( - () => ref.invalidate(paginatedSearchProvider), - ); - searchPreFilter(); + useEffect(() { + Future.microtask(() => ref.invalidate(paginatedSearchProvider)); + searchPreFilter(); - return null; - }, - [], - ); + return null; + }, []); showPeoplePicker() { - handleOnSelect(Set value) { - filter.value = filter.value.copyWith( - people: value, - ); + handleOnSelect(Set value) { + filter.value = filter.value.copyWith(people: value); peopleCurrentFilterWidget.value = Text( - value - .map((e) => e.name != '' ? e.name : 'no_name'.t(context: context)) - .join(', '), + value.map((e) => e.name != '' ? e.name : 'no_name'.t(context: context)).join(', '), style: context.textTheme.labelLarge, ); } handleClear() { - filter.value = filter.value.copyWith( - people: {}, - ); + filter.value = filter.value.copyWith(people: {}); peopleCurrentFilterWidget.value = null; search(); @@ -183,10 +148,7 @@ class DriftSearchPage extends HookConsumerWidget { expanded: true, onSearch: search, onClear: handleClear, - child: PeoplePicker( - onSelect: handleOnSelect, - filter: filter.value.people, - ), + child: PeoplePicker(onSelect: handleOnSelect, filter: filter.value.people), ), ), ); @@ -195,11 +157,7 @@ class DriftSearchPage extends HookConsumerWidget { showLocationPicker() { handleOnSelect(Map value) { filter.value = filter.value.copyWith( - location: SearchLocationFilter( - country: value['country'], - city: value['city'], - state: value['state'], - ), + location: SearchLocationFilter(country: value['country'], city: value['city'], state: value['state']), ); final locationText = []; @@ -215,16 +173,11 @@ class DriftSearchPage extends HookConsumerWidget { locationText.add(value['city']!); } - locationCurrentFilterWidget.value = Text( - locationText.join(', '), - style: context.textTheme.labelLarge, - ); + locationCurrentFilterWidget.value = Text(locationText.join(', '), style: context.textTheme.labelLarge); } handleClear() { - filter.value = filter.value.copyWith( - location: SearchLocationFilter(), - ); + filter.value = filter.value.copyWith(location: SearchLocationFilter()); locationCurrentFilterWidget.value = null; search(); @@ -241,15 +194,10 @@ class DriftSearchPage extends HookConsumerWidget { child: Padding( padding: const EdgeInsets.symmetric(vertical: 16.0), child: Container( - padding: EdgeInsets.only( - bottom: context.viewInsets.bottom, - ), + padding: EdgeInsets.only(bottom: context.viewInsets.bottom), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: LocationPicker( - onSelected: handleOnSelect, - filter: filter.value.location, - ), + child: LocationPicker(onSelected: handleOnSelect, filter: filter.value.location), ), ), ), @@ -260,10 +208,7 @@ class DriftSearchPage extends HookConsumerWidget { showCameraPicker() { handleOnSelect(Map value) { filter.value = filter.value.copyWith( - camera: SearchCameraFilter( - make: value['make'], - model: value['model'], - ), + camera: SearchCameraFilter(make: value['make'], model: value['model']), ); cameraCurrentFilterWidget.value = Text( @@ -273,9 +218,7 @@ class DriftSearchPage extends HookConsumerWidget { } handleClear() { - filter.value = filter.value.copyWith( - camera: SearchCameraFilter(), - ); + filter.value = filter.value.copyWith(camera: SearchCameraFilter()); cameraCurrentFilterWidget.value = null; search(); @@ -291,10 +234,7 @@ class DriftSearchPage extends HookConsumerWidget { onClear: handleClear, child: Padding( padding: const EdgeInsets.all(16.0), - child: CameraPicker( - onSelect: handleOnSelect, - filter: filter.value.camera, - ), + child: CameraPicker(onSelect: handleOnSelect, filter: filter.value.camera), ), ), ); @@ -326,9 +266,7 @@ class DriftSearchPage extends HookConsumerWidget { ); if (date == null) { - filter.value = filter.value.copyWith( - date: SearchDateFilter(), - ); + filter.value = filter.value.copyWith(date: SearchDateFilter()); dateRangeCurrentFilterWidget.value = null; search(); @@ -338,13 +276,7 @@ class DriftSearchPage extends HookConsumerWidget { filter.value = filter.value.copyWith( date: SearchDateFilter( takenAfter: date.start, - takenBefore: date.end.add( - const Duration( - hours: 23, - minutes: 59, - seconds: 59, - ), - ), + takenBefore: date.end.add(const Duration(hours: 23, minutes: 59, seconds: 59)), ), ); @@ -373,24 +305,20 @@ class DriftSearchPage extends HookConsumerWidget { // MEDIA PICKER showMediaTypePicker() { handleOnSelected(AssetType assetType) { - filter.value = filter.value.copyWith( - mediaType: assetType, - ); + filter.value = filter.value.copyWith(mediaType: assetType); mediaTypeCurrentFilterWidget.value = Text( assetType == AssetType.image ? 'image'.t(context: context) : assetType == AssetType.video - ? 'video'.t(context: context) - : 'all'.t(context: context), + ? 'video'.t(context: context) + : 'all'.t(context: context), style: context.textTheme.labelLarge, ); } handleClear() { - filter.value = filter.value.copyWith( - mediaType: AssetType.other, - ); + filter.value = filter.value.copyWith(mediaType: AssetType.other); mediaTypeCurrentFilterWidget.value = null; search(); @@ -402,10 +330,7 @@ class DriftSearchPage extends HookConsumerWidget { title: 'search_filter_media_type_title'.t(context: context), onSearch: search, onClear: handleClear, - child: MediaTypePicker( - onSelect: handleOnSelected, - filter: filter.value.mediaType, - ), + child: MediaTypePicker(onSelect: handleOnSelected, filter: filter.value.mediaType), ), ); } @@ -417,34 +342,19 @@ class DriftSearchPage extends HookConsumerWidget { value.forEach((key, value) { switch (key) { case DisplayOption.notInAlbum: - filter.value = filter.value.copyWith( - display: filter.value.display.copyWith( - isNotInAlbum: value, - ), - ); + filter.value = filter.value.copyWith(display: filter.value.display.copyWith(isNotInAlbum: value)); if (value) { - filterText.add( - 'search_filter_display_option_not_in_album' - .t(context: context), - ); + filterText.add('search_filter_display_option_not_in_album'.t(context: context)); } break; case DisplayOption.archive: - filter.value = filter.value.copyWith( - display: filter.value.display.copyWith( - isArchive: value, - ), - ); + filter.value = filter.value.copyWith(display: filter.value.display.copyWith(isArchive: value)); if (value) { filterText.add('archive'.t(context: context)); } break; case DisplayOption.favorite: - filter.value = filter.value.copyWith( - display: filter.value.display.copyWith( - isFavorite: value, - ), - ); + filter.value = filter.value.copyWith(display: filter.value.display.copyWith(isFavorite: value)); if (value) { filterText.add('favorite'.t(context: context)); } @@ -457,19 +367,12 @@ class DriftSearchPage extends HookConsumerWidget { return; } - displayOptionCurrentFilterWidget.value = Text( - filterText.join(', '), - style: context.textTheme.labelLarge, - ); + displayOptionCurrentFilterWidget.value = Text(filterText.join(', '), style: context.textTheme.labelLarge); } handleClear() { filter.value = filter.value.copyWith( - display: SearchDisplayFilters( - isNotInAlbum: false, - isArchive: false, - isFavorite: false, - ), + display: SearchDisplayFilters(isNotInAlbum: false, isArchive: false, isFavorite: false), ); displayOptionCurrentFilterWidget.value = null; @@ -482,10 +385,7 @@ class DriftSearchPage extends HookConsumerWidget { title: 'display_options'.t(context: context), onSearch: search, onClear: handleClear, - child: DisplayOptionPicker( - onSelect: handleOnSelect, - filter: filter.value.display, - ), + child: DisplayOptionPicker(onSelect: handleOnSelect, filter: filter.value.display), ), ); } @@ -493,27 +393,15 @@ class DriftSearchPage extends HookConsumerWidget { handleTextSubmitted(String value) { switch (textSearchType.value) { case TextSearchType.context: - filter.value = filter.value.copyWith( - filename: '', - context: value, - description: '', - ); + filter.value = filter.value.copyWith(filename: '', context: value, description: ''); break; case TextSearchType.filename: - filter.value = filter.value.copyWith( - filename: value, - context: '', - description: '', - ); + filter.value = filter.value.copyWith(filename: value, context: '', description: ''); break; case TextSearchType.description: - filter.value = filter.value.copyWith( - filename: '', - context: '', - description: value, - ); + filter.value = filter.value.copyWith(filename: '', context: '', description: value); break; } @@ -521,10 +409,10 @@ class DriftSearchPage extends HookConsumerWidget { } IconData getSearchPrefixIcon() => switch (textSearchType.value) { - TextSearchType.context => Icons.image_search_rounded, - TextSearchType.filename => Icons.abc_rounded, - TextSearchType.description => Icons.text_snippet_outlined, - }; + TextSearchType.context => Icons.image_search_rounded, + TextSearchType.filename => Icons.abc_rounded, + TextSearchType.description => Icons.text_snippet_outlined, + }; return Scaffold( resizeToAvoidBottomInset: false, @@ -537,21 +425,11 @@ class DriftSearchPage extends HookConsumerWidget { style: MenuStyle( elevation: const WidgetStatePropertyAll(1), shape: WidgetStateProperty.all( - const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(24), - ), - ), - ), - padding: const WidgetStatePropertyAll( - EdgeInsets.all(4), + const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(24))), ), + padding: const WidgetStatePropertyAll(EdgeInsets.all(4)), ), - builder: ( - BuildContext context, - MenuController controller, - Widget? child, - ) { + builder: (BuildContext context, MenuController controller, Widget? child) { return IconButton( onPressed: () { if (controller.isOpen) { @@ -572,9 +450,7 @@ class DriftSearchPage extends HookConsumerWidget { 'search_by_context'.t(context: context), style: context.textTheme.bodyLarge?.copyWith( fontWeight: FontWeight.w500, - color: textSearchType.value == TextSearchType.context - ? context.colorScheme.primary - : null, + color: textSearchType.value == TextSearchType.context ? context.colorScheme.primary : null, ), ), selectedColor: context.colorScheme.primary, @@ -582,8 +458,7 @@ class DriftSearchPage extends HookConsumerWidget { ), onPressed: () { textSearchType.value = TextSearchType.context; - searchHintText.value = - 'sunrise_on_the_beach'.t(context: context); + searchHintText.value = 'sunrise_on_the_beach'.t(context: context); }, ), MenuItemButton( @@ -593,9 +468,7 @@ class DriftSearchPage extends HookConsumerWidget { 'search_filter_filename'.t(context: context), style: context.textTheme.bodyLarge?.copyWith( fontWeight: FontWeight.w500, - color: textSearchType.value == TextSearchType.filename - ? context.colorScheme.primary - : null, + color: textSearchType.value == TextSearchType.filename ? context.colorScheme.primary : null, ), ), selectedColor: context.colorScheme.primary, @@ -603,8 +476,7 @@ class DriftSearchPage extends HookConsumerWidget { ), onPressed: () { textSearchType.value = TextSearchType.filename; - searchHintText.value = - 'file_name_or_extension'.t(context: context); + searchHintText.value = 'file_name_or_extension'.t(context: context); }, ), MenuItemButton( @@ -614,20 +486,15 @@ class DriftSearchPage extends HookConsumerWidget { 'search_by_description'.t(context: context), style: context.textTheme.bodyLarge?.copyWith( fontWeight: FontWeight.w500, - color: - textSearchType.value == TextSearchType.description - ? context.colorScheme.primary - : null, + color: textSearchType.value == TextSearchType.description ? context.colorScheme.primary : null, ), ), selectedColor: context.colorScheme.primary, - selected: - textSearchType.value == TextSearchType.description, + selected: textSearchType.value == TextSearchType.description, ), onPressed: () { textSearchType.value = TextSearchType.description; - searchHintText.value = - 'search_by_description_example'.t(context: context); + searchHintText.value = 'search_by_description_example'.t(context: context); }, ), ], @@ -636,13 +503,8 @@ class DriftSearchPage extends HookConsumerWidget { ], title: Container( decoration: BoxDecoration( - border: Border.all( - color: context.colorScheme.onSurface.withAlpha(0), - width: 0, - ), - borderRadius: const BorderRadius.all( - Radius.circular(24), - ), + border: Border.all(color: context.colorScheme.onSurface.withAlpha(0), width: 0), + borderRadius: const BorderRadius.all(Radius.circular(24)), gradient: LinearGradient( colors: [ context.colorScheme.primary.withValues(alpha: 0.075), @@ -657,15 +519,8 @@ class DriftSearchPage extends HookConsumerWidget { hintText: searchHintText.value, key: const Key('search_text_field'), controller: textSearchController, - contentPadding: preFilter != null - ? const EdgeInsets.only(left: 24) - : const EdgeInsets.all(8), - prefixIcon: preFilter != null - ? null - : Icon( - getSearchPrefixIcon(), - color: context.colorScheme.primary, - ), + contentPadding: preFilter != null ? const EdgeInsets.only(left: 24) : const EdgeInsets.all(8), + prefixIcon: preFilter != null ? null : Icon(getSearchPrefixIcon(), color: context.colorScheme.primary), onSubmitted: handleTextSubmitted, focusNode: ref.watch(searchInputFocusProvider), ), @@ -674,7 +529,7 @@ class DriftSearchPage extends HookConsumerWidget { body: CustomScrollView( slivers: [ SliverPadding( - padding: const EdgeInsets.only(top: 12.0), + padding: const EdgeInsets.only(top: 12.0, bottom: 4.0), sliver: SliverToBoxAdapter( child: SizedBox( height: 50, @@ -718,8 +573,7 @@ class DriftSearchPage extends HookConsumerWidget { SearchFilterChip( icon: Icons.display_settings_outlined, onTap: showDisplayOptionPicker, - label: - 'search_filter_display_options'.t(context: context), + label: 'search_filter_display_options'.t(context: context), currentFilter: displayOptionCurrentFilterWidget.value, ), ], @@ -728,10 +582,7 @@ class DriftSearchPage extends HookConsumerWidget { ), ), if (isSearching.value) - const SliverFillRemaining( - hasScrollBody: false, - child: Center(child: CircularProgressIndicator()), - ) + const SliverFillRemaining(hasScrollBody: false, child: Center(child: CircularProgressIndicator())) else _SearchResultGrid(onScrollEnd: loadMoreSearchResult), ], @@ -755,16 +606,13 @@ class _SearchResultGrid extends ConsumerWidget { return NotificationListener( onNotification: (notification) { - final isBottomSheetNotification = notification.context - ?.findAncestorWidgetOfExactType() != - null; + final isBottomSheetNotification = + notification.context?.findAncestorWidgetOfExactType() != null; final metrics = notification.metrics; final isVerticalScroll = metrics.axis == Axis.vertical; - if (metrics.pixels >= metrics.maxScrollExtent && - isVerticalScroll && - !isBottomSheetNotification) { + if (metrics.pixels >= metrics.maxScrollExtent && isVerticalScroll && !isBottomSheetNotification) { onScrollEnd(); } @@ -773,21 +621,13 @@ class _SearchResultGrid extends ConsumerWidget { child: SliverFillRemaining( child: ProviderScope( overrides: [ - timelineServiceProvider.overrideWith( - (ref) { - final timelineService = ref - .watch(timelineFactoryProvider) - .fromAssets(searchResult.assets); - ref.onDispose(timelineService.dispose); - return timelineService; - }, - ), + timelineServiceProvider.overrideWith((ref) { + final timelineService = ref.watch(timelineFactoryProvider).fromAssets(searchResult.assets); + ref.onDispose(timelineService.dispose); + return timelineService; + }), ], - child: Timeline( - key: ValueKey(searchResult.totalAssets), - appBar: null, - groupBy: GroupAssetsBy.none, - ), + child: Timeline(key: ValueKey(searchResult.totalAssets), appBar: null, groupBy: GroupAssetsBy.none), ), ), ); @@ -806,24 +646,16 @@ class _SearchEmptyContent extends StatelessWidget { const SizedBox(height: 40), Center( child: Image.asset( - context.isDarkTheme - ? 'assets/polaroid-dark.png' - : 'assets/polaroid-light.png', + context.isDarkTheme ? 'assets/polaroid-dark.png' : 'assets/polaroid-light.png', height: 125, ), ), const SizedBox(height: 16), Center( - child: Text( - 'search_page_search_photos_videos'.t(context: context), - style: context.textTheme.labelLarge, - ), + child: Text('search_page_search_photos_videos'.t(context: context), style: context.textTheme.labelLarge), ), const SizedBox(height: 32), - const Padding( - padding: EdgeInsets.symmetric(horizontal: 16), - child: _QuickLinkList(), - ), + const Padding(padding: EdgeInsets.symmetric(horizontal: 16), child: _QuickLinkList()), ], ), ); @@ -837,13 +669,8 @@ class _QuickLinkList extends StatelessWidget { Widget build(BuildContext context) { return Container( decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), - border: Border.all( - color: context.colorScheme.outline.withAlpha(10), - width: 1, - ), + borderRadius: const BorderRadius.all(Radius.circular(20)), + border: Border.all(color: context.colorScheme.outline.withAlpha(10), width: 1), gradient: LinearGradient( colors: [ context.colorScheme.primary.withAlpha(10), @@ -906,19 +733,9 @@ class _QuickLink extends StatelessWidget { ); return ListTile( - shape: RoundedRectangleBorder( - borderRadius: borderRadius, - ), - leading: Icon( - icon, - size: 26, - ), - title: Text( - title, - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w500, - ), - ), + shape: RoundedRectangleBorder(borderRadius: borderRadius), + leading: Icon(icon, size: 26), + title: Text(title, style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w500)), onTap: onTap, ); } diff --git a/mobile/lib/presentation/pages/search/paginated_search.provider.dart b/mobile/lib/presentation/pages/search/paginated_search.provider.dart index 84635fd0b9..718a241ba4 100644 --- a/mobile/lib/presentation/pages/search/paginated_search.provider.dart +++ b/mobile/lib/presentation/pages/search/paginated_search.provider.dart @@ -4,16 +4,14 @@ import 'package:immich_mobile/domain/services/search.service.dart'; import 'package:immich_mobile/models/search/search_filter.model.dart'; import 'package:immich_mobile/providers/infrastructure/search.provider.dart'; -final paginatedSearchProvider = - StateNotifierProvider( +final paginatedSearchProvider = StateNotifierProvider( (ref) => PaginatedSearchNotifier(ref.watch(searchServiceProvider)), ); class PaginatedSearchNotifier extends StateNotifier { final SearchService _searchService; - PaginatedSearchNotifier(this._searchService) - : super(const SearchResult(assets: [], nextPage: 1)); + PaginatedSearchNotifier(this._searchService) : super(const SearchResult(assets: [], nextPage: 1)); Future search(SearchFilter filter) async { if (state.nextPage == null) { @@ -26,10 +24,7 @@ class PaginatedSearchNotifier extends StateNotifier { return false; } - state = SearchResult( - assets: [...state.assets, ...result.assets], - nextPage: result.nextPage, - ); + state = SearchResult(assets: [...state.assets, ...result.assets], nextPage: result.nextPage); return true; } diff --git a/mobile/lib/presentation/widgets/action_buttons/archive_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/archive_action_button.widget.dart index 86537816e3..d30ba07d0c 100644 --- a/mobile/lib/presentation/widgets/action_buttons/archive_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/archive_action_button.widget.dart @@ -2,8 +2,10 @@ import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; @@ -21,17 +23,16 @@ class ArchiveActionButton extends ConsumerWidget { final result = await ref.read(actionProvider.notifier).archive(source); ref.read(multiSelectProvider.notifier).reset(); - final successMessage = 'archive_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + if (source == ActionSource.viewer) { + EventStream.shared.emit(const ViewerReloadAssetEvent()); + } + + final successMessage = 'archive_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( context: context, - msg: result.success - ? successMessage - : 'scaffold_body_error_occurred'.t(context: context), + msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context), gravity: ToastGravity.BOTTOM, toastType: result.success ? ToastType.success : ToastType.error, ); @@ -42,7 +43,7 @@ class ArchiveActionButton extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { return BaseActionButton( iconData: Icons.archive_outlined, - label: "archive".t(context: context), + label: "to_archive".t(context: context), onPressed: () => _onTap(context, ref), ); } diff --git a/mobile/lib/presentation/widgets/action_buttons/base_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/base_action_button.widget.dart index 2ad285326c..5ec6c8bc54 100644 --- a/mobile/lib/presentation/widgets/action_buttons/base_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/base_action_button.widget.dart @@ -25,12 +25,10 @@ class BaseActionButton extends StatelessWidget { @override Widget build(BuildContext context) { - final miniWidth = - minWidth ?? (context.isMobile ? context.width / 4.5 : 75.0); + final miniWidth = minWidth ?? (context.isMobile ? context.width / 4.5 : 75.0); final iconTheme = IconTheme.of(context); final iconSize = iconTheme.size ?? 24.0; - final iconColor = - this.iconColor ?? iconTheme.color ?? context.themeData.iconTheme.color; + final iconColor = this.iconColor ?? iconTheme.color ?? context.themeData.iconTheme.color; final textColor = context.themeData.textTheme.labelLarge?.color; if (menuItem) { @@ -41,14 +39,10 @@ class BaseActionButton extends StatelessWidget { } return ConstrainedBox( - constraints: BoxConstraints( - maxWidth: maxWidth, - ), + constraints: BoxConstraints(maxWidth: maxWidth), child: MaterialButton( padding: const EdgeInsets.all(10), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(20)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(20))), textColor: textColor, onPressed: onPressed, onLongPress: onLongPressed, @@ -61,12 +55,10 @@ class BaseActionButton extends StatelessWidget { const SizedBox(height: 8), Text( label, - style: const TextStyle( - fontSize: 14.0, - fontWeight: FontWeight.w400, - ), + style: const TextStyle(fontSize: 14.0, fontWeight: FontWeight.w400), maxLines: 3, textAlign: TextAlign.center, + softWrap: true, ), ], ), diff --git a/mobile/lib/presentation/widgets/action_buttons/cast_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/cast_action_button.widget.dart index 2900d55834..26b8ba6f47 100644 --- a/mobile/lib/presentation/widgets/action_buttons/cast_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/cast_action_button.widget.dart @@ -17,14 +17,10 @@ class CastActionButton extends ConsumerWidget { return BaseActionButton( iconData: isCasting ? Icons.cast_connected_rounded : Icons.cast_rounded, - iconColor: - isCasting ? context.primaryColor : null, // null = default color + iconColor: isCasting ? context.primaryColor : null, // null = default color label: "cast".t(context: context), onPressed: () { - showDialog( - context: context, - builder: (context) => const CastDialog(), - ); + showDialog(context: context, builder: (context) => const CastDialog()); }, menuItem: menuItem, ); diff --git a/mobile/lib/presentation/widgets/action_buttons/delete_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/delete_action_button.widget.dart index d81f998a7b..723700af55 100644 --- a/mobile/lib/presentation/widgets/action_buttons/delete_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/delete_action_button.widget.dart @@ -2,36 +2,66 @@ import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/domain/utils/event_stream.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; -class DeletePermanentActionButton extends ConsumerWidget { +/// This delete action has the following behavior: +/// - Set the deletedAt information, put the asset in the trash in the server +/// which will be permanently deleted after the number of days configure by the admin +/// - Prompt to delete the asset locally +class DeleteActionButton extends ConsumerWidget { final ActionSource source; - - const DeletePermanentActionButton({super.key, required this.source}); + final bool showConfirmation; + const DeleteActionButton({super.key, required this.source, this.showConfirmation = false}); void _onTap(BuildContext context, WidgetRef ref) async { if (!context.mounted) { return; } - final result = await ref.read(actionProvider.notifier).delete(source); + if (showConfirmation) { + final confirm = await showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text('delete'.t(context: context)), + content: Text('delete_action_confirmation_message'.t(context: context)), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(false), + child: Text('cancel'.t(context: context)), + ), + TextButton( + onPressed: () => Navigator.of(context).pop(true), + child: Text( + 'confirm'.t(context: context), + style: TextStyle(color: context.colorScheme.error), + ), + ), + ], + ), + ); + if (confirm != true) return; + } + + final result = await ref.read(actionProvider.notifier).trashRemoteAndDeleteLocal(source); ref.read(multiSelectProvider.notifier).reset(); - final successMessage = 'delete_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + if (source == ActionSource.viewer) { + EventStream.shared.emit(const ViewerReloadAssetEvent()); + } + + final successMessage = 'delete_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( context: context, - msg: result.success - ? successMessage - : 'scaffold_body_error_occurred'.t(context: context), + msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context), gravity: ToastGravity.BOTTOM, toastType: result.success ? ToastType.success : ToastType.error, ); @@ -42,8 +72,8 @@ class DeletePermanentActionButton extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { return BaseActionButton( maxWidth: 110.0, - iconData: Icons.delete_forever, - label: "delete_dialog_title".t(context: context), + iconData: Icons.delete_sweep_outlined, + label: "delete".t(context: context), onPressed: () => _onTap(context, ref), ); } diff --git a/mobile/lib/presentation/widgets/action_buttons/delete_local_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/delete_local_action_button.widget.dart index 90534ca68c..cccdee9b3a 100644 --- a/mobile/lib/presentation/widgets/action_buttons/delete_local_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/delete_local_action_button.widget.dart @@ -2,12 +2,16 @@ import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; +/// This delete action has the following behavior: +/// - Prompt to delete the asset locally class DeleteLocalActionButton extends ConsumerWidget { final ActionSource source; @@ -21,17 +25,20 @@ class DeleteLocalActionButton extends ConsumerWidget { final result = await ref.read(actionProvider.notifier).deleteLocal(source); ref.read(multiSelectProvider.notifier).reset(); - final successMessage = 'delete_local_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + if (source == ActionSource.viewer) { + EventStream.shared.emit(const ViewerReloadAssetEvent()); + } + + if (result.count == 0) { + return; + } + + final successMessage = 'delete_local_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( context: context, - msg: result.success - ? successMessage - : 'scaffold_body_error_occurred'.t(context: context), + msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context), gravity: ToastGravity.BOTTOM, toastType: result.success ? ToastType.success : ToastType.error, ); diff --git a/mobile/lib/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart new file mode 100644 index 0000000000..4979df904c --- /dev/null +++ b/mobile/lib/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart @@ -0,0 +1,57 @@ +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/domain/utils/event_stream.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; +import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; + +/// This delete action has the following behavior: +/// - Delete permanently on the server +/// - Prompt to delete the asset locally +class DeletePermanentActionButton extends ConsumerWidget { + final ActionSource source; + + const DeletePermanentActionButton({super.key, required this.source}); + + void _onTap(BuildContext context, WidgetRef ref) async { + if (!context.mounted) { + return; + } + + final result = await ref.read(actionProvider.notifier).deleteRemoteAndLocal(source); + ref.read(multiSelectProvider.notifier).reset(); + + if (source == ActionSource.viewer) { + EventStream.shared.emit(const ViewerReloadAssetEvent()); + } + + final successMessage = 'delete_permanently_action_prompt'.t( + context: context, + args: {'count': result.count.toString()}, + ); + + if (context.mounted) { + ImmichToast.show( + context: context, + msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context), + gravity: ToastGravity.BOTTOM, + toastType: result.success ? ToastType.success : ToastType.error, + ); + } + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + return BaseActionButton( + maxWidth: 110.0, + iconData: Icons.delete_forever, + label: "delete_permanently".t(context: context), + onPressed: () => _onTap(context, ref), + ); + } +} diff --git a/mobile/lib/presentation/widgets/action_buttons/delete_trash_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/delete_trash_action_button.widget.dart new file mode 100644 index 0000000000..cb0e7091c8 --- /dev/null +++ b/mobile/lib/presentation/widgets/action_buttons/delete_trash_action_button.widget.dart @@ -0,0 +1,54 @@ +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; + +/// This delete action has the following behavior: +/// - Delete permanently on the server +/// - Prompt to delete the asset locally +/// +/// This action is used when the asset is selected in multi-selection mode in the trash page +class DeleteTrashActionButton extends ConsumerWidget { + final ActionSource source; + + const DeleteTrashActionButton({super.key, required this.source}); + + void _onTap(BuildContext context, WidgetRef ref) async { + if (!context.mounted) { + return; + } + + final result = await ref.read(actionProvider.notifier).deleteRemoteAndLocal(source); + ref.read(multiSelectProvider.notifier).reset(); + + final successMessage = 'assets_permanently_deleted_count'.t( + context: context, + args: {'count': result.count.toString()}, + ); + + if (context.mounted) { + ImmichToast.show( + context: context, + msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context), + gravity: ToastGravity.BOTTOM, + toastType: result.success ? ToastType.success : ToastType.error, + ); + } + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + return TextButton.icon( + icon: Icon(Icons.delete_forever, color: Colors.red[400]), + label: Text( + "delete".t(context: context), + style: TextStyle(fontSize: 14, color: Colors.red[400], fontWeight: FontWeight.bold), + ), + onPressed: () => _onTap(context, ref), + ); + } +} diff --git a/mobile/lib/presentation/widgets/action_buttons/download_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/download_action_button.widget.dart index 53ea5d4946..a6464308e2 100644 --- a/mobile/lib/presentation/widgets/action_buttons/download_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/download_action_button.widget.dart @@ -1,16 +1,53 @@ +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:immich_mobile/constants/enums.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; class DownloadActionButton extends ConsumerWidget { - const DownloadActionButton({super.key}); + final ActionSource source; + + const DownloadActionButton({super.key, required this.source}); + + void _onTap(BuildContext context, WidgetRef ref) async { + if (!context.mounted) { + return; + } + + final result = await ref.read(actionProvider.notifier).downloadAll(source); + ref.read(multiSelectProvider.notifier).reset(); + + if (!context.mounted) { + return; + } + + if (!result.success) { + ImmichToast.show( + context: context, + msg: 'scaffold_body_error_occurred'.t(context: context), + gravity: ToastGravity.BOTTOM, + toastType: ToastType.error, + ); + } else if (result.count > 0) { + ImmichToast.show( + context: context, + msg: 'download_action_prompt'.t(context: context, args: {'count': result.count.toString()}), + gravity: ToastGravity.BOTTOM, + toastType: ToastType.success, + ); + } + } @override Widget build(BuildContext context, WidgetRef ref) { return BaseActionButton( iconData: Icons.download, label: "download".t(context: context), + onPressed: () => _onTap(context, ref), ); } } diff --git a/mobile/lib/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart index 3db3dde44d..6eeec0658b 100644 --- a/mobile/lib/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart @@ -1,10 +1,44 @@ import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; class EditDateTimeActionButton extends ConsumerWidget { - const EditDateTimeActionButton({super.key}); + final ActionSource source; + + const EditDateTimeActionButton({super.key, required this.source}); + + _onTap(BuildContext context, WidgetRef ref) async { + if (!context.mounted) { + return; + } + + final result = await ref.read(actionProvider.notifier).editDateTime(source, context); + if (result == null) { + return; + } + + ref.read(multiSelectProvider.notifier).reset(); + + final successMessage = 'edit_date_and_time_action_prompt'.t( + context: context, + args: {'count': result.count.toString()}, + ); + + if (context.mounted) { + ImmichToast.show( + context: context, + msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context), + gravity: ToastGravity.BOTTOM, + toastType: result.success ? ToastType.success : ToastType.error, + ); + } + } @override Widget build(BuildContext context, WidgetRef ref) { @@ -12,6 +46,7 @@ class EditDateTimeActionButton extends ConsumerWidget { maxWidth: 95.0, iconData: Icons.edit_calendar_outlined, label: "control_bottom_app_bar_edit_time".t(context: context), + onPressed: () => _onTap(context, ref), ); } } diff --git a/mobile/lib/presentation/widgets/action_buttons/edit_location_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/edit_location_action_button.widget.dart index c7279995ff..1a8a1a5c39 100644 --- a/mobile/lib/presentation/widgets/action_buttons/edit_location_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/edit_location_action_button.widget.dart @@ -18,25 +18,19 @@ class EditLocationActionButton extends ConsumerWidget { return; } - final result = - await ref.read(actionProvider.notifier).editLocation(source, context); + final result = await ref.read(actionProvider.notifier).editLocation(source, context); if (result == null) { return; } ref.read(multiSelectProvider.notifier).reset(); - final successMessage = 'edit_location_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + final successMessage = 'edit_location_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( context: context, - msg: result.success - ? successMessage - : 'scaffold_body_error_occurred'.t(context: context), + msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context), gravity: ToastGravity.BOTTOM, toastType: result.success ? ToastType.success : ToastType.error, ); diff --git a/mobile/lib/presentation/widgets/action_buttons/favorite_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/favorite_action_button.widget.dart index 39d059c2d1..0aca5158ef 100644 --- a/mobile/lib/presentation/widgets/action_buttons/favorite_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/favorite_action_button.widget.dart @@ -12,11 +12,7 @@ class FavoriteActionButton extends ConsumerWidget { final ActionSource source; final bool menuItem; - const FavoriteActionButton({ - super.key, - required this.source, - this.menuItem = false, - }); + const FavoriteActionButton({super.key, required this.source, this.menuItem = false}); void _onTap(BuildContext context, WidgetRef ref) async { if (!context.mounted) { @@ -31,17 +27,12 @@ class FavoriteActionButton extends ConsumerWidget { ref.read(multiSelectProvider.notifier).reset(); - final successMessage = 'favorite_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + final successMessage = 'favorite_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( context: context, - msg: result.success - ? successMessage - : 'scaffold_body_error_occurred'.t(context: context), + msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context), gravity: ToastGravity.BOTTOM, toastType: result.success ? ToastType.success : ToastType.error, ); diff --git a/mobile/lib/presentation/widgets/action_buttons/motion_photo_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/motion_photo_action_button.widget.dart index 47f797c166..696b9ff367 100644 --- a/mobile/lib/presentation/widgets/action_buttons/motion_photo_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/motion_photo_action_button.widget.dart @@ -14,9 +14,7 @@ class MotionPhotoActionButton extends ConsumerWidget { final isPlaying = ref.watch(isPlayingMotionVideoProvider); return BaseActionButton( - iconData: isPlaying - ? Icons.motion_photos_pause_outlined - : Icons.play_circle_outline_rounded, + iconData: isPlaying ? Icons.motion_photos_pause_outlined : Icons.play_circle_outline_rounded, label: "play_motion_photo".t(context: context), onPressed: ref.read(isPlayingMotionVideoProvider.notifier).toggle, menuItem: menuItem, diff --git a/mobile/lib/presentation/widgets/action_buttons/move_to_lock_folder_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/move_to_lock_folder_action_button.widget.dart index 7546f07961..0fde43b459 100644 --- a/mobile/lib/presentation/widgets/action_buttons/move_to_lock_folder_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/move_to_lock_folder_action_button.widget.dart @@ -2,8 +2,10 @@ import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; @@ -18,10 +20,13 @@ class MoveToLockFolderActionButton extends ConsumerWidget { return; } - final result = - await ref.read(actionProvider.notifier).moveToLockFolder(source); + final result = await ref.read(actionProvider.notifier).moveToLockFolder(source); ref.read(multiSelectProvider.notifier).reset(); + if (source == ActionSource.viewer) { + EventStream.shared.emit(const ViewerReloadAssetEvent()); + } + final successMessage = 'move_to_lock_folder_action_prompt'.t( context: context, args: {'count': result.count.toString()}, @@ -30,9 +35,7 @@ class MoveToLockFolderActionButton extends ConsumerWidget { if (context.mounted) { ImmichToast.show( context: context, - msg: result.success - ? successMessage - : 'scaffold_body_error_occurred'.t(context: context), + msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context), gravity: ToastGravity.BOTTOM, toastType: result.success ? ToastType.success : ToastType.error, ); diff --git a/mobile/lib/presentation/widgets/action_buttons/remove_from_album_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/remove_from_album_action_button.widget.dart index 8228e27cc1..a1f6f7e7d1 100644 --- a/mobile/lib/presentation/widgets/action_buttons/remove_from_album_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/remove_from_album_action_button.widget.dart @@ -12,20 +12,14 @@ class RemoveFromAlbumActionButton extends ConsumerWidget { final String albumId; final ActionSource source; - const RemoveFromAlbumActionButton({ - super.key, - required this.albumId, - required this.source, - }); + const RemoveFromAlbumActionButton({super.key, required this.albumId, required this.source}); void _onTap(BuildContext context, WidgetRef ref) async { if (!context.mounted) { return; } - final result = await ref - .read(actionProvider.notifier) - .removeFromAlbum(source, albumId); + final result = await ref.read(actionProvider.notifier).removeFromAlbum(source, albumId); ref.read(multiSelectProvider.notifier).reset(); final successMessage = 'remove_from_album_action_prompt'.t( @@ -36,9 +30,7 @@ class RemoveFromAlbumActionButton extends ConsumerWidget { if (context.mounted) { ImmichToast.show( context: context, - msg: result.success - ? successMessage - : 'scaffold_body_error_occurred'.t(context: context), + msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context), gravity: ToastGravity.BOTTOM, toastType: result.success ? ToastType.success : ToastType.error, ); diff --git a/mobile/lib/presentation/widgets/action_buttons/remove_from_lock_folder_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/remove_from_lock_folder_action_button.widget.dart index 20fb62013f..028abf5596 100644 --- a/mobile/lib/presentation/widgets/action_buttons/remove_from_lock_folder_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/remove_from_lock_folder_action_button.widget.dart @@ -18,8 +18,7 @@ class RemoveFromLockFolderActionButton extends ConsumerWidget { return; } - final result = - await ref.read(actionProvider.notifier).removeFromLockFolder(source); + final result = await ref.read(actionProvider.notifier).removeFromLockFolder(source); ref.read(multiSelectProvider.notifier).reset(); final successMessage = 'remove_from_lock_folder_action_prompt'.t( @@ -30,9 +29,7 @@ class RemoveFromLockFolderActionButton extends ConsumerWidget { if (context.mounted) { ImmichToast.show( context: context, - msg: result.success - ? successMessage - : 'scaffold_body_error_occurred'.t(context: context), + msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context), gravity: ToastGravity.BOTTOM, toastType: result.success ? ToastType.success : ToastType.error, ); diff --git a/mobile/lib/presentation/widgets/action_buttons/restore_trash_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/restore_trash_action_button.widget.dart new file mode 100644 index 0000000000..e7928bd325 --- /dev/null +++ b/mobile/lib/presentation/widgets/action_buttons/restore_trash_action_button.widget.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; + +class RestoreTrashActionButton extends ConsumerWidget { + final ActionSource source; + + const RestoreTrashActionButton({super.key, required this.source}); + + void _onTap(BuildContext context, WidgetRef ref) async { + if (!context.mounted) { + return; + } + + final result = await ref.read(actionProvider.notifier).restoreTrash(source); + ref.read(multiSelectProvider.notifier).reset(); + + final successMessage = 'assets_restored_count'.t(context: context, args: {'count': result.count.toString()}); + + if (context.mounted) { + ImmichToast.show( + context: context, + msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context), + gravity: ToastGravity.BOTTOM, + toastType: result.success ? ToastType.success : ToastType.error, + ); + } + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + return TextButton.icon( + icon: const Icon(Icons.history_rounded), + label: Text('restore'.t(), style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), + onPressed: () => _onTap(context, ref), + ); + } +} diff --git a/mobile/lib/presentation/widgets/action_buttons/share_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/share_action_button.widget.dart index 1b1553dabc..546fbe408d 100644 --- a/mobile/lib/presentation/widgets/action_buttons/share_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/share_action_button.widget.dart @@ -37,8 +37,7 @@ class ShareActionButton extends ConsumerWidget { } else if (result.count > 0) { ImmichToast.show( context: context, - msg: 'share_action_prompt' - .t(context: context, args: {'count': result.count.toString()}), + msg: 'share_action_prompt'.t(context: context, args: {'count': result.count.toString()}), gravity: ToastGravity.BOTTOM, toastType: ToastType.success, ); @@ -48,8 +47,7 @@ class ShareActionButton extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { return BaseActionButton( - iconData: - Platform.isAndroid ? Icons.share_rounded : Icons.ios_share_rounded, + iconData: Platform.isAndroid ? Icons.share_rounded : Icons.ios_share_rounded, label: 'share'.t(context: context), onPressed: () => _onTap(context, ref), ); diff --git a/mobile/lib/presentation/widgets/action_buttons/stack_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/stack_action_button.widget.dart index 13782c0098..22fccf5473 100644 --- a/mobile/lib/presentation/widgets/action_buttons/stack_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/stack_action_button.widget.dart @@ -24,21 +24,15 @@ class StackActionButton extends ConsumerWidget { throw Exception('User must be logged in to access stack action'); } - final result = - await ref.read(actionProvider.notifier).stack(user.id, source); + final result = await ref.read(actionProvider.notifier).stack(user.id, source); ref.read(multiSelectProvider.notifier).reset(); - final successMessage = 'stack_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + final successMessage = 'stack_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( context: context, - msg: result.success - ? successMessage - : 'scaffold_body_error_occurred'.t(context: context), + msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context), gravity: ToastGravity.BOTTOM, toastType: result.success ? ToastType.success : ToastType.error, ); diff --git a/mobile/lib/presentation/widgets/action_buttons/trash_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/trash_action_button.widget.dart index 449b688550..df8f544601 100644 --- a/mobile/lib/presentation/widgets/action_buttons/trash_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/trash_action_button.widget.dart @@ -2,12 +2,17 @@ import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; +/// This delete action has the following behavior: +/// - Set the deletedAt information, put the asset in the trash in the server +/// which will be permanently deleted after the number of days configure by the admin class TrashActionButton extends ConsumerWidget { final ActionSource source; @@ -21,17 +26,16 @@ class TrashActionButton extends ConsumerWidget { final result = await ref.read(actionProvider.notifier).trash(source); ref.read(multiSelectProvider.notifier).reset(); - final successMessage = 'trash_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + if (source == ActionSource.viewer) { + EventStream.shared.emit(const ViewerReloadAssetEvent()); + } + + final successMessage = 'trash_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( context: context, - msg: result.success - ? successMessage - : 'scaffold_body_error_occurred'.t(context: context), + msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context), gravity: ToastGravity.BOTTOM, toastType: result.success ? ToastType.success : ToastType.error, ); diff --git a/mobile/lib/presentation/widgets/action_buttons/unarchive_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/unarchive_action_button.widget.dart index e44500144c..b457a1b4ca 100644 --- a/mobile/lib/presentation/widgets/action_buttons/unarchive_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/unarchive_action_button.widget.dart @@ -21,17 +21,12 @@ class UnArchiveActionButton extends ConsumerWidget { final result = await ref.read(actionProvider.notifier).unArchive(source); ref.read(multiSelectProvider.notifier).reset(); - final successMessage = 'unarchive_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + final successMessage = 'unarchive_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( context: context, - msg: result.success - ? successMessage - : 'scaffold_body_error_occurred'.t(context: context), + msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context), gravity: ToastGravity.BOTTOM, toastType: result.success ? ToastType.success : ToastType.error, ); diff --git a/mobile/lib/presentation/widgets/action_buttons/unfavorite_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/unfavorite_action_button.widget.dart index b465643796..7fdc5e81e8 100644 --- a/mobile/lib/presentation/widgets/action_buttons/unfavorite_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/unfavorite_action_button.widget.dart @@ -12,11 +12,7 @@ class UnFavoriteActionButton extends ConsumerWidget { final ActionSource source; final bool menuItem; - const UnFavoriteActionButton({ - super.key, - required this.source, - this.menuItem = false, - }); + const UnFavoriteActionButton({super.key, required this.source, this.menuItem = false}); void _onTap(BuildContext context, WidgetRef ref) async { if (!context.mounted) { @@ -31,17 +27,12 @@ class UnFavoriteActionButton extends ConsumerWidget { ref.read(multiSelectProvider.notifier).reset(); - final successMessage = 'unfavorite_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + final successMessage = 'unfavorite_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( context: context, - msg: result.success - ? successMessage - : 'scaffold_body_error_occurred'.t(context: context), + msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context), gravity: ToastGravity.BOTTOM, toastType: result.success ? ToastType.success : ToastType.error, ); diff --git a/mobile/lib/presentation/widgets/action_buttons/unstack_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/unstack_action_button.widget.dart index c2757043a3..ecc8a39c74 100644 --- a/mobile/lib/presentation/widgets/action_buttons/unstack_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/unstack_action_button.widget.dart @@ -21,17 +21,12 @@ class UnStackActionButton extends ConsumerWidget { final result = await ref.read(actionProvider.notifier).unStack(source); ref.read(multiSelectProvider.notifier).reset(); - final successMessage = 'unstack_action_prompt'.t( - context: context, - args: {'count': result.count.toString()}, - ); + final successMessage = 'unstack_action_prompt'.t(context: context, args: {'count': result.count.toString()}); if (context.mounted) { ImmichToast.show( context: context, - msg: result.success - ? successMessage - : 'scaffold_body_error_occurred'.t(context: context), + msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context), gravity: ToastGravity.BOTTOM, toastType: result.success ? ToastType.success : ToastType.error, ); diff --git a/mobile/lib/presentation/widgets/action_buttons/upload_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/upload_action_button.widget.dart index 66467e44a3..f037d365d8 100644 --- a/mobile/lib/presentation/widgets/action_buttons/upload_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/upload_action_button.widget.dart @@ -1,16 +1,45 @@ import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; class UploadActionButton extends ConsumerWidget { - const UploadActionButton({super.key}); + final ActionSource source; + + const UploadActionButton({super.key, required this.source}); + + void _onTap(BuildContext context, WidgetRef ref) async { + if (!context.mounted) { + return; + } + + final result = await ref.read(actionProvider.notifier).upload(source); + + final successMessage = 'upload_action_prompt'.t(context: context, args: {'count': result.count.toString()}); + + if (context.mounted) { + ImmichToast.show( + context: context, + msg: result.success ? successMessage : 'scaffold_body_error_occurred'.t(context: context), + gravity: ToastGravity.BOTTOM, + toastType: result.success ? ToastType.success : ToastType.error, + ); + + ref.read(multiSelectProvider.notifier).reset(); + } + } @override Widget build(BuildContext context, WidgetRef ref) { return BaseActionButton( iconData: Icons.backup_outlined, label: "upload".t(context: context), + onPressed: () => _onTap(context, ref), ); } } diff --git a/mobile/lib/presentation/widgets/album/album_selector.widget.dart b/mobile/lib/presentation/widgets/album/album_selector.widget.dart new file mode 100644 index 0000000000..2eec620ec4 --- /dev/null +++ b/mobile/lib/presentation/widgets/album/album_selector.widget.dart @@ -0,0 +1,611 @@ +import 'dart:async'; +import 'dart:math'; + +import 'package:auto_route/auto_route.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/album/album.model.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/theme_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/models/albums/album_search.model.dart'; +import 'package:immich_mobile/pages/common/large_leading_tile.dart'; +import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/utils/remote_album.utils.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; +import 'package:immich_mobile/widgets/common/search_field.dart'; +import 'package:sliver_tools/sliver_tools.dart'; + +typedef AlbumSelectorCallback = void Function(RemoteAlbum album); + +class AlbumSelector extends ConsumerStatefulWidget { + final AlbumSelectorCallback onAlbumSelected; + + const AlbumSelector({super.key, required this.onAlbumSelected}); + + @override + ConsumerState createState() => _AlbumSelectorState(); +} + +class _AlbumSelectorState extends ConsumerState { + bool isGrid = false; + final searchController = TextEditingController(); + QuickFilterMode filterMode = QuickFilterMode.all; + final searchFocusNode = FocusNode(); + + @override + void initState() { + super.initState(); + + // Load albums when component mounts + WidgetsBinding.instance.addPostFrameCallback((_) { + ref.read(remoteAlbumProvider.notifier).refresh(); + }); + + searchController.addListener(() { + onSearch(searchController.text, filterMode); + }); + } + + void onSearch(String searchTerm, QuickFilterMode sortMode) { + final userId = ref.watch(currentUserProvider)?.id; + ref.read(remoteAlbumProvider.notifier).searchAlbums(searchTerm, userId, sortMode); + } + + Future onRefresh() async { + await ref.read(remoteAlbumProvider.notifier).refresh(); + } + + void toggleViewMode() { + setState(() { + isGrid = !isGrid; + }); + } + + void changeFilter(QuickFilterMode sortMode) { + setState(() { + filterMode = sortMode; + }); + } + + void clearSearch() { + setState(() { + filterMode = QuickFilterMode.all; + searchController.clear(); + ref.read(remoteAlbumProvider.notifier).clearSearch(); + }); + } + + @override + void dispose() { + searchController.dispose(); + searchFocusNode.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final albums = ref.watch(remoteAlbumProvider.select((s) => s.filteredAlbums)); + + final userId = ref.watch(currentUserProvider)?.id; + + return MultiSliver( + children: [ + _SearchBar( + searchController: searchController, + searchFocusNode: searchFocusNode, + onSearch: onSearch, + filterMode: filterMode, + onClearSearch: clearSearch, + ), + _QuickFilterButtonRow( + filterMode: filterMode, + onChangeFilter: changeFilter, + onSearch: onSearch, + searchController: searchController, + ), + _QuickSortAndViewMode(isGrid: isGrid, onToggleViewMode: toggleViewMode), + isGrid + ? _AlbumGrid(albums: albums, userId: userId, onAlbumSelected: widget.onAlbumSelected) + : _AlbumList(albums: albums, userId: userId, onAlbumSelected: widget.onAlbumSelected), + ], + ); + } +} + +class _SortButton extends ConsumerStatefulWidget { + const _SortButton(); + + @override + ConsumerState<_SortButton> createState() => _SortButtonState(); +} + +class _SortButtonState extends ConsumerState<_SortButton> { + RemoteAlbumSortMode albumSortOption = RemoteAlbumSortMode.lastModified; + bool albumSortIsReverse = true; + + void onMenuTapped(RemoteAlbumSortMode sortMode) { + final selected = albumSortOption == sortMode; + // Switch direction + if (selected) { + setState(() { + albumSortIsReverse = !albumSortIsReverse; + }); + ref.read(remoteAlbumProvider.notifier).sortFilteredAlbums(sortMode, isReverse: albumSortIsReverse); + } else { + setState(() { + albumSortOption = sortMode; + }); + ref.read(remoteAlbumProvider.notifier).sortFilteredAlbums(sortMode, isReverse: albumSortIsReverse); + } + } + + @override + Widget build(BuildContext context) { + return MenuAnchor( + style: MenuStyle( + elevation: const WidgetStatePropertyAll(1), + shape: WidgetStateProperty.all( + const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(24))), + ), + padding: const WidgetStatePropertyAll(EdgeInsets.all(4)), + ), + consumeOutsideTap: true, + menuChildren: RemoteAlbumSortMode.values + .map( + (sortMode) => MenuItemButton( + leadingIcon: albumSortOption == sortMode + ? albumSortIsReverse + ? Icon( + Icons.keyboard_arrow_down, + color: albumSortOption == sortMode + ? context.colorScheme.onPrimary + : context.colorScheme.onSurface, + ) + : Icon( + Icons.keyboard_arrow_up_rounded, + color: albumSortOption == sortMode + ? context.colorScheme.onPrimary + : context.colorScheme.onSurface, + ) + : const Icon(Icons.abc, color: Colors.transparent), + onPressed: () => onMenuTapped(sortMode), + style: ButtonStyle( + padding: WidgetStateProperty.all(const EdgeInsets.fromLTRB(16, 16, 32, 16)), + backgroundColor: WidgetStateProperty.all( + albumSortOption == sortMode ? context.colorScheme.primary : Colors.transparent, + ), + shape: WidgetStateProperty.all( + const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(24))), + ), + ), + child: Text( + sortMode.key.t(context: context), + style: context.textTheme.titleSmall?.copyWith( + fontWeight: FontWeight.w600, + color: albumSortOption == sortMode + ? context.colorScheme.onPrimary + : context.colorScheme.onSurface.withAlpha(185), + ), + ), + ), + ) + .toList(), + builder: (context, controller, child) { + return GestureDetector( + onTap: () { + if (controller.isOpen) { + controller.close(); + } else { + controller.open(); + } + }, + child: Row( + children: [ + Padding( + padding: const EdgeInsets.only(right: 5), + child: albumSortIsReverse + ? const Icon(Icons.keyboard_arrow_down) + : const Icon(Icons.keyboard_arrow_up_rounded), + ), + Text( + albumSortOption.key.t(context: context), + style: context.textTheme.bodyLarge?.copyWith( + fontWeight: FontWeight.w500, + color: context.colorScheme.onSurface.withAlpha(225), + ), + ), + ], + ), + ); + }, + ); + } +} + +class _SearchBar extends StatelessWidget { + const _SearchBar({ + required this.searchController, + required this.searchFocusNode, + required this.onSearch, + required this.filterMode, + required this.onClearSearch, + }); + + final TextEditingController searchController; + final FocusNode searchFocusNode; + final void Function(String, QuickFilterMode) onSearch; + final QuickFilterMode filterMode; + final VoidCallback onClearSearch; + + @override + Widget build(BuildContext context) { + return SliverPadding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + sliver: SliverToBoxAdapter( + child: Container( + decoration: BoxDecoration( + border: Border.all(color: context.colorScheme.onSurface.withAlpha(0), width: 0), + borderRadius: const BorderRadius.all(Radius.circular(24)), + gradient: LinearGradient( + colors: [ + context.colorScheme.primary.withValues(alpha: 0.075), + context.colorScheme.primary.withValues(alpha: 0.09), + context.colorScheme.primary.withValues(alpha: 0.075), + ], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + transform: const GradientRotation(0.5 * pi), + ), + ), + child: SearchField( + autofocus: false, + contentPadding: const EdgeInsets.all(16), + hintText: 'search_albums'.tr(), + prefixIcon: const Icon(Icons.search_rounded), + suffixIcon: searchController.text.isNotEmpty + ? IconButton(icon: const Icon(Icons.clear_rounded), onPressed: onClearSearch) + : null, + controller: searchController, + onChanged: (_) => onSearch(searchController.text, filterMode), + focusNode: searchFocusNode, + onTapOutside: (_) => searchFocusNode.unfocus(), + ), + ), + ), + ); + } +} + +class _QuickFilterButtonRow extends StatelessWidget { + const _QuickFilterButtonRow({ + required this.filterMode, + required this.onChangeFilter, + required this.onSearch, + required this.searchController, + }); + + final QuickFilterMode filterMode; + final void Function(QuickFilterMode) onChangeFilter; + final void Function(String, QuickFilterMode) onSearch; + final TextEditingController searchController; + + @override + Widget build(BuildContext context) { + return SliverPadding( + padding: const EdgeInsets.symmetric(horizontal: 16), + sliver: SliverToBoxAdapter( + child: Wrap( + spacing: 4, + runSpacing: 4, + children: [ + _QuickFilterButton( + label: 'all'.tr(), + isSelected: filterMode == QuickFilterMode.all, + onTap: () { + onChangeFilter(QuickFilterMode.all); + onSearch(searchController.text, QuickFilterMode.all); + }, + ), + _QuickFilterButton( + label: 'shared_with_me'.tr(), + isSelected: filterMode == QuickFilterMode.sharedWithMe, + onTap: () { + onChangeFilter(QuickFilterMode.sharedWithMe); + onSearch(searchController.text, QuickFilterMode.sharedWithMe); + }, + ), + _QuickFilterButton( + label: 'my_albums'.tr(), + isSelected: filterMode == QuickFilterMode.myAlbums, + onTap: () { + onChangeFilter(QuickFilterMode.myAlbums); + onSearch(searchController.text, QuickFilterMode.myAlbums); + }, + ), + ], + ), + ), + ); + } +} + +class _QuickFilterButton extends StatelessWidget { + const _QuickFilterButton({required this.isSelected, required this.onTap, required this.label}); + + final bool isSelected; + final VoidCallback onTap; + final String label; + + @override + Widget build(BuildContext context) { + return TextButton( + onPressed: onTap, + style: ButtonStyle( + backgroundColor: WidgetStateProperty.all(isSelected ? context.colorScheme.primary : Colors.transparent), + shape: WidgetStateProperty.all( + RoundedRectangleBorder( + borderRadius: const BorderRadius.all(Radius.circular(20)), + side: BorderSide(color: context.colorScheme.onSurface.withAlpha(25), width: 1), + ), + ), + ), + child: Text( + label, + style: TextStyle( + color: isSelected ? context.colorScheme.onPrimary : context.colorScheme.onSurface, + fontSize: 14, + ), + ), + ); + } +} + +class _QuickSortAndViewMode extends StatelessWidget { + const _QuickSortAndViewMode({required this.isGrid, required this.onToggleViewMode}); + + final bool isGrid; + final VoidCallback onToggleViewMode; + + @override + Widget build(BuildContext context) { + return SliverPadding( + padding: const EdgeInsets.symmetric(horizontal: 16), + sliver: SliverToBoxAdapter( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const _SortButton(), + IconButton( + icon: Icon(isGrid ? Icons.view_list_outlined : Icons.grid_view_outlined, size: 24), + onPressed: onToggleViewMode, + ), + ], + ), + ), + ); + } +} + +class _AlbumList extends ConsumerWidget { + const _AlbumList({required this.albums, required this.userId, required this.onAlbumSelected}); + + final List albums; + final String? userId; + final AlbumSelectorCallback onAlbumSelected; + + @override + Widget build(BuildContext context, WidgetRef ref) { + if (albums.isEmpty) { + return const SliverToBoxAdapter( + child: Center( + child: Padding(padding: EdgeInsets.all(20.0), child: Text('No albums found')), + ), + ); + } + + return SliverPadding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + sliver: SliverList.builder( + itemBuilder: (_, index) { + final album = albums[index]; + + return Padding( + padding: const EdgeInsets.only(bottom: 8.0), + child: LargeLeadingTile( + title: Text( + album.name, + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600), + ), + subtitle: Text( + '${'items_count'.t(context: context, args: {'count': album.assetCount})} • ${album.ownerId != userId ? 'shared_by_user'.t(context: context, args: {'user': album.ownerName}) : 'owned'.t(context: context)}', + overflow: TextOverflow.ellipsis, + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), + ), + onTap: () => onAlbumSelected(album), + leadingPadding: const EdgeInsets.only(right: 16), + leading: album.thumbnailAssetId != null + ? ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(15)), + child: SizedBox(width: 80, height: 80, child: Thumbnail(remoteId: album.thumbnailAssetId)), + ) + : SizedBox( + width: 80, + height: 80, + child: Container( + decoration: BoxDecoration( + color: context.colorScheme.surfaceContainer, + borderRadius: const BorderRadius.all(Radius.circular(16)), + border: Border.all(color: context.colorScheme.outline.withAlpha(50), width: 1), + ), + child: const Icon(Icons.photo_album_rounded, size: 24, color: Colors.grey), + ), + ), + ), + ); + }, + itemCount: albums.length, + ), + ); + } +} + +class _AlbumGrid extends StatelessWidget { + const _AlbumGrid({required this.albums, required this.userId, required this.onAlbumSelected}); + + final List albums; + final String? userId; + final AlbumSelectorCallback onAlbumSelected; + + @override + Widget build(BuildContext context) { + if (albums.isEmpty) { + return const SliverToBoxAdapter( + child: Center( + child: Padding(padding: EdgeInsets.all(20.0), child: Text('No albums found')), + ), + ); + } + + return SliverPadding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + sliver: SliverGrid( + gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( + maxCrossAxisExtent: 250, + mainAxisSpacing: 4, + crossAxisSpacing: 4, + childAspectRatio: .7, + ), + delegate: SliverChildBuilderDelegate((context, index) { + final album = albums[index]; + return _GridAlbumCard(album: album, userId: userId, onAlbumSelected: onAlbumSelected); + }, childCount: albums.length), + ), + ); + } +} + +class _GridAlbumCard extends ConsumerWidget { + const _GridAlbumCard({required this.album, required this.userId, required this.onAlbumSelected}); + + final RemoteAlbum album; + final String? userId; + final AlbumSelectorCallback onAlbumSelected; + + @override + Widget build(BuildContext context, WidgetRef ref) { + return GestureDetector( + onTap: () => onAlbumSelected(album), + child: Card( + elevation: 0, + color: context.colorScheme.surfaceBright, + shape: RoundedRectangleBorder( + borderRadius: const BorderRadius.all(Radius.circular(16)), + side: BorderSide(color: context.colorScheme.onSurface.withAlpha(25), width: 1), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + flex: 2, + child: ClipRRect( + borderRadius: const BorderRadius.vertical(top: Radius.circular(15)), + child: SizedBox( + width: double.infinity, + child: album.thumbnailAssetId != null + ? Thumbnail(remoteId: album.thumbnailAssetId) + : Container( + color: context.colorScheme.surfaceContainerHighest, + child: const Icon(Icons.photo_album_rounded, size: 40, color: Colors.grey), + ), + ), + ), + ), + Expanded( + flex: 1, + child: Padding( + padding: const EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Text( + album.name, + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: context.textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600), + ), + Text( + '${'items_count'.t(context: context, args: {'count': album.assetCount})} • ${album.ownerId != userId ? 'shared_by_user'.t(context: context, args: {'user': album.ownerName}) : 'owned'.t(context: context)}', + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: context.textTheme.labelMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), + ), + ], + ), + ), + ), + ], + ), + ), + ); + } +} + +class AddToAlbumHeader extends ConsumerWidget { + const AddToAlbumHeader({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + Future onCreateAlbum() async { + final newAlbum = await ref + .read(remoteAlbumProvider.notifier) + .createAlbum( + title: "Untitled Album", + assetIds: ref.read(multiSelectProvider).selectedAssets.map((e) => (e as RemoteAsset).id).toList(), + ); + + if (newAlbum == null) { + ImmichToast.show(context: context, toastType: ToastType.error, msg: 'errors.failed_to_create_album'.tr()); + return; + } + + ref.read(currentRemoteAlbumProvider.notifier).setAlbum(newAlbum); + context.pushRoute(RemoteAlbumRoute(album: newAlbum)); + } + + return SliverPadding( + padding: const EdgeInsets.symmetric(horizontal: 16), + sliver: SliverToBoxAdapter( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text("add_to_album", style: context.textTheme.titleSmall).tr(), + TextButton.icon( + style: TextButton.styleFrom( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), // remove internal padding + minimumSize: const Size(0, 0), // allow shrinking + tapTargetSize: MaterialTapTargetSize.shrinkWrap, // remove extra height + ), + onPressed: onCreateAlbum, + icon: Icon(Icons.add, color: context.primaryColor), + label: Text( + "common_create_new_album", + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold, fontSize: 14), + ).tr(), + ), + ], + ), + ), + ); + } +} diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_stack.provider.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_stack.provider.dart index cb4e02b56c..1eb3366e30 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/asset_stack.provider.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_stack.provider.dart @@ -2,15 +2,10 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; -class StackChildrenNotifier - extends AutoDisposeFamilyAsyncNotifier, BaseAsset?> { +class StackChildrenNotifier extends AutoDisposeFamilyAsyncNotifier, BaseAsset?> { @override Future> build(BaseAsset? asset) async { - if (asset == null || - asset is! RemoteAsset || - asset.stackId == null || - // The stackCount check is to ensure we only fetch stacks for timelines that have stacks - asset.stackCount == 0) { + if (asset == null || asset is! RemoteAsset || asset.stackId == null) { return const []; } @@ -19,6 +14,4 @@ class StackChildrenNotifier } final stackChildrenNotifier = AsyncNotifierProvider.autoDispose - .family, BaseAsset?>( - StackChildrenNotifier.new, -); + .family, BaseAsset?>(StackChildrenNotifier.new); diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_stack.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_stack.widget.dart index 8b3d0c6575..e5d1487d53 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/asset_stack.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_stack.widget.dart @@ -11,11 +11,8 @@ class AssetStackRow extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - int opacity = ref.watch( - assetViewerProvider.select((state) => state.backgroundOpacity), - ); - final showControls = - ref.watch(assetViewerProvider.select((s) => s.showingControls)); + int opacity = ref.watch(assetViewerProvider.select((state) => state.backgroundOpacity)); + final showControls = ref.watch(assetViewerProvider.select((s) => s.showingControls)); if (!showControls) { opacity = 0; @@ -28,11 +25,10 @@ class AssetStackRow extends ConsumerWidget { child: AnimatedOpacity( opacity: opacity / 255, duration: Durations.short2, - child: ref.watch(stackChildrenNotifier(asset)).when( - data: (state) => SizedBox.square( - dimension: 80, - child: _StackList(stack: state), - ), + child: ref + .watch(stackChildrenNotifier(asset)) + .when( + data: (state) => SizedBox.square(dimension: 80, child: _StackList(stack: state)), error: (_, __) => const SizedBox.shrink(), loading: () => const SizedBox.shrink(), ), @@ -50,11 +46,7 @@ class _StackList extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { return ListView.builder( scrollDirection: Axis.horizontal, - padding: const EdgeInsets.only( - left: 5, - right: 5, - bottom: 30, - ), + padding: const EdgeInsets.only(left: 5, right: 5, bottom: 30), itemCount: stack.length, itemBuilder: (ctx, index) { final asset = stack[index]; @@ -68,14 +60,11 @@ class _StackList extends ConsumerWidget { child: Container( height: 60, width: 60, - decoration: index == - ref.watch(assetViewerProvider.select((s) => s.stackIndex)) + decoration: index == ref.watch(assetViewerProvider.select((s) => s.stackIndex)) ? const BoxDecoration( color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(6)), - border: Border.fromBorderSide( - BorderSide(color: Colors.white, width: 2), - ), + border: Border.fromBorderSide(BorderSide(color: Colors.white, width: 2)), ) : const BoxDecoration( color: Colors.white, @@ -89,10 +78,7 @@ class _StackList extends ConsumerWidget { children: [ Image( fit: BoxFit.cover, - image: getThumbnailImageProvider( - remoteId: asset.id, - size: const Size.square(60), - ), + image: getThumbnailImageProvider(remoteId: asset.id, size: const Size.square(60)), ), if (asset.isVideo) const Icon( @@ -100,11 +86,7 @@ class _StackList extends ConsumerWidget { color: Colors.white, size: 16, shadows: [ - Shadow( - blurRadius: 5.0, - color: Color.fromRGBO(0, 0, 0, 0.6), - offset: Offset(0.0, 0.0), - ), + Shadow(blurRadius: 5.0, color: Color.fromRGBO(0, 0, 0, 0.6), offset: Offset(0.0, 0.0)), ], ), ], diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart index 50f4a09197..2fa54ad65d 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart @@ -25,6 +25,7 @@ import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider import 'package:immich_mobile/providers/cast.provider.dart'; import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; +import 'package:immich_mobile/providers/routes.provider.dart'; import 'package:immich_mobile/widgets/photo_view/photo_view.dart'; import 'package:immich_mobile/widgets/photo_view/photo_view_gallery.dart'; import 'package:platform/platform.dart'; @@ -33,12 +34,9 @@ import 'package:platform/platform.dart'; class AssetViewerPage extends StatelessWidget { final int initialIndex; final TimelineService timelineService; + final int? heroOffset; - const AssetViewerPage({ - super.key, - required this.initialIndex, - required this.timelineService, - }); + const AssetViewerPage({super.key, required this.initialIndex, required this.timelineService, this.heroOffset}); @override Widget build(BuildContext context) { @@ -46,7 +44,7 @@ class AssetViewerPage extends StatelessWidget { // since the Timeline and AssetViewer are on different routes / Widget subtrees. return ProviderScope( overrides: [timelineServiceProvider.overrideWithValue(timelineService)], - child: AssetViewer(initialIndex: initialIndex), + child: AssetViewer(initialIndex: initialIndex, heroOffset: heroOffset), ); } } @@ -54,12 +52,9 @@ class AssetViewerPage extends StatelessWidget { class AssetViewer extends ConsumerStatefulWidget { final int initialIndex; final Platform? platform; + final int? heroOffset; - const AssetViewer({ - super.key, - required this.initialIndex, - this.platform, - }); + const AssetViewer({super.key, required this.initialIndex, this.platform, this.heroOffset}); @override ConsumerState createState() => _AssetViewerState(); @@ -84,6 +79,7 @@ class _AssetViewerState extends ConsumerState { bool blockGestures = false; bool dragInProgress = false; bool shouldPopOnDrag = false; + bool assetReloadRequested = false; double? initialScale; double previousExtent = _kBottomSheetMinimumExtent; Offset dragDownPosition = Offset.zero; @@ -106,7 +102,7 @@ class _AssetViewerState extends ConsumerState { _onAssetChanged(widget.initialIndex); }); reloadSubscription = EventStream.shared.listen(_onEvent); - heroOffset = TabsRouterScope.of(context)?.controller.activeIndex ?? 0; + heroOffset = widget.heroOffset ?? TabsRouterScope.of(context)?.controller.activeIndex ?? 0; } @override @@ -118,12 +114,10 @@ class _AssetViewerState extends ConsumerState { super.dispose(); } - bool get showingBottomSheet => - ref.read(assetViewerProvider.select((s) => s.showingBottomSheet)); + bool get showingBottomSheet => ref.read(assetViewerProvider.select((s) => s.showingBottomSheet)); Color get backgroundColor { - final opacity = - ref.read(assetViewerProvider.select((s) => s.backgroundOpacity)); + final opacity = ref.read(assetViewerProvider.select((s) => s.backgroundOpacity)); return Colors.black.withAlpha(opacity); } @@ -137,9 +131,7 @@ class _AssetViewerState extends ConsumerState { // This is used to calculate the scale of the asset when the bottom sheet is showing. // It is a small increment to ensure that the asset is slightly zoomed in when the // bottom sheet is showing, which emulates the zoom effect. - double get _getScaleForBottomSheet => - (viewController?.prevValue.scale ?? viewController?.value.scale ?? 1.0) + - 0.01; + double get _getScaleForBottomSheet => (viewController?.prevValue.scale ?? viewController?.value.scale ?? 1.0) + 0.01; double _getVerticalOffsetForBottomSheet(double extent) => (context.height * extent) - (context.height * _kBottomSheetMinimumExtent); @@ -160,11 +152,7 @@ class _AssetViewerState extends ConsumerState { context, onError: (_, __) {}, ), - precacheImage( - getFullImageProvider(asset, size: screenSize), - context, - onError: (_, __) {}, - ), + precacheImage(getFullImageProvider(asset, size: screenSize), context, onError: (_, __) {}), ]), ); } @@ -220,9 +208,7 @@ class _AssetViewerState extends ConsumerState { duration: const Duration(seconds: 2), content: Text( "local_asset_cast_failed".tr(), - style: context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), ), ), ); @@ -232,9 +218,9 @@ class _AssetViewerState extends ConsumerState { void _onPageBuild(PhotoViewControllerBase controller) { viewController ??= controller; - if (showingBottomSheet) { - final verticalOffset = (context.height * bottomSheetController.size) - - (context.height * _kBottomSheetMinimumExtent); + if (showingBottomSheet && bottomSheetController.isAttached) { + final verticalOffset = + (context.height * bottomSheetController.size) - (context.height * _kBottomSheetMinimumExtent); controller.position = Offset(0, -verticalOffset); } } @@ -262,7 +248,7 @@ class _AssetViewerState extends ConsumerState { initialPhotoViewState = controller.value; final isZoomed = scaleStateController.scaleState == PhotoViewScaleState.zoomedIn || - scaleStateController.scaleState == PhotoViewScaleState.covering; + scaleStateController.scaleState == PhotoViewScaleState.covering; if (!showingBottomSheet && isZoomed) { blockGestures = true; } @@ -347,13 +333,9 @@ class _AssetViewerState extends ConsumerState { updatedScale = initialPhotoViewState.scale! * (1.0 - scaleReduction); } - final backgroundOpacity = - (255 * (1.0 - (scaleReduction / dragRatio))).round(); + final backgroundOpacity = (255 * (1.0 - (scaleReduction / dragRatio))).round(); - viewController?.updateMultiple( - position: initialPhotoViewState.position + delta, - scale: updatedScale, - ); + viewController?.updateMultiple(position: initialPhotoViewState.position + delta, scale: updatedScale); ref.read(assetViewerProvider.notifier).setOpacity(backgroundOpacity); } @@ -403,7 +385,12 @@ class _AssetViewerState extends ConsumerState { void _onEvent(Event event) { if (event is TimelineReloadEvent) { - _onTimelineReload(event); + _onTimelineReloadEvent(); + return; + } + + if (event is ViewerReloadAssetEvent) { + assetReloadRequested = true; return; } @@ -416,14 +403,22 @@ class _AssetViewerState extends ConsumerState { } } - void _onTimelineReload(_) { - setState(() { - totalAssets = ref.read(timelineServiceProvider).totalAssets; - if (totalAssets == 0) { - context.maybePop(); - return; - } + void _onTimelineReloadEvent() { + totalAssets = ref.read(timelineServiceProvider).totalAssets; + if (totalAssets == 0) { + context.maybePop(); + return; + } + if (assetReloadRequested) { + assetReloadRequested = false; + _onAssetReloadEvent(); + return; + } + } + + void _onAssetReloadEvent() { + setState(() { final index = pageController.page?.round() ?? 0; final newAsset = ref.read(timelineServiceProvider).getAsset(index); final currentAsset = ref.read(currentAssetNotifier); @@ -437,32 +432,21 @@ class _AssetViewerState extends ConsumerState { }); } - void _openBottomSheet( - BuildContext ctx, { - double extent = _kBottomSheetMinimumExtent, - }) { + void _openBottomSheet(BuildContext ctx, {double extent = _kBottomSheetMinimumExtent}) { ref.read(assetViewerProvider.notifier).setBottomSheet(true); initialScale = viewController?.scale; viewController?.updateMultiple(scale: _getScaleForBottomSheet); previousExtent = _kBottomSheetMinimumExtent; sheetCloseController = showBottomSheet( context: ctx, - sheetAnimationStyle: const AnimationStyle( - duration: Durations.short4, - reverseDuration: Durations.short2, - ), + sheetAnimationStyle: const AnimationStyle(duration: Durations.short4, reverseDuration: Durations.short2), constraints: const BoxConstraints(maxWidth: double.infinity), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical(top: Radius.circular(20.0)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(20.0))), backgroundColor: ctx.colorScheme.surfaceContainerLowest, builder: (_) { return NotificationListener( onNotification: _onNotification, - child: AssetDetailBottomSheet( - controller: bottomSheetController, - initialChildSize: extent, - ), + child: AssetDetailBottomSheet(controller: bottomSheetController, initialChildSize: extent), ); }, ); @@ -479,41 +463,26 @@ class _AssetViewerState extends ConsumerState { } void _snapBottomSheet() { - if (bottomSheetController.size > _kBottomSheetSnapExtent || + if (!bottomSheetController.isAttached || + bottomSheetController.size > _kBottomSheetSnapExtent || bottomSheetController.size < 0.4) { return; } isSnapping = true; - bottomSheetController.animateTo( - _kBottomSheetSnapExtent, - duration: Durations.short3, - curve: Curves.easeOut, - ); + bottomSheetController.animateTo(_kBottomSheetSnapExtent, duration: Durations.short3, curve: Curves.easeOut); } - Widget _placeholderBuilder( - BuildContext ctx, - ImageChunkEvent? progress, - int index, - ) { + Widget _placeholderBuilder(BuildContext ctx, ImageChunkEvent? progress, int index) { BaseAsset asset = ref.read(timelineServiceProvider).getAsset(index); final stackChildren = ref.read(stackChildrenNotifier(asset)).valueOrNull; if (stackChildren != null && stackChildren.isNotEmpty) { - asset = stackChildren - .elementAt(ref.read(assetViewerProvider.select((s) => s.stackIndex))); + asset = stackChildren.elementAt(ref.read(assetViewerProvider.select((s) => s.stackIndex))); } return Container( width: double.infinity, height: double.infinity, color: backgroundColor, - child: Thumbnail( - asset: asset, - fit: BoxFit.contain, - size: Size( - ctx.width, - ctx.height, - ), - ), + child: Thumbnail(asset: asset, fit: BoxFit.contain, size: Size(ctx.width, ctx.height)), ); } @@ -532,8 +501,7 @@ class _AssetViewerState extends ConsumerState { BaseAsset asset = ref.read(timelineServiceProvider).getAsset(index); final stackChildren = ref.read(stackChildrenNotifier(asset)).valueOrNull; if (stackChildren != null && stackChildren.isNotEmpty) { - asset = stackChildren - .elementAt(ref.read(assetViewerProvider.select((s) => s.stackIndex))); + asset = stackChildren.elementAt(ref.read(assetViewerProvider.select((s) => s.stackIndex))); } final isPlayingMotionVideo = ref.read(isPlayingMotionVideoProvider); @@ -549,8 +517,7 @@ class _AssetViewerState extends ConsumerState { return PhotoViewGalleryPageOptions( key: ValueKey(asset.heroTag), imageProvider: getFullImageProvider(asset, size: size), - heroAttributes: - PhotoViewHeroAttributes(tag: '${asset.heroTag}_$heroOffset'), + heroAttributes: PhotoViewHeroAttributes(tag: '${asset.heroTag}_$heroOffset'), filterQuality: FilterQuality.high, tightMode: true, initialScale: PhotoViewComputedScale.contained * 0.999, @@ -565,11 +532,7 @@ class _AssetViewerState extends ConsumerState { width: ctx.width, height: ctx.height, color: backgroundColor, - child: Thumbnail( - asset: asset, - fit: BoxFit.contain, - size: size, - ), + child: Thumbnail(asset: asset, fit: BoxFit.contain, size: size), ), ); } @@ -585,8 +548,7 @@ class _AssetViewerState extends ConsumerState { onDragUpdate: _onDragUpdate, onDragEnd: _onDragEnd, onTapDown: _onTapDown, - heroAttributes: - PhotoViewHeroAttributes(tag: '${asset.heroTag}_$heroOffset'), + heroAttributes: PhotoViewHeroAttributes(tag: '${asset.heroTag}_$heroOffset'), filterQuality: FilterQuality.high, initialScale: PhotoViewComputedScale.contained * 0.99, maxScale: 1.0, @@ -600,8 +562,7 @@ class _AssetViewerState extends ConsumerState { asset: asset, image: Image( key: ValueKey(asset), - image: - getFullImageProvider(asset, size: Size(ctx.width, ctx.height)), + image: getFullImageProvider(asset, size: Size(ctx.width, ctx.height)), fit: BoxFit.contain, height: ctx.height, width: ctx.width, @@ -626,8 +587,7 @@ class _AssetViewerState extends ConsumerState { ref.watch(isPlayingMotionVideoProvider); // Listen for casting changes and send initial asset to the cast provider - ref.listen(castProvider.select((value) => value.isCasting), - (_, isCasting) async { + ref.listen(castProvider.select((value) => value.isCasting), (_, isCasting) async { if (!isCasting) return; final asset = ref.read(currentAssetNotifier); @@ -638,6 +598,8 @@ class _AssetViewerState extends ConsumerState { }); }); + final isInLockedView = ref.watch(inLockedViewProvider); + // Currently it is not possible to scroll the asset when the bottom sheet is open all the way. // Issue: https://github.com/flutter/flutter/issues/109037 // TODO: Add a custom scrum builder once the fix lands on stable @@ -654,8 +616,7 @@ class _AssetViewerState extends ConsumerState { pageController: pageController, scrollPhysics: platform.isIOS ? const FastScrollPhysics() // Use bouncing physics for iOS - : const FastClampingScrollPhysics() // Use heavy physics for Android - , + : const FastClampingScrollPhysics(), // Use heavy physics for Android itemCount: totalAssets, onPageChanged: _onPageChanged, onPageBuild: _onPageBuild, @@ -666,14 +627,11 @@ class _AssetViewerState extends ConsumerState { ), bottomNavigationBar: showingBottomSheet ? const SizedBox.shrink() - : const Column( + : Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - AssetStackRow(), - ViewerBottomBar(), - ], + children: [const AssetStackRow(), if (!isInLockedView) const ViewerBottomBar()], ), ), ); diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.state.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.state.dart index 825b637e8d..88513516eb 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.state.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.state.dart @@ -7,6 +7,10 @@ class ViewerOpenBottomSheetEvent extends Event { const ViewerOpenBottomSheetEvent(); } +class ViewerReloadAssetEvent extends Event { + const ViewerReloadAssetEvent(); +} + class AssetViewerState { final int backgroundOpacity; final bool showingBottomSheet; @@ -75,17 +79,11 @@ class AssetViewerStateNotifier extends AutoDisposeNotifier { } void setOpacity(int opacity) { - state = state.copyWith( - backgroundOpacity: opacity, - showingControls: opacity == 255 ? true : state.showingControls, - ); + state = state.copyWith(backgroundOpacity: opacity, showingControls: opacity == 255 ? true : state.showingControls); } void setBottomSheet(bool showing) { - state = state.copyWith( - showingBottomSheet: showing, - showingControls: showing ? true : state.showingControls, - ); + state = state.copyWith(showingBottomSheet: showing, showingControls: showing ? true : state.showingControls); if (showing) { ref.read(videoPlayerControlsProvider.notifier).pause(); } @@ -104,7 +102,6 @@ class AssetViewerStateNotifier extends AutoDisposeNotifier { } } -final assetViewerProvider = - AutoDisposeNotifierProvider( +final assetViewerProvider = AutoDisposeNotifierProvider( AssetViewerStateNotifier.new, ); diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart index 9237c3bcdb..881ed4d150 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart @@ -3,10 +3,11 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; -import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart'; -import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/share_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; @@ -24,14 +25,9 @@ class ViewerBottomBar extends ConsumerWidget { final user = ref.watch(currentUserProvider); final isOwner = asset is RemoteAsset && asset.ownerId == user?.id; - final isSheetOpen = ref.watch( - assetViewerProvider.select((s) => s.showingBottomSheet), - ); - int opacity = ref.watch( - assetViewerProvider.select((state) => state.backgroundOpacity), - ); - final showControls = - ref.watch(assetViewerProvider.select((s) => s.showingControls)); + final isSheetOpen = ref.watch(assetViewerProvider.select((s) => s.showingBottomSheet)); + int opacity = ref.watch(assetViewerProvider.select((state) => state.backgroundOpacity)); + final showControls = ref.watch(assetViewerProvider.select((s) => s.showingControls)); if (!showControls) { opacity = 0; @@ -39,9 +35,11 @@ class ViewerBottomBar extends ConsumerWidget { final actions = [ const ShareActionButton(source: ActionSource.viewer), - const _EditActionButton(), - if (asset.hasRemote && isOwner) - const ArchiveActionButton(source: ActionSource.viewer), + if (asset.isLocalOnly) const UploadActionButton(source: ActionSource.viewer), + if (asset.hasRemote && isOwner) const ArchiveActionButton(source: ActionSource.viewer), + asset.isLocalOnly + ? const DeleteLocalActionButton(source: ActionSource.viewer) + : const DeleteActionButton(source: ActionSource.viewer, showConfirmation: true), ]; return IgnorePointer( @@ -53,31 +51,23 @@ class ViewerBottomBar extends ConsumerWidget { duration: Durations.short4, child: isSheetOpen ? const SizedBox.shrink() - : SafeArea( - child: Theme( - data: context.themeData.copyWith( - iconTheme: - const IconThemeData(size: 22, color: Colors.white), - textTheme: context.themeData.textTheme.copyWith( - labelLarge: - context.themeData.textTheme.labelLarge?.copyWith( - color: Colors.white, - ), - ), + : Theme( + data: context.themeData.copyWith( + iconTheme: const IconThemeData(size: 22, color: Colors.white), + textTheme: context.themeData.textTheme.copyWith( + labelLarge: context.themeData.textTheme.labelLarge?.copyWith(color: Colors.white), ), - child: Container( - height: asset.isVideo ? 160 : 80, - color: Colors.black.withAlpha(125), - child: Column( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - if (asset.isVideo) const VideoControls(), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: actions, - ), - ], - ), + ), + child: Container( + height: context.padding.bottom + (asset.isVideo ? 160 : 90), + color: Colors.black.withAlpha(125), + padding: EdgeInsets.only(bottom: context.padding.bottom), + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + if (asset.isVideo) const VideoControls(), + Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: actions), + ], ), ), ), @@ -86,15 +76,3 @@ class ViewerBottomBar extends ConsumerWidget { ); } } - -class _EditActionButton extends ConsumerWidget { - const _EditActionButton(); - - @override - Widget build(BuildContext context, WidgetRef ref) { - return BaseActionButton( - iconData: Icons.tune_outlined, - label: 'edit'.t(context: context), - ); - } -} diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart index 6695022d1e..1d76d3c39d 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart @@ -8,6 +8,7 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/download_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/move_to_lock_folder_action_button.widget.dart'; @@ -15,11 +16,15 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/share_action_b import 'package:immich_mobile/presentation/widgets/action_buttons/share_link_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/trash_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart'; -import 'package:immich_mobile/presentation/widgets/asset_viewer/bottom_sheet/location_details.widget.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/bottom_sheet/sheet_location_details.widget.dart'; +import 'package:immich_mobile/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart'; import 'package:immich_mobile/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; +import 'package:immich_mobile/providers/routes.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/utils/bytes_units.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; const _kSeparator = ' • '; @@ -27,11 +32,7 @@ class AssetDetailBottomSheet extends ConsumerWidget { final DraggableScrollableController? controller; final double initialChildSize; - const AssetDetailBottomSheet({ - this.controller, - this.initialChildSize = 0.35, - super.key, - }); + const AssetDetailBottomSheet({this.controller, this.initialChildSize = 0.35, super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -40,31 +41,32 @@ class AssetDetailBottomSheet extends ConsumerWidget { return const SizedBox.shrink(); } - final isTrashEnable = ref.watch( - serverInfoProvider.select((state) => state.serverFeatures.trash), - ); + final isTrashEnable = ref.watch(serverInfoProvider.select((state) => state.serverFeatures.trash)); + + final isInLockedView = ref.watch(inLockedViewProvider); final actions = [ const ShareActionButton(source: ActionSource.viewer), if (asset.hasRemote) ...[ const ShareLinkActionButton(source: ActionSource.viewer), const ArchiveActionButton(source: ActionSource.viewer), - if (!asset.hasLocal) const DownloadActionButton(), + if (!asset.hasLocal) const DownloadActionButton(source: ActionSource.viewer), isTrashEnable ? const TrashActionButton(source: ActionSource.viewer) : const DeletePermanentActionButton(source: ActionSource.viewer), - const MoveToLockFolderActionButton( - source: ActionSource.viewer, - ), + const DeleteActionButton(source: ActionSource.viewer), + const MoveToLockFolderActionButton(source: ActionSource.viewer), ], if (asset.storage == AssetState.local) ...[ const DeleteLocalActionButton(source: ActionSource.viewer), - const UploadActionButton(), + const UploadActionButton(source: ActionSource.timeline), ], ]; + final lockedViewActions = []; + return BaseBottomSheet( - actions: actions, + actions: isInLockedView ? lockedViewActions : actions, slivers: const [_AssetDetailBottomSheet()], controller: controller, initialChildSize: initialChildSize, @@ -73,6 +75,7 @@ class AssetDetailBottomSheet extends ConsumerWidget { expand: false, shouldCloseOnMinExtent: false, resizeOnScroll: false, + backgroundColor: context.isDarkTheme ? Colors.black : Colors.white, ); } } @@ -84,23 +87,23 @@ class _AssetDetailBottomSheet extends ConsumerWidget { final dateTime = asset.createdAt.toLocal(); final date = DateFormat.yMMMEd(ctx.locale.toLanguageTag()).format(dateTime); final time = DateFormat.jm(ctx.locale.toLanguageTag()).format(dateTime); - return '$date$_kSeparator$time'; + final timezone = dateTime.timeZoneOffset.isNegative + ? 'UTC-${dateTime.timeZoneOffset.inHours.abs().toString().padLeft(2, '0')}:${(dateTime.timeZoneOffset.inMinutes.abs() % 60).toString().padLeft(2, '0')}' + : 'UTC+${dateTime.timeZoneOffset.inHours.toString().padLeft(2, '0')}:${(dateTime.timeZoneOffset.inMinutes.abs() % 60).toString().padLeft(2, '0')}'; + return '$date$_kSeparator$time $timezone'; } String _getFileInfo(BaseAsset asset, ExifInfo? exifInfo) { final height = asset.height ?? exifInfo?.height; final width = asset.width ?? exifInfo?.width; - final resolution = - (width != null && height != null) ? "$width x $height" : null; - final fileSize = - exifInfo?.fileSize != null ? formatBytes(exifInfo!.fileSize!) : null; + final resolution = (width != null && height != null) ? "${width.toInt()} x ${height.toInt()}" : null; + final fileSize = exifInfo?.fileSize != null ? formatBytes(exifInfo!.fileSize!) : null; return switch ((fileSize, resolution)) { (null, null) => '', (String fileSize, null) => fileSize, (null, String resolution) => resolution, - (String fileSize, String resolution) => - '$fileSize$_kSeparator$resolution', + (String fileSize, String resolution) => '$fileSize$_kSeparator$resolution', }; } @@ -122,17 +125,12 @@ class _AssetDetailBottomSheet extends ConsumerWidget { return null; } - final fNumber = - exifInfo.fNumber.isNotEmpty ? 'ƒ/${exifInfo.fNumber}' : null; - final exposureTime = - exifInfo.exposureTime.isNotEmpty ? exifInfo.exposureTime : null; - final focalLength = - exifInfo.focalLength.isNotEmpty ? '${exifInfo.focalLength} mm' : null; + final fNumber = exifInfo.fNumber.isNotEmpty ? 'ƒ/${exifInfo.fNumber}' : null; + final exposureTime = exifInfo.exposureTime.isNotEmpty ? exifInfo.exposureTime : null; + final focalLength = exifInfo.focalLength.isNotEmpty ? '${exifInfo.focalLength} mm' : null; final iso = exifInfo.iso != null ? 'ISO ${exifInfo.iso}' : null; - return [fNumber, exposureTime, focalLength, iso] - .where((spec) => spec != null && spec.isNotEmpty) - .join(_kSeparator); + return [fNumber, exposureTime, focalLength, iso].where((spec) => spec != null && spec.isNotEmpty).join(_kSeparator); } @override @@ -145,51 +143,53 @@ class _AssetDetailBottomSheet extends ConsumerWidget { final exifInfo = ref.watch(currentAssetExifProvider).valueOrNull; final cameraTitle = _getCameraInfoTitle(exifInfo); + Future editDateTime() async { + await ref.read(actionProvider.notifier).editDateTime(ActionSource.viewer, context); + } + return SliverList.list( children: [ // Asset Date and Time _SheetTile( title: _getDateTime(context, asset), - titleStyle: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - fontSize: 16, - ), + titleStyle: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600), + trailing: asset.hasRemote ? const Icon(Icons.edit, size: 18) : null, + onTap: asset.hasRemote ? () async => await editDateTime() : null, ), + if (exifInfo != null) _SheetAssetDescription(exif: exifInfo), + const SheetPeopleDetails(), const SheetLocationDetails(), // Details header _SheetTile( title: 'exif_bottom_sheet_details'.t(context: context), - titleStyle: context.textTheme.labelLarge, + titleStyle: context.textTheme.labelMedium?.copyWith( + color: context.textTheme.labelMedium?.color?.withAlpha(200), + fontWeight: FontWeight.w600, + ), ), // File info _SheetTile( title: asset.name, - titleStyle: context.textTheme.labelLarge - ?.copyWith(fontWeight: FontWeight.w600), + titleStyle: context.textTheme.labelLarge, leading: Icon( asset.isImage ? Icons.image_outlined : Icons.videocam_outlined, - size: 30, + size: 24, color: context.textTheme.labelLarge?.color, ), subtitle: _getFileInfo(asset, exifInfo), - subtitleStyle: context.textTheme.labelLarge?.copyWith( - color: context.textTheme.labelLarge?.color?.withAlpha(200), + subtitleStyle: context.textTheme.bodyMedium?.copyWith( + color: context.textTheme.bodyMedium?.color?.withAlpha(155), ), ), // Camera info if (cameraTitle != null) _SheetTile( title: cameraTitle, - titleStyle: context.textTheme.labelLarge - ?.copyWith(fontWeight: FontWeight.w600), - leading: Icon( - Icons.camera_outlined, - size: 30, - color: context.textTheme.labelLarge?.color, - ), + titleStyle: context.textTheme.labelLarge, + leading: Icon(Icons.camera_outlined, size: 24, color: context.textTheme.labelLarge?.color), subtitle: _getCameraInfoSubtitle(exifInfo), - subtitleStyle: context.textTheme.labelLarge?.copyWith( - color: context.textTheme.labelLarge?.color?.withAlpha(200), + subtitleStyle: context.textTheme.bodyMedium?.copyWith( + color: context.textTheme.bodyMedium?.color?.withAlpha(155), ), ), ], @@ -200,9 +200,11 @@ class _AssetDetailBottomSheet extends ConsumerWidget { class _SheetTile extends StatelessWidget { final String title; final Widget? leading; + final Widget? trailing; final String? subtitle; final TextStyle? titleStyle; final TextStyle? subtitleStyle; + final VoidCallback? onTap; const _SheetTile({ required this.title, @@ -210,6 +212,8 @@ class _SheetTile extends StatelessWidget { this.leading, this.subtitle, this.subtitleStyle, + this.trailing, + this.onTap, }); @override @@ -246,8 +250,85 @@ class _SheetTile extends StatelessWidget { title: titleWidget, titleAlignment: ListTileTitleAlignment.center, leading: leading, + trailing: trailing, contentPadding: leading == null ? null : const EdgeInsets.only(left: 25), subtitle: subtitleWidget, + onTap: onTap, + ); + } +} + +class _SheetAssetDescription extends ConsumerStatefulWidget { + final ExifInfo exif; + + const _SheetAssetDescription({required this.exif}); + + @override + ConsumerState<_SheetAssetDescription> createState() => _SheetAssetDescriptionState(); +} + +class _SheetAssetDescriptionState extends ConsumerState<_SheetAssetDescription> { + late TextEditingController _controller; + final _descriptionFocus = FocusNode(); + + @override + void initState() { + super.initState(); + _controller = TextEditingController(text: widget.exif.description ?? ''); + } + + Future saveDescription(String? previousDescription) async { + final newDescription = _controller.text.trim(); + + if (newDescription == previousDescription) { + _descriptionFocus.unfocus(); + return; + } + + final editAction = await ref.read(actionProvider.notifier).updateDescription(ActionSource.viewer, newDescription); + + if (!editAction.success) { + _controller.text = previousDescription ?? ''; + + ImmichToast.show( + context: context, + msg: 'exif_bottom_sheet_description_error'.t(context: context), + toastType: ToastType.error, + ); + } + + _descriptionFocus.unfocus(); + } + + @override + Widget build(BuildContext context) { + // Watch the current asset EXIF provider to get updates + final currentExifInfo = ref.watch(currentAssetExifProvider).valueOrNull; + + // Update controller text when EXIF data changes + final currentDescription = currentExifInfo?.description ?? ''; + if (_controller.text != currentDescription && !_descriptionFocus.hasFocus) { + _controller.text = currentDescription; + } + + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8), + child: TextField( + controller: _controller, + keyboardType: TextInputType.multiline, + focusNode: _descriptionFocus, + maxLines: null, // makes it grow as text is added + decoration: InputDecoration( + hintText: 'exif_bottom_sheet_description'.t(context: context), + border: InputBorder.none, + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + disabledBorder: InputBorder.none, + errorBorder: InputBorder.none, + focusedErrorBorder: InputBorder.none, + ), + onTapOutside: (_) => saveDescription(currentExifInfo?.description), + ), ); } } diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/location_details.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_location_details.widget.dart similarity index 64% rename from mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/location_details.widget.dart rename to mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_location_details.widget.dart index 2bf52bd094..ab57ea4d8b 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/location_details.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_location_details.widget.dart @@ -38,20 +38,13 @@ class _SheetLocationDetailsState extends ConsumerState { _mapController = controller; } - void _onExifChanged( - AsyncValue? previous, - AsyncValue current, - ) { + void _onExifChanged(AsyncValue? previous, AsyncValue current) { asset = ref.read(currentAssetNotifier); setState(() { exifInfo = current.valueOrNull; final hasCoordinates = exifInfo?.hasCoordinates ?? false; if (exifInfo != null && hasCoordinates) { - _mapController?.moveCamera( - CameraUpdate.newLatLng( - LatLng(exifInfo!.latitude!, exifInfo!.longitude!), - ), - ); + _mapController?.moveCamera(CameraUpdate.newLatLng(LatLng(exifInfo!.latitude!, exifInfo!.longitude!))); } }); } @@ -59,11 +52,7 @@ class _SheetLocationDetailsState extends ConsumerState { @override void initState() { super.initState(); - ref.listenManual( - currentAssetExifProvider, - _onExifChanged, - fireImmediately: true, - ); + ref.listenManual(currentAssetExifProvider, _onExifChanged, fireImmediately: true); } @override @@ -71,23 +60,16 @@ class _SheetLocationDetailsState extends ConsumerState { final hasCoordinates = exifInfo?.hasCoordinates ?? false; // Guard no lat/lng - if (!hasCoordinates || - (asset != null && asset is LocalAsset && asset!.hasRemote)) { + if (!hasCoordinates || (asset != null && asset is LocalAsset && asset!.hasRemote)) { return const SizedBox.shrink(); } - final remoteId = asset is LocalAsset - ? (asset as LocalAsset).remoteId - : (asset as RemoteAsset).id; + final remoteId = asset is LocalAsset ? (asset as LocalAsset).remoteId : (asset as RemoteAsset).id; final locationName = _getLocationName(exifInfo); - final coordinates = - "${exifInfo!.latitude!.toStringAsFixed(4)}, ${exifInfo!.longitude!.toStringAsFixed(4)}"; + final coordinates = "${exifInfo!.latitude!.toStringAsFixed(4)}, ${exifInfo!.longitude!.toStringAsFixed(4)}"; return Padding( - padding: EdgeInsets.symmetric( - vertical: 16.0, - horizontal: context.isMobile ? 16.0 : 56.0, - ), + padding: EdgeInsets.symmetric(vertical: 16.0, horizontal: context.isMobile ? 16.0 : 56.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -95,30 +77,22 @@ class _SheetLocationDetailsState extends ConsumerState { padding: const EdgeInsets.only(bottom: 16), child: Text( "exif_bottom_sheet_location".t(context: context), - style: context.textTheme.labelLarge, + style: context.textTheme.labelMedium?.copyWith( + color: context.textTheme.labelMedium?.color?.withAlpha(200), + fontWeight: FontWeight.w600, + ), ), ), - ExifMap( - exifInfo: exifInfo!, - markerId: remoteId, - onMapCreated: _onMapCreated, - ), + ExifMap(exifInfo: exifInfo!, markerId: remoteId, onMapCreated: _onMapCreated), const SizedBox(height: 15), if (locationName != null) Padding( padding: const EdgeInsets.only(bottom: 4.0), - child: Text( - locationName, - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - ), - ), + child: Text(locationName, style: context.textTheme.labelLarge), ), Text( coordinates, - style: context.textTheme.labelLarge?.copyWith( - color: context.textTheme.labelLarge?.color?.withAlpha(150), - ), + style: context.textTheme.labelMedium?.copyWith(color: context.textTheme.labelMedium?.color?.withAlpha(150)), ), ], ), diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart new file mode 100644 index 0000000000..5adaa7cc72 --- /dev/null +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart @@ -0,0 +1,175 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/people/person_edit_name_modal.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/people.provider.dart'; +import 'package:immich_mobile/providers/routes.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/services/api.service.dart'; +import 'package:immich_mobile/utils/people.utils.dart'; +import 'package:immich_mobile/utils/image_url_builder.dart'; + +class SheetPeopleDetails extends ConsumerStatefulWidget { + const SheetPeopleDetails({super.key}); + + @override + ConsumerState createState() => _SheetPeopleDetailsState(); +} + +class _SheetPeopleDetailsState extends ConsumerState { + @override + Widget build(BuildContext context) { + final asset = ref.watch(currentAssetNotifier); + if (asset is! RemoteAsset) { + return const SizedBox.shrink(); + } + + final peopleFuture = ref.watch(driftPeopleAssetProvider(asset.id)); + + Future showNameEditModal(DriftPerson person) async { + await showDialog( + context: context, + useRootNavigator: false, + builder: (BuildContext context) { + return DriftPersonNameEditForm(person: person); + }, + ); + + ref.invalidate(driftPeopleAssetProvider(asset.id)); + } + + return peopleFuture.when( + data: (people) { + return AnimatedCrossFade( + firstChild: const SizedBox.shrink(), + secondChild: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(left: 16, top: 16, bottom: 16), + child: Text( + "people".t(context: context).toUpperCase(), + style: context.textTheme.labelMedium?.copyWith( + color: context.textTheme.labelMedium?.color?.withAlpha(200), + fontWeight: FontWeight.w600, + ), + ), + ), + SizedBox( + height: 150, + child: ListView( + padding: const EdgeInsets.only(left: 16.0), + scrollDirection: Axis.horizontal, + children: [ + for (final person in people) + _PeopleAvatar( + person: person, + assetFileCreatedAt: asset.createdAt, + onTap: () { + final previousRouteData = ref.read(previousRouteDataProvider); + final previousRouteArgs = previousRouteData?.arguments; + + // Prevent circular navigation + if (previousRouteArgs is DriftPersonRouteArgs && previousRouteArgs.person.id == person.id) { + context.back(); + return; + } + context.pop(); + context.pushRoute(DriftPersonRoute(person: person)); + }, + onNameTap: () => showNameEditModal(person), + ), + ], + ), + ), + ], + ), + crossFadeState: people.isEmpty ? CrossFadeState.showFirst : CrossFadeState.showSecond, + duration: Durations.short4, + ); + }, + error: (error, stack) => Text("error_loading_people".t(context: context), style: context.textTheme.bodyMedium), + loading: () => const SizedBox.shrink(), + ); + } +} + +class _PeopleAvatar extends StatelessWidget { + final DriftPerson person; + final DateTime assetFileCreatedAt; + final VoidCallback? onTap; + final VoidCallback? onNameTap; + final double imageSize = 96; + + const _PeopleAvatar({required this.person, required this.assetFileCreatedAt, this.onTap, this.onNameTap}); + + @override + Widget build(BuildContext context) { + final headers = ApiService.getRequestHeaders(); + + return ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 96), + child: Padding( + padding: const EdgeInsets.only(right: 16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + GestureDetector( + onTap: onTap, + child: SizedBox( + height: imageSize, + child: Material( + shape: CircleBorder(side: BorderSide(color: context.primaryColor.withAlpha(50), width: 1.0)), + shadowColor: context.colorScheme.shadow, + elevation: 3, + child: CircleAvatar( + maxRadius: imageSize / 2, + backgroundImage: NetworkImage(getFaceThumbnailUrl(person.id), headers: headers), + ), + ), + ), + ), + const SizedBox(height: 4), + if (person.name.isEmpty) + GestureDetector( + onTap: () => onNameTap?.call(), + child: Text( + "add_a_name".t(context: context), + style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor), + maxLines: 2, + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.center, + ), + ) + else + Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + person.name, + textAlign: TextAlign.center, + overflow: TextOverflow.ellipsis, + style: context.textTheme.labelLarge, + maxLines: 1, + ), + if (person.birthDate != null) + Text( + formatAge(person.birthDate!, assetFileCreatedAt), + textAlign: TextAlign.center, + style: context.textTheme.bodyMedium?.copyWith( + color: context.textTheme.bodyMedium?.color?.withAlpha(175), + ), + ), + ], + ), + ], + ), + ), + ); + } +} diff --git a/mobile/lib/presentation/widgets/asset_viewer/top_app_bar.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/top_app_bar.widget.dart index c85c4390ae..450012f7fa 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/top_app_bar.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/top_app_bar.widget.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/models/timeline.model.dart'; import 'package:immich_mobile/domain/utils/event_stream.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/cast_action_button.widget.dart'; @@ -12,8 +13,10 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/unfavorite_act import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; import 'package:immich_mobile/providers/cast.provider.dart'; import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; +import 'package:immich_mobile/providers/routes.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/providers/websocket.provider.dart'; +import 'package:immich_mobile/routing/router.dart'; class ViewerTopAppBar extends ConsumerWidget implements PreferredSizeWidget { const ViewerTopAppBar({super.key}); @@ -27,54 +30,63 @@ class ViewerTopAppBar extends ConsumerWidget implements PreferredSizeWidget { final user = ref.watch(currentUserProvider); final isOwner = asset is RemoteAsset && asset.ownerId == user?.id; + final isInLockedView = ref.watch(inLockedViewProvider); - final isShowingSheet = ref - .watch(assetViewerProvider.select((state) => state.showingBottomSheet)); - int opacity = ref.watch( - assetViewerProvider.select((state) => state.backgroundOpacity), - ); - final showControls = - ref.watch(assetViewerProvider.select((s) => s.showingControls)); + final previousRouteName = ref.watch(previousRouteNameProvider); + final showViewInTimelineButton = previousRouteName != TabShellRoute.name && previousRouteName != null; + + final isShowingSheet = ref.watch(assetViewerProvider.select((state) => state.showingBottomSheet)); + int opacity = ref.watch(assetViewerProvider.select((state) => state.backgroundOpacity)); + final showControls = ref.watch(assetViewerProvider.select((s) => s.showingControls)); if (!showControls) { opacity = 0; } - final isCasting = ref.watch( - castProvider.select((c) => c.isCasting), - ); - final websocketConnected = - ref.watch(websocketProvider.select((c) => c.isConnected)); + final isCasting = ref.watch(castProvider.select((c) => c.isCasting)); + final websocketConnected = ref.watch(websocketProvider.select((c) => c.isConnected)); final actions = [ - if (isCasting || (asset.hasRemote && websocketConnected)) - const CastActionButton( - menuItem: true, + if (isCasting || (asset.hasRemote && websocketConnected)) const CastActionButton(menuItem: true), + if (showViewInTimelineButton) + IconButton( + onPressed: () async { + await context.maybePop(); + await context.navigateTo(const TabShellRoute(children: [MainTimelineRoute()])); + EventStream.shared.emit(ScrollToDateEvent(asset.createdAt)); + }, + icon: const Icon(Icons.image_search), + tooltip: 'view_in_timeline', ), if (asset.hasRemote && isOwner && !asset.isFavorite) const FavoriteActionButton(source: ActionSource.viewer, menuItem: true), if (asset.hasRemote && isOwner && asset.isFavorite) - const UnFavoriteActionButton( - source: ActionSource.viewer, - menuItem: true, - ), + const UnFavoriteActionButton(source: ActionSource.viewer, menuItem: true), if (asset.isMotionPhoto) const MotionPhotoActionButton(menuItem: true), const _KebabMenu(), ]; + final lockedViewActions = [ + if (isCasting || (asset.hasRemote && websocketConnected)) const CastActionButton(menuItem: true), + const _KebabMenu(), + ]; + return IgnorePointer( ignoring: opacity < 255, child: AnimatedOpacity( opacity: opacity / 255, duration: Durations.short2, child: AppBar( - backgroundColor: - isShowingSheet ? Colors.transparent : Colors.black.withAlpha(125), + backgroundColor: isShowingSheet ? Colors.transparent : Colors.black.withAlpha(125), leading: const _AppBarBackButton(), iconTheme: const IconThemeData(size: 22, color: Colors.white), actionsIconTheme: const IconThemeData(size: 22, color: Colors.white), shape: const Border(), - actions: isShowingSheet ? null : actions, + actions: isShowingSheet + ? null + : isInLockedView + ? lockedViewActions + : actions, ), ), ); @@ -103,12 +115,9 @@ class _AppBarBackButton extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final isShowingSheet = ref - .watch(assetViewerProvider.select((state) => state.showingBottomSheet)); - final backgroundColor = - isShowingSheet && !context.isDarkTheme ? Colors.white : Colors.black; - final foregroundColor = - isShowingSheet && !context.isDarkTheme ? Colors.black : Colors.white; + final isShowingSheet = ref.watch(assetViewerProvider.select((state) => state.showingBottomSheet)); + final backgroundColor = isShowingSheet && !context.isDarkTheme ? Colors.white : Colors.black; + final foregroundColor = isShowingSheet && !context.isDarkTheme ? Colors.black : Colors.white; return Padding( padding: const EdgeInsets.only(left: 12.0), diff --git a/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart index 17880da3e7..32510c2ca5 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/video_viewer.widget.dart @@ -27,6 +27,23 @@ import 'package:logging/logging.dart'; import 'package:native_video_player/native_video_player.dart'; import 'package:wakelock_plus/wakelock_plus.dart'; +bool _isCurrentAsset(BaseAsset asset, BaseAsset? currentAsset) { + if (asset is RemoteAsset) { + return switch (currentAsset) { + RemoteAsset remoteAsset => remoteAsset.id == asset.id, + LocalAsset localAsset => localAsset.remoteId == asset.id, + _ => false, + }; + } else if (asset is LocalAsset) { + return switch (currentAsset) { + RemoteAsset remoteAsset => remoteAsset.localId == asset.id, + LocalAsset localAsset => localAsset.id == asset.id, + _ => false, + }; + } + return false; +} + class NativeVideoViewer extends HookConsumerWidget { final BaseAsset asset; final bool showControls; @@ -56,7 +73,7 @@ class NativeVideoViewer extends HookConsumerWidget { // If the swipe is completed, `isCurrent` will be true for video B after a delay. // If the swipe is canceled, `currentAsset` will not have changed and video A will continue to play. final currentAsset = useState(ref.read(currentAssetNotifier)); - final isCurrent = currentAsset.value == asset; + final isCurrent = _isCurrentAsset(asset, currentAsset.value); // Used to show the placeholder during hero animations for remote videos to avoid a stutter final isVisible = useState(Platform.isIOS && asset.hasLocal); @@ -72,18 +89,13 @@ class NativeVideoViewer extends HookConsumerWidget { try { if (asset.hasLocal && asset.livePhotoVideoId == null) { - final id = asset is LocalAsset - ? (asset as LocalAsset).id - : (asset as RemoteAsset).localId!; + final id = asset is LocalAsset ? (asset as LocalAsset).id : (asset as RemoteAsset).localId!; final file = await const StorageRepository().getFileForAsset(id); if (file == null) { throw Exception('No file found for the video'); } - final source = await VideoSource.init( - path: file.path, - type: VideoSourceType.file, - ); + final source = await VideoSource.init(path: file.path, type: VideoSourceType.file); return source; } @@ -91,10 +103,8 @@ class NativeVideoViewer extends HookConsumerWidget { // Use a network URL for the video player controller final serverEndpoint = Store.get(StoreKey.serverEndpoint); - final isOriginalVideo = - ref.read(settingsProvider).get(Setting.loadOriginalVideo); - final String postfixUrl = - isOriginalVideo ? 'original' : 'video/playback'; + final isOriginalVideo = ref.read(settingsProvider).get(Setting.loadOriginalVideo); + final String postfixUrl = isOriginalVideo ? 'original' : 'video/playback'; final String videoUrl = asset.livePhotoVideoId != null ? '$serverEndpoint/assets/${asset.livePhotoVideoId}/$postfixUrl' : '$serverEndpoint/assets/$remoteId/$postfixUrl'; @@ -106,32 +116,24 @@ class NativeVideoViewer extends HookConsumerWidget { ); return source; } catch (error) { - log.severe( - 'Error creating video source for asset ${asset.name}: $error', - ); + log.severe('Error creating video source for asset ${asset.name}: $error'); return null; } } final videoSource = useMemoized>(() => createSource()); final aspectRatio = useState(null); - useMemoized( - () async { - if (!context.mounted || aspectRatio.value != null) { - return null; - } + useMemoized(() async { + if (!context.mounted || aspectRatio.value != null) { + return null; + } - try { - aspectRatio.value = - await ref.read(assetServiceProvider).getAspectRatio(asset); - } catch (error) { - log.severe( - 'Error getting aspect ratio for asset ${asset.name}: $error', - ); - } - }, - [asset.heroTag], - ); + try { + aspectRatio.value = await ref.read(assetServiceProvider).getAspectRatio(asset); + } catch (error) { + log.severe('Error getting aspect ratio for asset ${asset.name}: $error'); + } + }, [asset.heroTag]); void checkIfBuffering() { if (!context.mounted) { @@ -139,11 +141,11 @@ class NativeVideoViewer extends HookConsumerWidget { } final videoPlayback = ref.read(videoPlaybackValueProvider); - if ((isBuffering.value || - videoPlayback.state == VideoPlaybackState.initializing) && + if ((isBuffering.value || videoPlayback.state == VideoPlaybackState.initializing) && videoPlayback.state != VideoPlaybackState.buffering) { - ref.read(videoPlaybackValueProvider.notifier).value = - videoPlayback.copyWith(state: VideoPlaybackState.buffering); + ref.read(videoPlaybackValueProvider.notifier).value = videoPlayback.copyWith( + state: VideoPlaybackState.buffering, + ); } } @@ -199,8 +201,7 @@ class NativeVideoViewer extends HookConsumerWidget { return; } - final videoPlayback = - VideoPlaybackValue.fromNativeController(videoController); + final videoPlayback = VideoPlaybackValue.fromNativeController(videoController); ref.read(videoPlaybackValueProvider.notifier).value = videoPlayback; if (ref.read(assetViewerProvider.select((s) => s.showingBottomSheet))) { @@ -221,8 +222,7 @@ class NativeVideoViewer extends HookConsumerWidget { return; } - final videoPlayback = - VideoPlaybackValue.fromNativeController(videoController); + final videoPlayback = VideoPlaybackValue.fromNativeController(videoController); if (videoPlayback.state == VideoPlaybackState.playing) { // Sync with the controls playing WakelockPlus.enable(); @@ -231,8 +231,7 @@ class NativeVideoViewer extends HookConsumerWidget { WakelockPlus.disable(); } - ref.read(videoPlaybackValueProvider.notifier).status = - videoPlayback.state; + ref.read(videoPlaybackValueProvider.notifier).status = videoPlayback.state; } void onPlaybackPositionChanged() { @@ -251,8 +250,7 @@ class NativeVideoViewer extends HookConsumerWidget { return; } - ref.read(videoPlaybackValueProvider.notifier).position = - Duration(seconds: playbackInfo.position); + ref.read(videoPlaybackValueProvider.notifier).position = Duration(seconds: playbackInfo.position); // Check if the video is buffering if (playbackInfo.status == PlaybackStatus.playing) { @@ -276,10 +274,8 @@ class NativeVideoViewer extends HookConsumerWidget { } void removeListeners(NativeVideoPlayerController controller) { - controller.onPlaybackPositionChanged - .removeListener(onPlaybackPositionChanged); - controller.onPlaybackStatusChanged - .removeListener(onPlaybackStatusChanged); + controller.onPlaybackPositionChanged.removeListener(onPlaybackPositionChanged); + controller.onPlaybackStatusChanged.removeListener(onPlaybackStatusChanged); controller.onPlaybackReady.removeListener(onPlaybackReady); controller.onPlaybackEnded.removeListener(onPlaybackEnded); } @@ -304,9 +300,7 @@ class NativeVideoViewer extends HookConsumerWidget { nc.loadVideoSource(source).catchError((error) { log.severe('Error loading video source: $error'); }); - final loopVideo = ref - .read(appSettingsServiceProvider) - .getSetting(AppSettingsEnum.loopVideo); + final loopVideo = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.loopVideo); nc.setLoop(!asset.isMotionPhoto && loopVideo); controller.value = nc; @@ -339,48 +333,42 @@ class NativeVideoViewer extends HookConsumerWidget { // This delay seems like a hacky way to resolve underlying bugs in video // playback, but other resolutions failed thus far Timer( - Platform.isIOS - ? Duration(milliseconds: 300 * playbackDelayFactor) - : imageToVideo - ? Duration(milliseconds: 200 * playbackDelayFactor) - : Duration(milliseconds: 400 * playbackDelayFactor), () { - if (!context.mounted) { - return; - } - - currentAsset.value = value; - if (currentAsset.value == asset) { - onPlaybackReady(); - } - }); - }); - - useEffect( - () { - // If opening a remote video from a hero animation, delay visibility to avoid a stutter - final timer = isVisible.value - ? null - : Timer( - const Duration(milliseconds: 300), - () => isVisible.value = true, - ); - - return () { - timer?.cancel(); - final playerController = controller.value; - if (playerController == null) { + Platform.isIOS + ? Duration(milliseconds: 300 * playbackDelayFactor) + : imageToVideo + ? Duration(milliseconds: 200 * playbackDelayFactor) + : Duration(milliseconds: 400 * playbackDelayFactor), + () { + if (!context.mounted) { return; } - removeListeners(playerController); - playerController.stop().catchError((error) { - log.fine('Error stopping video: $error'); - }); - WakelockPlus.disable(); - }; - }, - const [], - ); + currentAsset.value = value; + if (currentAsset.value == asset) { + onPlaybackReady(); + } + }, + ); + }); + + useEffect(() { + // If opening a remote video from a hero animation, delay visibility to avoid a stutter + final timer = isVisible.value ? null : Timer(const Duration(milliseconds: 300), () => isVisible.value = true); + + return () { + timer?.cancel(); + final playerController = controller.value; + if (playerController == null) { + return; + } + removeListeners(playerController); + playerController.stop().catchError((error) { + log.fine('Error stopping video: $error'); + }); + + WakelockPlus.disable(); + }; + }, const []); useOnAppLifecycleStateChange((_, state) async { if (state == AppLifecycleState.resumed && shouldPlayOnForeground.value) { @@ -410,12 +398,7 @@ class NativeVideoViewer extends HookConsumerWidget { child: AspectRatio( key: ValueKey(asset), aspectRatio: aspectRatio.value!, - child: isCurrent - ? NativeVideoPlayerView( - key: ValueKey(asset), - onViewReady: initController, - ) - : null, + child: isCurrent ? NativeVideoPlayerView(key: ValueKey(asset), onViewReady: initController) : null, ), ), ), diff --git a/mobile/lib/presentation/widgets/asset_viewer/video_viewer_controls.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/video_viewer_controls.widget.dart index 883169ae83..c1324b8ac0 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/video_viewer_controls.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/video_viewer_controls.widget.dart @@ -13,47 +13,33 @@ import 'package:immich_mobile/widgets/common/delayed_loading_indicator.dart'; class VideoViewerControls extends HookConsumerWidget { final Duration hideTimerDuration; - const VideoViewerControls({ - super.key, - this.hideTimerDuration = const Duration(seconds: 5), - }); + const VideoViewerControls({super.key, this.hideTimerDuration = const Duration(seconds: 5)}); @override Widget build(BuildContext context, WidgetRef ref) { - final assetIsVideo = ref.watch( - currentAssetNotifier.select((asset) => asset != null && asset.isVideo), - ); - bool showControls = - ref.watch(assetViewerProvider.select((s) => s.showingControls)); - final showBottomSheet = - ref.watch(assetViewerProvider.select((s) => s.showingBottomSheet)); + final assetIsVideo = ref.watch(currentAssetNotifier.select((asset) => asset != null && asset.isVideo)); + bool showControls = ref.watch(assetViewerProvider.select((s) => s.showingControls)); + final showBottomSheet = ref.watch(assetViewerProvider.select((s) => s.showingBottomSheet)); if (showBottomSheet) { showControls = false; } - final VideoPlaybackState state = - ref.watch(videoPlaybackValueProvider.select((value) => value.state)); + final VideoPlaybackState state = ref.watch(videoPlaybackValueProvider.select((value) => value.state)); final cast = ref.watch(castProvider); // A timer to hide the controls - final hideTimer = useTimer( - hideTimerDuration, - () { - if (!context.mounted) { - return; - } - final state = ref.read(videoPlaybackValueProvider).state; + final hideTimer = useTimer(hideTimerDuration, () { + if (!context.mounted) { + return; + } + final state = ref.read(videoPlaybackValueProvider).state; - // Do not hide on paused - if (state != VideoPlaybackState.paused && - state != VideoPlaybackState.completed && - assetIsVideo) { - ref.read(assetViewerProvider.notifier).setControls(false); - } - }, - ); - final showBuffering = - state == VideoPlaybackState.buffering && !cast.isCasting; + // Do not hide on paused + if (state != VideoPlaybackState.paused && state != VideoPlaybackState.completed && assetIsVideo) { + ref.read(assetViewerProvider.notifier).setControls(false); + } + }); + final showBuffering = state == VideoPlaybackState.buffering && !cast.isCasting; /// Shows the controls and starts the timer to hide them void showControlsAndStartHideTimer() { @@ -62,8 +48,7 @@ class VideoViewerControls extends HookConsumerWidget { } // When we change position, show or hide timer - ref.listen(videoPlayerControlsProvider.select((v) => v.position), - (previous, next) { + ref.listen(videoPlayerControlsProvider.select((v) => v.position), (previous, next) { showControlsAndStartHideTimer(); }); @@ -104,21 +89,16 @@ class VideoViewerControls extends HookConsumerWidget { child: Stack( children: [ if (showBuffering) - const Center( - child: DelayedLoadingIndicator( - fadeInDuration: Duration(milliseconds: 400), - ), - ) + const Center(child: DelayedLoadingIndicator(fadeInDuration: Duration(milliseconds: 400))) else GestureDetector( - onTap: () => - ref.read(assetViewerProvider.notifier).setControls(false), + onTap: () => ref.read(assetViewerProvider.notifier).setControls(false), child: CenterPlayButton( backgroundColor: Colors.black54, iconColor: Colors.white, isFinished: state == VideoPlaybackState.completed, - isPlaying: state == VideoPlaybackState.playing || - (cast.isCasting && cast.castState == CastState.playing), + isPlaying: + state == VideoPlaybackState.playing || (cast.isCasting && cast.castState == CastState.playing), show: assetIsVideo && showControls, onPressed: togglePlay, ), diff --git a/mobile/lib/presentation/widgets/backup/backup_toggle_button.widget.dart b/mobile/lib/presentation/widgets/backup/backup_toggle_button.widget.dart new file mode 100644 index 0000000000..a74c169224 --- /dev/null +++ b/mobile/lib/presentation/widgets/backup/backup_toggle_button.widget.dart @@ -0,0 +1,192 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/theme_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/providers/app_settings.provider.dart'; +import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; + +class BackupToggleButton extends ConsumerStatefulWidget { + final VoidCallback onStart; + final VoidCallback onStop; + + const BackupToggleButton({super.key, required this.onStart, required this.onStop}); + + @override + ConsumerState createState() => BackupToggleButtonState(); +} + +class BackupToggleButtonState extends ConsumerState with SingleTickerProviderStateMixin { + late AnimationController _animationController; + late Animation _gradientAnimation; + bool _isEnabled = false; + + @override + void initState() { + super.initState(); + _animationController = AnimationController(duration: const Duration(seconds: 8), vsync: this); + + _gradientAnimation = Tween( + begin: 0, + end: 1, + ).animate(CurvedAnimation(parent: _animationController, curve: Curves.easeInOut)); + + _isEnabled = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup); + } + + @override + void dispose() { + _animationController.dispose(); + super.dispose(); + } + + Future _onToggle(bool value) async { + await ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.enableBackup, value); + + setState(() { + _isEnabled = value; + }); + + if (value) { + widget.onStart.call(); + } else { + widget.onStop.call(); + } + } + + @override + Widget build(BuildContext context) { + final enqueueCount = ref.watch(driftBackupProvider.select((state) => state.enqueueCount)); + + final enqueueTotalCount = ref.watch(driftBackupProvider.select((state) => state.enqueueTotalCount)); + + final isCanceling = ref.watch(driftBackupProvider.select((state) => state.isCanceling)); + + final uploadTasks = ref.watch(driftBackupProvider.select((state) => state.uploadItems)); + + final isUploading = uploadTasks.isNotEmpty; + + return AnimatedBuilder( + animation: _animationController, + builder: (context, child) { + final gradientColors = [ + Color.lerp( + context.primaryColor.withValues(alpha: 0.5), + context.primaryColor.withValues(alpha: 0.3), + _gradientAnimation.value, + )!, + Color.lerp( + context.primaryColor.withValues(alpha: 0.2), + context.primaryColor.withValues(alpha: 0.4), + _gradientAnimation.value, + )!, + Color.lerp( + context.primaryColor.withValues(alpha: 0.3), + context.primaryColor.withValues(alpha: 0.5), + _gradientAnimation.value, + )!, + ]; + + return Container( + margin: const EdgeInsets.symmetric(horizontal: 4, vertical: 4), + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(20)), + gradient: LinearGradient( + colors: gradientColors, + stops: const [0.0, 0.5, 1.0], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + boxShadow: [ + BoxShadow(color: context.primaryColor.withValues(alpha: 0.1), blurRadius: 12, offset: const Offset(0, 2)), + ], + ), + child: Container( + margin: const EdgeInsets.all(1.5), + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(18.5)), + color: context.colorScheme.surfaceContainerLow, + ), + child: Material( + color: context.colorScheme.surfaceContainerLow, + borderRadius: const BorderRadius.all(Radius.circular(20.5)), + child: InkWell( + borderRadius: const BorderRadius.all(Radius.circular(20.5)), + onTap: () => isCanceling ? null : _onToggle(!_isEnabled), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16), + child: Row( + children: [ + Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + shape: BoxShape.circle, + gradient: LinearGradient( + colors: [ + context.primaryColor.withValues(alpha: 0.2), + context.primaryColor.withValues(alpha: 0.1), + ], + ), + ), + child: isUploading + ? const SizedBox(width: 24, height: 24, child: CircularProgressIndicator(strokeWidth: 2)) + : Icon(Icons.cloud_upload_outlined, color: context.primaryColor, size: 24), + ), + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + "enable_backup".t(context: context), + style: context.textTheme.titleMedium?.copyWith( + fontWeight: FontWeight.w600, + color: context.primaryColor, + ), + ), + ], + ), + if (enqueueCount != enqueueTotalCount) + Text( + "queue_status".t( + context: context, + args: {'count': enqueueCount.toString(), 'total': enqueueTotalCount.toString()}, + ), + style: context.textTheme.labelLarge?.copyWith( + color: context.colorScheme.onSurfaceSecondary, + ), + ), + if (isCanceling) + Row( + children: [ + Text("canceling".t(), style: context.textTheme.labelLarge), + const SizedBox(width: 4), + SizedBox( + width: 18, + height: 18, + child: CircularProgressIndicator( + strokeWidth: 2, + backgroundColor: context.colorScheme.onSurface.withValues(alpha: 0.2), + ), + ), + ], + ), + ], + ), + ), + Switch.adaptive(value: _isEnabled, onChanged: (value) => isCanceling ? null : _onToggle(value)), + ], + ), + ), + ), + ), + ), + ); + }, + ); + } +} diff --git a/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart index a3d24ec8ee..45c602935d 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; -import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/download_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart'; @@ -24,9 +24,7 @@ class ArchiveBottomSheet extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final multiselect = ref.watch(multiSelectProvider); - final isTrashEnable = ref.watch( - serverInfoProvider.select((state) => state.serverFeatures.trash), - ); + final isTrashEnable = ref.watch(serverInfoProvider.select((state) => state.serverFeatures.trash)); return BaseBottomSheet( initialChildSize: 0.25, @@ -38,22 +36,18 @@ class ArchiveBottomSheet extends ConsumerWidget { const ShareLinkActionButton(source: ActionSource.timeline), const UnArchiveActionButton(source: ActionSource.timeline), const FavoriteActionButton(source: ActionSource.timeline), - const DownloadActionButton(), + const DownloadActionButton(source: ActionSource.timeline), isTrashEnable ? const TrashActionButton(source: ActionSource.timeline) - : const DeletePermanentActionButton( - source: ActionSource.timeline, - ), - const EditDateTimeActionButton(), + : const DeletePermanentActionButton(source: ActionSource.timeline), + const EditDateTimeActionButton(source: ActionSource.timeline), const EditLocationActionButton(source: ActionSource.timeline), - const MoveToLockFolderActionButton( - source: ActionSource.timeline, - ), + const MoveToLockFolderActionButton(source: ActionSource.timeline), const StackActionButton(source: ActionSource.timeline), ], if (multiselect.hasLocal) ...[ const DeleteLocalActionButton(source: ActionSource.timeline), - const UploadActionButton(), + const UploadActionButton(source: ActionSource.timeline), ], ], ); diff --git a/mobile/lib/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart index 2db8ae2b4c..a2c88d9fd7 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart @@ -14,6 +14,7 @@ class BaseBottomSheet extends ConsumerStatefulWidget { final bool expand; final bool shouldCloseOnMinExtent; final bool resizeOnScroll; + final Color? backgroundColor; const BaseBottomSheet({ super.key, @@ -26,15 +27,14 @@ class BaseBottomSheet extends ConsumerStatefulWidget { this.expand = true, this.shouldCloseOnMinExtent = true, this.resizeOnScroll = true, + this.backgroundColor, }); @override - ConsumerState createState() => - _BaseDraggableScrollableSheetState(); + ConsumerState createState() => _BaseDraggableScrollableSheetState(); } -class _BaseDraggableScrollableSheetState - extends ConsumerState { +class _BaseDraggableScrollableSheetState extends ConsumerState { late DraggableScrollableController _controller; @override @@ -69,14 +69,11 @@ class _BaseDraggableScrollableSheetState shouldCloseOnMinExtent: widget.shouldCloseOnMinExtent, builder: (BuildContext context, ScrollController scrollController) { return Card( - color: context.colorScheme.surfaceContainerHigh, - surfaceTintColor: context.colorScheme.surfaceContainerHigh, + color: widget.backgroundColor ?? context.colorScheme.surfaceContainerHigh, borderOnForeground: false, clipBehavior: Clip.antiAlias, elevation: 6.0, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical(top: Radius.circular(18)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(18))), margin: const EdgeInsets.symmetric(horizontal: 0), child: CustomScrollView( controller: scrollController, @@ -90,11 +87,7 @@ class _BaseDraggableScrollableSheetState if (widget.actions.isNotEmpty) SizedBox( height: 115, - child: ListView( - shrinkWrap: true, - scrollDirection: Axis.horizontal, - children: widget.actions, - ), + child: ListView(shrinkWrap: true, scrollDirection: Axis.horizontal, children: widget.actions), ), if (widget.actions.isNotEmpty) ...[ const Divider(indent: 16, endIndent: 16), diff --git a/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart index eefe19194c..3fb499f2a1 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/favorite_bottom_sheet.widget.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart'; -import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/download_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart'; @@ -24,9 +24,7 @@ class FavoriteBottomSheet extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final multiselect = ref.watch(multiSelectProvider); - final isTrashEnable = ref.watch( - serverInfoProvider.select((state) => state.serverFeatures.trash), - ); + final isTrashEnable = ref.watch(serverInfoProvider.select((state) => state.serverFeatures.trash)); return BaseBottomSheet( initialChildSize: 0.25, @@ -38,22 +36,18 @@ class FavoriteBottomSheet extends ConsumerWidget { const ShareLinkActionButton(source: ActionSource.timeline), const UnFavoriteActionButton(source: ActionSource.timeline), const ArchiveActionButton(source: ActionSource.timeline), - const DownloadActionButton(), + const DownloadActionButton(source: ActionSource.timeline), isTrashEnable ? const TrashActionButton(source: ActionSource.timeline) - : const DeletePermanentActionButton( - source: ActionSource.timeline, - ), - const EditDateTimeActionButton(), + : const DeletePermanentActionButton(source: ActionSource.timeline), + const EditDateTimeActionButton(source: ActionSource.timeline), const EditLocationActionButton(source: ActionSource.timeline), - const MoveToLockFolderActionButton( - source: ActionSource.timeline, - ), + const MoveToLockFolderActionButton(source: ActionSource.timeline), const StackActionButton(source: ActionSource.timeline), ], if (multiselect.hasLocal) ...[ const DeleteLocalActionButton(source: ActionSource.timeline), - const UploadActionButton(), + const UploadActionButton(source: ActionSource.timeline), ], ], ); diff --git a/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart index f9a9dd3203..70b2fb00b0 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/general_bottom_sheet.widget.dart @@ -1,8 +1,12 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/domain/models/album/album.model.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/download_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart'; @@ -14,9 +18,12 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/share_link_act import 'package:immich_mobile/presentation/widgets/action_buttons/stack_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/trash_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/album/album_selector.widget.dart'; import 'package:immich_mobile/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart'; +import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; class GeneralBottomSheet extends ConsumerWidget { const GeneralBottomSheet({super.key}); @@ -24,13 +31,36 @@ class GeneralBottomSheet extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final multiselect = ref.watch(multiSelectProvider); - final isTrashEnable = ref.watch( - serverInfoProvider.select((state) => state.serverFeatures.trash), - ); + final isTrashEnable = ref.watch(serverInfoProvider.select((state) => state.serverFeatures.trash)); + + Future addAssetsToAlbum(RemoteAlbum album) async { + final selectedAssets = multiselect.selectedAssets; + if (selectedAssets.isEmpty) { + return; + } + + final addedCount = await ref + .read(remoteAlbumProvider.notifier) + .addAssets(album.id, selectedAssets.map((e) => (e as RemoteAsset).id).toList()); + + if (addedCount != selectedAssets.length) { + ImmichToast.show( + context: context, + msg: 'add_to_album_bottom_sheet_already_exists'.tr(namedArgs: {"album": album.name}), + ); + } else { + ImmichToast.show( + context: context, + msg: 'add_to_album_bottom_sheet_added'.tr(namedArgs: {"album": album.name}), + ); + } + + ref.read(multiSelectProvider.notifier).reset(); + } return BaseBottomSheet( - initialChildSize: 0.25, - maxChildSize: 0.4, + initialChildSize: 0.45, + maxChildSize: 0.85, shouldCloseOnMinExtent: false, actions: [ const ShareActionButton(source: ActionSource.timeline), @@ -38,24 +68,28 @@ class GeneralBottomSheet extends ConsumerWidget { const ShareLinkActionButton(source: ActionSource.timeline), const ArchiveActionButton(source: ActionSource.timeline), const FavoriteActionButton(source: ActionSource.timeline), - const DownloadActionButton(), + const DownloadActionButton(source: ActionSource.timeline), isTrashEnable ? const TrashActionButton(source: ActionSource.timeline) - : const DeletePermanentActionButton( - source: ActionSource.timeline, - ), - const EditDateTimeActionButton(), + : const DeletePermanentActionButton(source: ActionSource.timeline), + const DeleteActionButton(source: ActionSource.timeline), + if (multiselect.hasLocal || multiselect.hasMerged) ...[ + const DeleteLocalActionButton(source: ActionSource.timeline), + ], + const EditDateTimeActionButton(source: ActionSource.timeline), const EditLocationActionButton(source: ActionSource.timeline), - const MoveToLockFolderActionButton( - source: ActionSource.timeline, - ), + const MoveToLockFolderActionButton(source: ActionSource.timeline), const StackActionButton(source: ActionSource.timeline), ], if (multiselect.hasLocal) ...[ const DeleteLocalActionButton(source: ActionSource.timeline), - const UploadActionButton(), + const UploadActionButton(source: ActionSource.timeline), ], ], + slivers: [ + const AddToAlbumHeader(), + AlbumSelector(onAlbumSelected: addAssetsToAlbum), + ], ); } } diff --git a/mobile/lib/presentation/widgets/bottom_sheet/local_album_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/local_album_bottom_sheet.widget.dart index 2ad0fe7485..b1e87dfaea 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/local_album_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/local_album_bottom_sheet.widget.dart @@ -18,7 +18,7 @@ class LocalAlbumBottomSheet extends ConsumerWidget { actions: [ ShareActionButton(source: ActionSource.timeline), DeleteLocalActionButton(source: ActionSource.timeline), - UploadActionButton(), + UploadActionButton(source: ActionSource.timeline), ], ); } diff --git a/mobile/lib/presentation/widgets/bottom_sheet/locked_folder_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/locked_folder_bottom_sheet.widget.dart index ef71f3a3a3..a644e6a035 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/locked_folder_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/locked_folder_bottom_sheet.widget.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; -import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/download_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/remove_from_lock_folder_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/share_action_button.widget.dart'; @@ -18,7 +18,7 @@ class LockedFolderBottomSheet extends ConsumerWidget { shouldCloseOnMinExtent: false, actions: [ ShareActionButton(source: ActionSource.timeline), - DownloadActionButton(), + DownloadActionButton(source: ActionSource.timeline), DeletePermanentActionButton(source: ActionSource.timeline), RemoveFromLockFolderActionButton(source: ActionSource.timeline), ], diff --git a/mobile/lib/presentation/widgets/bottom_sheet/partner_detail_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/partner_detail_bottom_sheet.widget.dart index 1cdf6f28d6..5e4dae34bc 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/partner_detail_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/partner_detail_bottom_sheet.widget.dart @@ -16,7 +16,7 @@ class PartnerDetailBottomSheet extends ConsumerWidget { shouldCloseOnMinExtent: false, actions: [ ShareActionButton(source: ActionSource.timeline), - DownloadActionButton(), + DownloadActionButton(source: ActionSource.timeline), ], ); } diff --git a/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart index b5fecdf7a4..9f41a0c681 100644 --- a/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/bottom_sheet/remote_album_bottom_sheet.widget.dart @@ -3,7 +3,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/models/album/album.model.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart'; -import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/download_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/edit_date_time_action_button.widget.dart'; @@ -27,9 +27,7 @@ class RemoteAlbumBottomSheet extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final multiselect = ref.watch(multiSelectProvider); - final isTrashEnable = ref.watch( - serverInfoProvider.select((state) => state.serverFeatures.trash), - ); + final isTrashEnable = ref.watch(serverInfoProvider.select((state) => state.serverFeatures.trash)); return BaseBottomSheet( initialChildSize: 0.25, @@ -41,27 +39,20 @@ class RemoteAlbumBottomSheet extends ConsumerWidget { const ShareLinkActionButton(source: ActionSource.timeline), const ArchiveActionButton(source: ActionSource.timeline), const FavoriteActionButton(source: ActionSource.timeline), - const DownloadActionButton(), + const DownloadActionButton(source: ActionSource.timeline), isTrashEnable ? const TrashActionButton(source: ActionSource.timeline) - : const DeletePermanentActionButton( - source: ActionSource.timeline, - ), - const EditDateTimeActionButton(), + : const DeletePermanentActionButton(source: ActionSource.timeline), + const EditDateTimeActionButton(source: ActionSource.timeline), const EditLocationActionButton(source: ActionSource.timeline), - const MoveToLockFolderActionButton( - source: ActionSource.timeline, - ), + const MoveToLockFolderActionButton(source: ActionSource.timeline), const StackActionButton(source: ActionSource.timeline), ], if (multiselect.hasLocal) ...[ const DeleteLocalActionButton(source: ActionSource.timeline), - const UploadActionButton(), + const UploadActionButton(source: ActionSource.timeline), ], - RemoveFromAlbumActionButton( - source: ActionSource.timeline, - albumId: album.id, - ), + RemoveFromAlbumActionButton(source: ActionSource.timeline, albumId: album.id), ], ); } diff --git a/mobile/lib/presentation/widgets/bottom_sheet/trash_bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/bottom_sheet/trash_bottom_sheet.widget.dart new file mode 100644 index 0000000000..9f8216c4ed --- /dev/null +++ b/mobile/lib/presentation/widgets/bottom_sheet/trash_bottom_sheet.widget.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/delete_trash_action_button.widget.dart'; +import 'package:immich_mobile/presentation/widgets/action_buttons/restore_trash_action_button.widget.dart'; + +class TrashBottomBar extends ConsumerWidget { + const TrashBottomBar({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return SafeArea( + child: Align( + alignment: Alignment.bottomCenter, + child: SizedBox( + height: 64, + child: Container( + color: context.themeData.canvasColor, + child: const Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + DeleteTrashActionButton(source: ActionSource.timeline), + RestoreTrashActionButton(source: ActionSource.timeline), + ], + ), + ), + ), + ), + ); + } +} diff --git a/mobile/lib/presentation/widgets/images/image_provider.dart b/mobile/lib/presentation/widgets/images/image_provider.dart index 970bb581cf..d94480b434 100644 --- a/mobile/lib/presentation/widgets/images/image_provider.dart +++ b/mobile/lib/presentation/widgets/images/image_provider.dart @@ -5,20 +5,12 @@ import 'package:immich_mobile/domain/services/setting.service.dart'; import 'package:immich_mobile/presentation/widgets/images/local_image_provider.dart'; import 'package:immich_mobile/presentation/widgets/images/remote_image_provider.dart'; -ImageProvider getFullImageProvider( - BaseAsset asset, { - Size size = const Size(1080, 1920), -}) { +ImageProvider getFullImageProvider(BaseAsset asset, {Size size = const Size(1080, 1920)}) { // Create new provider and cache it final ImageProvider provider; if (_shouldUseLocalAsset(asset)) { final id = asset is LocalAsset ? asset.id : (asset as RemoteAsset).localId!; - provider = LocalFullImageProvider( - id: id, - name: asset.name, - size: size, - type: asset.type, - ); + provider = LocalFullImageProvider(id: id, name: asset.name, size: size, type: asset.type); } else { final String assetId; if (asset is LocalAsset && asset.hasRemote) { @@ -34,15 +26,8 @@ ImageProvider getFullImageProvider( return provider; } -ImageProvider getThumbnailImageProvider({ - BaseAsset? asset, - String? remoteId, - Size size = const Size.square(256), -}) { - assert( - asset != null || remoteId != null, - 'Either asset or remoteId must be provided', - ); +ImageProvider getThumbnailImageProvider({BaseAsset? asset, String? remoteId, Size size = const Size.square(256)}) { + assert(asset != null || remoteId != null, 'Either asset or remoteId must be provided'); if (remoteId != null) { return RemoteThumbProvider(assetId: remoteId); @@ -50,12 +35,7 @@ ImageProvider getThumbnailImageProvider({ if (_shouldUseLocalAsset(asset!)) { final id = asset is LocalAsset ? asset.id : (asset as RemoteAsset).localId!; - return LocalThumbProvider( - id: id, - updatedAt: asset.updatedAt, - name: asset.name, - size: size, - ); + return LocalThumbProvider(id: id, updatedAt: asset.updatedAt, name: asset.name, size: size); } final String assetId; @@ -71,5 +51,4 @@ ImageProvider getThumbnailImageProvider({ } bool _shouldUseLocalAsset(BaseAsset asset) => - asset.hasLocal && - (!asset.hasRemote || !AppSetting.get(Setting.preferRemoteImage)); + asset.hasLocal && (!asset.hasRemote || !AppSetting.get(Setting.preferRemoteImage)); diff --git a/mobile/lib/presentation/widgets/images/local_album_thumbnail.widget.dart b/mobile/lib/presentation/widgets/images/local_album_thumbnail.widget.dart index dcf0f28527..8b9ede4c6d 100644 --- a/mobile/lib/presentation/widgets/images/local_album_thumbnail.widget.dart +++ b/mobile/lib/presentation/widgets/images/local_album_thumbnail.widget.dart @@ -5,10 +5,7 @@ import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart' import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; class LocalAlbumThumbnail extends ConsumerWidget { - const LocalAlbumThumbnail({ - super.key, - required this.albumId, - }); + const LocalAlbumThumbnail({super.key, required this.albumId}); final String albumId; @override @@ -21,34 +18,21 @@ class LocalAlbumThumbnail extends ConsumerWidget { decoration: BoxDecoration( color: context.colorScheme.surfaceContainer, borderRadius: const BorderRadius.all(Radius.circular(16)), - border: Border.all( - color: context.colorScheme.outline.withAlpha(50), - width: 1, - ), - ), - child: Icon( - Icons.collections, - size: 24, - color: context.primaryColor, + border: Border.all(color: context.colorScheme.outline.withAlpha(50), width: 1), ), + child: Icon(Icons.collections, size: 24, color: context.primaryColor), ); } return ClipRRect( borderRadius: const BorderRadius.all(Radius.circular(16)), - child: Thumbnail( - asset: data, - ), + child: Thumbnail(asset: data), ); }, error: (error, stack) { return const Icon(Icons.error, size: 24); }, - loading: () => const SizedBox( - width: 24, - height: 24, - child: Center(child: CircularProgressIndicator()), - ), + loading: () => const SizedBox(width: 24, height: 24, child: Center(child: CircularProgressIndicator())), ); } } diff --git a/mobile/lib/presentation/widgets/images/local_image_provider.dart b/mobile/lib/presentation/widgets/images/local_image_provider.dart index 65311de48a..350bcbb8fb 100644 --- a/mobile/lib/presentation/widgets/images/local_image_provider.dart +++ b/mobile/lib/presentation/widgets/images/local_image_provider.dart @@ -17,8 +17,7 @@ import 'package:immich_mobile/providers/image/exceptions/image_loading_exception import 'package:logging/logging.dart'; class LocalThumbProvider extends ImageProvider { - final AssetMediaRepository _assetMediaRepository = - const AssetMediaRepository(); + final AssetMediaRepository _assetMediaRepository = const AssetMediaRepository(); final CacheManager? cacheManager; final String id; @@ -40,10 +39,7 @@ class LocalThumbProvider extends ImageProvider { } @override - ImageStreamCompleter loadImage( - LocalThumbProvider key, - ImageDecoderCallback decode, - ) { + ImageStreamCompleter loadImage(LocalThumbProvider key, ImageDecoderCallback decode) { final cache = cacheManager ?? ThumbnailImageCacheManager(); return MultiFrameImageStreamCompleter( codec: _codec(key, cache, decode), @@ -58,30 +54,21 @@ class LocalThumbProvider extends ImageProvider { ); } - Future _codec( - LocalThumbProvider key, - CacheManager cache, - ImageDecoderCallback decode, - ) async { - final cacheKey = - '${key.id}-${key.updatedAt}-${key.size.width}x${key.size.height}'; + Future _codec(LocalThumbProvider key, CacheManager cache, ImageDecoderCallback decode) async { + final cacheKey = '${key.id}-${key.updatedAt}-${key.size.width}x${key.size.height}'; final fileFromCache = await cache.getFileFromCache(cacheKey); if (fileFromCache != null) { try { - final buffer = - await ImmutableBuffer.fromFilePath(fileFromCache.file.path); + final buffer = await ImmutableBuffer.fromFilePath(fileFromCache.file.path); return decode(buffer); } catch (_) {} } - final thumbnailBytes = - await _assetMediaRepository.getThumbnail(key.id, size: key.size); + final thumbnailBytes = await _assetMediaRepository.getThumbnail(key.id, size: key.size); if (thumbnailBytes == null) { PaintingBinding.instance.imageCache.evict(key); - throw StateError( - "Loading thumb for local photo ${key.name} failed", - ); + throw StateError("Loading thumb for local photo ${key.name} failed"); } final buffer = await ImmutableBuffer.fromUint8List(thumbnailBytes); @@ -103,8 +90,7 @@ class LocalThumbProvider extends ImageProvider { } class LocalFullImageProvider extends ImageProvider { - final AssetMediaRepository _assetMediaRepository = - const AssetMediaRepository(); + final AssetMediaRepository _assetMediaRepository = const AssetMediaRepository(); final StorageRepository _storageRepository = const StorageRepository(); final String id; @@ -112,12 +98,7 @@ class LocalFullImageProvider extends ImageProvider { final Size size; final AssetType type; - const LocalFullImageProvider({ - required this.id, - required this.name, - required this.size, - required this.type, - }); + const LocalFullImageProvider({required this.id, required this.name, required this.size, required this.type}); @override Future obtainKey(ImageConfiguration configuration) { @@ -125,10 +106,7 @@ class LocalFullImageProvider extends ImageProvider { } @override - ImageStreamCompleter loadImage( - LocalFullImageProvider key, - ImageDecoderCallback decode, - ) { + ImageStreamCompleter loadImage(LocalFullImageProvider key, ImageDecoderCallback decode) { return MultiImageStreamCompleter( codec: _codec(key, decode), scale: 1.0, @@ -139,10 +117,7 @@ class LocalFullImageProvider extends ImageProvider { } // Streams in each stage of the image as we ask for it - Stream _codec( - LocalFullImageProvider key, - ImageDecoderCallback decode, - ) async* { + Stream _codec(LocalFullImageProvider key, ImageDecoderCallback decode) async* { try { switch (key.type) { case AssetType.image: @@ -160,20 +135,13 @@ class LocalFullImageProvider extends ImageProvider { throw StateError('Unsupported asset type ${key.type}'); } } catch (error, stack) { - Logger('ImmichLocalImageProvider') - .severe('Error loading local image ${key.name}', error, stack); - throw const ImageLoadingException( - 'Could not load image from local storage', - ); + Logger('ImmichLocalImageProvider').severe('Error loading local image ${key.name}', error, stack); + throw const ImageLoadingException('Could not load image from local storage'); } } - Future _getThumbnailCodec( - LocalFullImageProvider key, - ImageDecoderCallback decode, - ) async { - final thumbBytes = - await _assetMediaRepository.getThumbnail(key.id, size: key.size); + Future _getThumbnailCodec(LocalFullImageProvider key, ImageDecoderCallback decode) async { + final thumbBytes = await _assetMediaRepository.getThumbnail(key.id, size: key.size); if (thumbBytes == null) { return null; } @@ -181,18 +149,14 @@ class LocalFullImageProvider extends ImageProvider { return decode(buffer); } - Stream _decodeProgressive( - LocalFullImageProvider key, - ImageDecoderCallback decode, - ) async* { + Stream _decodeProgressive(LocalFullImageProvider key, ImageDecoderCallback decode) async* { final file = await _storageRepository.getFileForAsset(key.id); if (file == null) { throw StateError("Opening file for asset ${key.name} failed"); } final fileSize = await file.length(); - final devicePixelRatio = - PlatformDispatcher.instance.views.first.devicePixelRatio; + final devicePixelRatio = PlatformDispatcher.instance.views.first.devicePixelRatio; final isLargeFile = fileSize > 20 * 1024 * 1024; // 20MB final isHEIC = file.path.toLowerCase().contains(RegExp(r'\.(heic|heif)$')); final isProgressive = isLargeFile || (isHEIC && !Platform.isIOS); @@ -204,8 +168,7 @@ class LocalFullImageProvider extends ImageProvider { (key.size.width * progressiveMultiplier).clamp(256, 1024), (key.size.height * progressiveMultiplier).clamp(256, 1024), ); - final mediumThumb = - await _assetMediaRepository.getThumbnail(key.id, size: size); + final mediumThumb = await _assetMediaRepository.getThumbnail(key.id, size: size); if (mediumThumb != null) { final mediumBuffer = await ImmutableBuffer.fromUint8List(mediumThumb); yield await decode(mediumBuffer); @@ -221,8 +184,7 @@ class LocalFullImageProvider extends ImageProvider { (key.size.width * progressiveMultiplier).clamp(512, 2048), (key.size.height * progressiveMultiplier).clamp(512, 2048), ); - final highThumb = - await _assetMediaRepository.getThumbnail(key.id, size: size); + final highThumb = await _assetMediaRepository.getThumbnail(key.id, size: size); if (highThumb != null) { final highBuffer = await ImmutableBuffer.fromUint8List(highThumb); yield await decode(highBuffer); @@ -238,15 +200,11 @@ class LocalFullImageProvider extends ImageProvider { bool operator ==(Object other) { if (identical(this, other)) return true; if (other is LocalFullImageProvider) { - return id == other.id && - size == other.size && - type == other.type && - name == other.name; + return id == other.id && size == other.size && type == other.type && name == other.name; } return false; } @override - int get hashCode => - id.hashCode ^ size.hashCode ^ type.hashCode ^ name.hashCode; + int get hashCode => id.hashCode ^ size.hashCode ^ type.hashCode ^ name.hashCode; } diff --git a/mobile/lib/presentation/widgets/images/remote_image_provider.dart b/mobile/lib/presentation/widgets/images/remote_image_provider.dart index 14d13a08d8..27f310f4f2 100644 --- a/mobile/lib/presentation/widgets/images/remote_image_provider.dart +++ b/mobile/lib/presentation/widgets/images/remote_image_provider.dart @@ -15,10 +15,7 @@ class RemoteThumbProvider extends ImageProvider { final String assetId; final CacheManager? cacheManager; - const RemoteThumbProvider({ - required this.assetId, - this.cacheManager, - }); + const RemoteThumbProvider({required this.assetId, this.cacheManager}); @override Future obtainKey(ImageConfiguration configuration) { @@ -26,10 +23,7 @@ class RemoteThumbProvider extends ImageProvider { } @override - ImageStreamCompleter loadImage( - RemoteThumbProvider key, - ImageDecoderCallback decode, - ) { + ImageStreamCompleter loadImage(RemoteThumbProvider key, ImageDecoderCallback decode) { final cache = cacheManager ?? RemoteImageCacheManager(); final chunkController = StreamController(); return MultiFrameImageStreamCompleter( @@ -49,9 +43,7 @@ class RemoteThumbProvider extends ImageProvider { ImageDecoderCallback decode, StreamController chunkController, ) async { - final preview = getThumbnailUrlForRemoteId( - key.assetId, - ); + final preview = getThumbnailUrlForRemoteId(key.assetId); return ImageLoader.loadImageFromCache( preview, @@ -79,10 +71,7 @@ class RemoteFullImageProvider extends ImageProvider { final String assetId; final CacheManager? cacheManager; - const RemoteFullImageProvider({ - required this.assetId, - this.cacheManager, - }); + const RemoteFullImageProvider({required this.assetId, this.cacheManager}); @override Future obtainKey(ImageConfiguration configuration) { @@ -90,10 +79,7 @@ class RemoteFullImageProvider extends ImageProvider { } @override - ImageStreamCompleter loadImage( - RemoteFullImageProvider key, - ImageDecoderCallback decode, - ) { + ImageStreamCompleter loadImage(RemoteFullImageProvider key, ImageDecoderCallback decode) { final cache = cacheManager ?? RemoteImageCacheManager(); final chunkEvents = StreamController(); return MultiImageStreamCompleter( diff --git a/mobile/lib/presentation/widgets/images/thumb_hash_provider.dart b/mobile/lib/presentation/widgets/images/thumb_hash_provider.dart index cd286a4cdf..8d292523d7 100644 --- a/mobile/lib/presentation/widgets/images/thumb_hash_provider.dart +++ b/mobile/lib/presentation/widgets/images/thumb_hash_provider.dart @@ -8,9 +8,7 @@ import 'package:thumbhash/thumbhash.dart'; class ThumbHashProvider extends ImageProvider { final String thumbHash; - const ThumbHashProvider({ - required this.thumbHash, - }); + const ThumbHashProvider({required this.thumbHash}); @override Future obtainKey(ImageConfiguration configuration) { @@ -18,20 +16,11 @@ class ThumbHashProvider extends ImageProvider { } @override - ImageStreamCompleter loadImage( - ThumbHashProvider key, - ImageDecoderCallback decode, - ) { - return MultiFrameImageStreamCompleter( - codec: _loadCodec(key, decode), - scale: 1.0, - ); + ImageStreamCompleter loadImage(ThumbHashProvider key, ImageDecoderCallback decode) { + return MultiFrameImageStreamCompleter(codec: _loadCodec(key, decode), scale: 1.0); } - Future _loadCodec( - ThumbHashProvider key, - ImageDecoderCallback decode, - ) async { + Future _loadCodec(ThumbHashProvider key, ImageDecoderCallback decode) async { final image = thumbHashToRGBA(base64Decode(key.thumbHash)); return decode(await ImmutableBuffer.fromUint8List(rgbaToBmp(image))); } diff --git a/mobile/lib/presentation/widgets/images/thumbnail.widget.dart b/mobile/lib/presentation/widgets/images/thumbnail.widget.dart index f54c32dac1..8335bd406b 100644 --- a/mobile/lib/presentation/widgets/images/thumbnail.widget.dart +++ b/mobile/lib/presentation/widgets/images/thumbnail.widget.dart @@ -8,16 +8,8 @@ import 'package:logging/logging.dart'; import 'package:octo_image/octo_image.dart'; class Thumbnail extends StatelessWidget { - const Thumbnail({ - this.asset, - this.remoteId, - this.size = const Size.square(256), - this.fit = BoxFit.cover, - super.key, - }) : assert( - asset != null || remoteId != null, - 'Either asset or remoteId must be provided', - ); + const Thumbnail({this.asset, this.remoteId, this.size = const Size.square(256), this.fit = BoxFit.cover, super.key}) + : assert(asset != null || remoteId != null, 'Either asset or remoteId must be provided'); final BaseAsset? asset; final String? remoteId; @@ -26,21 +18,14 @@ class Thumbnail extends StatelessWidget { @override Widget build(BuildContext context) { - final thumbHash = - asset is RemoteAsset ? (asset as RemoteAsset).thumbHash : null; - final provider = - getThumbnailImageProvider(asset: asset, remoteId: remoteId, size: size); + final thumbHash = asset is RemoteAsset ? (asset as RemoteAsset).thumbHash : null; + final provider = getThumbnailImageProvider(asset: asset, remoteId: remoteId, size: size); return OctoImage.fromSet( image: provider, octoSet: OctoSet( placeholderBuilder: _blurHashPlaceholderBuilder(thumbHash, fit: fit), - errorBuilder: _blurHashErrorBuilder( - thumbHash, - provider: provider, - fit: fit, - asset: asset, - ), + errorBuilder: _blurHashErrorBuilder(thumbHash, provider: provider, fit: fit, asset: asset), ), fadeOutDuration: const Duration(milliseconds: 100), fadeInDuration: Duration.zero, @@ -52,10 +37,7 @@ class Thumbnail extends StatelessWidget { } } -OctoPlaceholderBuilder _blurHashPlaceholderBuilder( - String? thumbHash, { - BoxFit? fit, -}) { +OctoPlaceholderBuilder _blurHashPlaceholderBuilder(String? thumbHash, {BoxFit? fit}) { return (context) => thumbHash == null ? const ThumbnailPlaceholder() : FadeInPlaceholderImage( @@ -65,24 +47,15 @@ OctoPlaceholderBuilder _blurHashPlaceholderBuilder( ); } -OctoErrorBuilder _blurHashErrorBuilder( - String? blurhash, { - BaseAsset? asset, - ImageProvider? provider, - BoxFit? fit, -}) => +OctoErrorBuilder _blurHashErrorBuilder(String? blurhash, {BaseAsset? asset, ImageProvider? provider, BoxFit? fit}) => (context, e, s) { - Logger("ImThumbnail") - .warning("Error loading thumbnail for ${asset?.name}", e, s); + Logger("ImThumbnail").warning("Error loading thumbnail for ${asset?.name}", e, s); provider?.evict(); return Stack( alignment: Alignment.center, children: [ _blurHashPlaceholderBuilder(blurhash, fit: fit)(context), - const Opacity( - opacity: 0.75, - child: Icon(Icons.error_outline_rounded), - ), + const Opacity(opacity: 0.75, child: Icon(Icons.error_outline_rounded)), ], ); }; diff --git a/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart b/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart index ce3d39629f..b9ef1ca45a 100644 --- a/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart +++ b/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart @@ -15,6 +15,7 @@ class ThumbnailTile extends ConsumerWidget { this.fit = BoxFit.cover, this.showStorageIndicator = true, this.lockSelection = false, + this.heroOffset, super.key, }); @@ -23,38 +24,33 @@ class ThumbnailTile extends ConsumerWidget { final BoxFit fit; final bool showStorageIndicator; final bool lockSelection; + final int? heroOffset; @override Widget build(BuildContext context, WidgetRef ref) { - final heroOffset = TabsRouterScope.of(context)?.controller.activeIndex ?? 0; + final heroIndex = heroOffset ?? TabsRouterScope.of(context)?.controller.activeIndex ?? 0; final assetContainerColor = context.isDarkTheme ? context.primaryColor.darken(amount: 0.4) : context.primaryColor.lighten(amount: 0.75); final isSelected = ref.watch( - multiSelectProvider.select( - (multiselect) => multiselect.selectedAssets.contains(asset), - ), + multiSelectProvider.select((multiselect) => multiselect.selectedAssets.contains(asset)), ); final borderStyle = lockSelection ? BoxDecoration( color: context.colorScheme.surfaceContainerHighest, - border: Border.all( - color: context.colorScheme.surfaceContainerHighest, - width: 6, - ), + border: Border.all(color: context.colorScheme.surfaceContainerHighest, width: 6), ) : isSelected - ? BoxDecoration( - color: assetContainerColor, - border: Border.all(color: assetContainerColor, width: 6), - ) - : const BoxDecoration(); + ? BoxDecoration( + color: assetContainerColor, + border: Border.all(color: assetContainerColor, width: 6), + ) + : const BoxDecoration(); - final hasStack = - asset is RemoteAsset && (asset as RemoteAsset).stackCount > 0; + final hasStack = asset is RemoteAsset && (asset as RemoteAsset).stackId != null; return Stack( children: [ @@ -70,25 +66,16 @@ class ThumbnailTile extends ConsumerWidget { children: [ Positioned.fill( child: Hero( - tag: '${asset.heroTag}_$heroOffset', - child: Thumbnail( - asset: asset, - fit: fit, - size: size, - ), + tag: '${asset.heroTag}_$heroIndex', + child: Thumbnail(asset: asset, fit: fit, size: size), ), ), if (hasStack) Align( alignment: Alignment.topRight, child: Padding( - padding: EdgeInsets.only( - right: 10.0, - top: asset.isVideo ? 24.0 : 6.0, - ), - child: _StackIndicator( - stackCount: (asset as RemoteAsset).stackCount, - ), + padding: EdgeInsets.only(right: 10.0, top: asset.isVideo ? 24.0 : 6.0), + child: const _TileOverlayIcon(Icons.burst_mode_rounded), ), ), if (asset.isVideo) @@ -102,26 +89,26 @@ class ThumbnailTile extends ConsumerWidget { if (showStorageIndicator) switch (asset.storage) { AssetState.local => const Align( - alignment: Alignment.bottomRight, - child: Padding( - padding: EdgeInsets.only(right: 10.0, bottom: 6.0), - child: _TileOverlayIcon(Icons.cloud_off_outlined), - ), + alignment: Alignment.bottomRight, + child: Padding( + padding: EdgeInsets.only(right: 10.0, bottom: 6.0), + child: _TileOverlayIcon(Icons.cloud_off_outlined), ), + ), AssetState.remote => const Align( - alignment: Alignment.bottomRight, - child: Padding( - padding: EdgeInsets.only(right: 10.0, bottom: 6.0), - child: _TileOverlayIcon(Icons.cloud_outlined), - ), + alignment: Alignment.bottomRight, + child: Padding( + padding: EdgeInsets.only(right: 10.0, bottom: 6.0), + child: _TileOverlayIcon(Icons.cloud_outlined), ), + ), AssetState.merged => const Align( - alignment: Alignment.bottomRight, - child: Padding( - padding: EdgeInsets.only(right: 10.0, bottom: 6.0), - child: _TileOverlayIcon(Icons.cloud_done_outlined), - ), + alignment: Alignment.bottomRight, + child: Padding( + padding: EdgeInsets.only(right: 10.0, bottom: 6.0), + child: _TileOverlayIcon(Icons.cloud_done_outlined), ), + ), }, if (asset.isFavorite) const Align( @@ -143,9 +130,7 @@ class ThumbnailTile extends ConsumerWidget { child: _SelectionIndicator( isSelected: isSelected, isLocked: lockSelection, - color: lockSelection - ? context.colorScheme.surfaceContainerHighest - : assetContainerColor, + color: lockSelection ? context.colorScheme.surfaceContainerHighest : assetContainerColor, ), ), ), @@ -159,79 +144,26 @@ class _SelectionIndicator extends StatelessWidget { final bool isLocked; final Color? color; - const _SelectionIndicator({ - required this.isSelected, - required this.isLocked, - this.color, - }); + const _SelectionIndicator({required this.isSelected, required this.isLocked, this.color}); @override Widget build(BuildContext context) { if (isLocked) { return DecoratedBox( - decoration: BoxDecoration( - shape: BoxShape.circle, - color: color, - ), - child: const Icon( - Icons.check_circle_rounded, - color: Colors.grey, - ), + decoration: BoxDecoration(shape: BoxShape.circle, color: color), + child: const Icon(Icons.check_circle_rounded, color: Colors.grey), ); } else if (isSelected) { return DecoratedBox( - decoration: BoxDecoration( - shape: BoxShape.circle, - color: color, - ), - child: Icon( - Icons.check_circle_rounded, - color: context.primaryColor, - ), + decoration: BoxDecoration(shape: BoxShape.circle, color: color), + child: Icon(Icons.check_circle_rounded, color: context.primaryColor), ); } else { - return const Icon( - Icons.circle_outlined, - color: Colors.white, - ); + return const Icon(Icons.circle_outlined, color: Colors.white); } } } -class _StackIndicator extends StatelessWidget { - final int stackCount; - - const _StackIndicator({required this.stackCount}); - - @override - Widget build(BuildContext context) { - return Row( - spacing: 3, - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.end, - // CrossAxisAlignment.start looks more centered vertically than CrossAxisAlignment.center - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - stackCount.toString(), - style: const TextStyle( - color: Colors.white, - fontSize: 12, - fontWeight: FontWeight.bold, - shadows: [ - Shadow( - blurRadius: 5.0, - color: Color.fromRGBO(0, 0, 0, 0.6), - ), - ], - ), - ), - const _TileOverlayIcon(Icons.burst_mode_rounded), - ], - ); - } -} - class _VideoIndicator extends StatelessWidget { final Duration duration; const _VideoIndicator(this.duration); @@ -251,12 +183,7 @@ class _VideoIndicator extends StatelessWidget { color: Colors.white, fontSize: 12, fontWeight: FontWeight.bold, - shadows: [ - Shadow( - blurRadius: 5.0, - color: Color.fromRGBO(0, 0, 0, 0.6), - ), - ], + shadows: [Shadow(blurRadius: 5.0, color: Color.fromRGBO(0, 0, 0, 0.6))], ), ), const _TileOverlayIcon(Icons.play_circle_outline_rounded), @@ -276,13 +203,7 @@ class _TileOverlayIcon extends StatelessWidget { icon, color: Colors.white, size: 16, - shadows: [ - const Shadow( - blurRadius: 5.0, - color: Color.fromRGBO(0, 0, 0, 0.6), - offset: Offset(0.0, 0.0), - ), - ], + shadows: [const Shadow(blurRadius: 5.0, color: Color.fromRGBO(0, 0, 0, 0.6), offset: Offset(0.0, 0.0))], ); } } diff --git a/mobile/lib/presentation/widgets/memory/memory_bottom_info.widget.dart b/mobile/lib/presentation/widgets/memory/memory_bottom_info.widget.dart index 79e6288a72..f067bc6bf3 100644 --- a/mobile/lib/presentation/widgets/memory/memory_bottom_info.widget.dart +++ b/mobile/lib/presentation/widgets/memory/memory_bottom_info.widget.dart @@ -4,17 +4,14 @@ import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:immich_mobile/domain/models/memory.model.dart'; - -import 'package:immich_mobile/providers/asset_viewer/scroll_to_date_notifier.provider.dart'; +import 'package:immich_mobile/domain/models/timeline.model.dart'; +import 'package:immich_mobile/domain/utils/event_stream.dart'; +import 'package:immich_mobile/routing/router.dart'; class DriftMemoryBottomInfo extends StatelessWidget { final DriftMemory memory; final String title; - const DriftMemoryBottomInfo({ - super.key, - required this.memory, - required this.title, - }); + const DriftMemoryBottomInfo({super.key, required this.memory, required this.title}); @override Widget build(BuildContext context) { @@ -22,43 +19,39 @@ class DriftMemoryBottomInfo extends StatelessWidget { final fileCreatedDate = memory.assets.first.createdAt; return Padding( padding: const EdgeInsets.all(16.0), - child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - title, - style: TextStyle( - color: Colors.grey[400], - fontSize: 13.0, - fontWeight: FontWeight.w500, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: TextStyle(color: Colors.grey[400], fontSize: 13.0, fontWeight: FontWeight.w500), ), - ), - Text( - df.format(fileCreatedDate), - style: const TextStyle( - color: Colors.white, - fontSize: 15.0, - fontWeight: FontWeight.w500, + Text( + df.format(fileCreatedDate), + style: const TextStyle(color: Colors.white, fontSize: 15.0, fontWeight: FontWeight.w500), ), - ), - ], - ), - MaterialButton( - minWidth: 0, - onPressed: () { - context.maybePop(); - scrollToDateNotifierProvider.scrollToDate(fileCreatedDate); - }, - shape: const CircleBorder(), - color: Colors.white.withValues(alpha: 0.2), - elevation: 0, - child: const Icon( - Icons.open_in_new, - color: Colors.white, + ], ), - ), - ]), + Tooltip( + message: 'view_in_timeline'.tr(), + child: MaterialButton( + minWidth: 0, + onPressed: () async { + await context.maybePop(); + await context.navigateTo(const TabShellRoute(children: [MainTimelineRoute()])); + EventStream.shared.emit(ScrollToDateEvent(fileCreatedDate)); + }, + shape: const CircleBorder(), + color: Colors.white.withValues(alpha: 0.2), + elevation: 0, + child: const Icon(Icons.open_in_new, color: Colors.white), + ), + ), + ], + ), ); } } diff --git a/mobile/lib/presentation/widgets/memory/memory_card.widget.dart b/mobile/lib/presentation/widgets/memory/memory_card.widget.dart index a6262bd2e5..eaed60b204 100644 --- a/mobile/lib/presentation/widgets/memory/memory_card.widget.dart +++ b/mobile/lib/presentation/widgets/memory/memory_card.widget.dart @@ -29,59 +29,38 @@ class DriftMemoryCard extends StatelessWidget { color: Colors.black, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(25.0)), - side: BorderSide( - color: Colors.black, - width: 1.0, - ), + side: BorderSide(color: Colors.black, width: 1.0), ), clipBehavior: Clip.hardEdge, child: Stack( children: [ - SizedBox.expand( - child: _BlurredBackdrop(asset: asset), - ), + SizedBox.expand(child: _BlurredBackdrop(asset: asset)), LayoutBuilder( builder: (context, constraints) { // Determine the fit using the aspect ratio BoxFit fit = BoxFit.contain; if (asset.width != null && asset.height != null) { final aspectRatio = asset.width! / asset.height!; - final phoneAspectRatio = - constraints.maxWidth / constraints.maxHeight; + final phoneAspectRatio = constraints.maxWidth / constraints.maxHeight; // Look for a 25% difference in either direction - if (phoneAspectRatio * .75 < aspectRatio && - phoneAspectRatio * 1.25 > aspectRatio) { + if (phoneAspectRatio * .75 < aspectRatio && phoneAspectRatio * 1.25 > aspectRatio) { // Cover to look nice if we have nearly the same aspect ratio fit = BoxFit.cover; } } if (asset.isImage) { - return Hero( - tag: 'memory-${asset.id}', - child: FullImage( - asset, - fit: fit, - size: const Size(double.infinity, double.infinity), - ), - ); + return FullImage(asset, fit: fit, size: const Size(double.infinity, double.infinity)); } else { - return Hero( - tag: 'memory-${asset.id}', - child: SizedBox( - width: context.width, - height: context.height, - child: NativeVideoViewer( - key: ValueKey(asset.id), - asset: asset, - showControls: false, - playbackDelayFactor: 2, - image: FullImage( - asset, - size: Size(context.width, context.height), - fit: BoxFit.contain, - ), - ), + return SizedBox( + width: context.width, + height: context.height, + child: NativeVideoViewer( + key: ValueKey(asset.id), + asset: asset, + showControls: false, + playbackDelayFactor: 2, + image: FullImage(asset, size: Size(context.width, context.height), fit: BoxFit.contain), ), ); } @@ -93,10 +72,7 @@ class DriftMemoryCard extends StatelessWidget { bottom: 18.0, child: Text( title, - style: context.textTheme.headlineMedium?.copyWith( - color: Colors.white, - fontWeight: FontWeight.w500, - ), + style: context.textTheme.headlineMedium?.copyWith(color: Colors.white, fontWeight: FontWeight.w500), ), ), ], @@ -117,16 +93,9 @@ class _BlurredBackdrop extends HookWidget { // Use a nice cheap blur hash image decoration return Container( decoration: BoxDecoration( - image: DecorationImage( - image: MemoryImage( - blurhash, - ), - fit: BoxFit.cover, - ), - ), - child: Container( - color: Colors.black.withValues(alpha: 0.2), + image: DecorationImage(image: MemoryImage(blurhash), fit: BoxFit.cover), ), + child: Container(color: Colors.black.withValues(alpha: 0.2)), ); } else { // Fall back to using a more expensive image filtered @@ -137,16 +106,11 @@ class _BlurredBackdrop extends HookWidget { child: Container( decoration: BoxDecoration( image: DecorationImage( - image: getFullImageProvider( - asset, - size: Size(context.width, context.height), - ), + image: getFullImageProvider(asset, size: Size(context.width, context.height)), fit: BoxFit.cover, ), ), - child: Container( - color: Colors.black.withValues(alpha: 0.2), - ), + child: Container(color: Colors.black.withValues(alpha: 0.2)), ), ); } diff --git a/mobile/lib/presentation/widgets/memory/memory_lane.widget.dart b/mobile/lib/presentation/widgets/memory/memory_lane.widget.dart index aa21f36dd1..e2bc59b4c7 100644 --- a/mobile/lib/presentation/widgets/memory/memory_lane.widget.dart +++ b/mobile/lib/presentation/widgets/memory/memory_lane.widget.dart @@ -17,17 +17,13 @@ class DriftMemoryLane extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { return ConstrainedBox( - constraints: const BoxConstraints( - maxHeight: 200, - ), + constraints: const BoxConstraints(maxHeight: 200), child: CarouselView( itemExtent: 145.0, shrinkExtent: 1.0, elevation: 2, backgroundColor: Colors.black, - overlayColor: WidgetStateProperty.all( - Colors.white.withValues(alpha: 0.1), - ), + overlayColor: WidgetStateProperty.all(Colors.white.withValues(alpha: 0.1)), onTap: (index) { ref.read(hapticFeedbackProvider.notifier).heavyImpact(); @@ -40,71 +36,42 @@ class DriftMemoryLane extends ConsumerWidget { } } - context.pushRoute( - DriftMemoryRoute( - memories: memories, - memoryIndex: index, - ), - ); + context.pushRoute(DriftMemoryRoute(memories: memories, memoryIndex: index)); }, - children: - memories.map((memory) => DriftMemoryCard(memory: memory)).toList(), + children: memories.map((memory) => DriftMemoryCard(memory: memory)).toList(), ), ); } } class DriftMemoryCard extends ConsumerWidget { - const DriftMemoryCard({ - super.key, - required this.memory, - }); + const DriftMemoryCard({super.key, required this.memory}); final DriftMemory memory; @override Widget build(BuildContext context, WidgetRef ref) { final yearsAgo = DateTime.now().year - memory.data.year; - final title = 'years_ago'.t( - context: context, - args: { - 'years': yearsAgo.toString(), - }, - ); + final title = 'years_ago'.t(context: context, args: {'years': yearsAgo.toString()}); return Center( child: Stack( children: [ ColorFiltered( - colorFilter: ColorFilter.mode( - Colors.black.withValues(alpha: 0.2), - BlendMode.darken, - ), - child: Hero( - tag: 'memory-${memory.assets[0].id}', - child: SizedBox( - width: 205, - height: 200, - child: Thumbnail( - remoteId: memory.assets[0].id, - fit: BoxFit.cover, - ), - ), + colorFilter: ColorFilter.mode(Colors.black.withValues(alpha: 0.2), BlendMode.darken), + child: SizedBox( + width: 205, + height: 200, + child: Thumbnail(remoteId: memory.assets[0].id, fit: BoxFit.cover), ), ), Positioned( bottom: 16, left: 16, child: ConstrainedBox( - constraints: const BoxConstraints( - maxWidth: 114, - ), + constraints: const BoxConstraints(maxWidth: 114), child: Text( title, - style: const TextStyle( - fontWeight: FontWeight.w600, - color: Colors.white, - fontSize: 15, - ), + style: const TextStyle(fontWeight: FontWeight.w600, color: Colors.white, fontSize: 15), ), ), ), diff --git a/mobile/lib/presentation/widgets/people/partner_user_avatar.widget.dart b/mobile/lib/presentation/widgets/people/partner_user_avatar.widget.dart new file mode 100644 index 0000000000..8cdf1ed286 --- /dev/null +++ b/mobile/lib/presentation/widgets/people/partner_user_avatar.widget.dart @@ -0,0 +1,31 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/services/api.service.dart'; + +class PartnerUserAvatar extends StatelessWidget { + const PartnerUserAvatar({super.key, required this.partner}); + + final PartnerUserDto partner; + + @override + Widget build(BuildContext context) { + final url = "${Store.get(StoreKey.serverEndpoint)}/users/${partner.id}/profile-image"; + final nameFirstLetter = partner.name.isNotEmpty ? partner.name[0] : ""; + return CircleAvatar( + radius: 16, + backgroundColor: context.primaryColor.withAlpha(50), + foregroundImage: CachedNetworkImageProvider( + url, + headers: ApiService.getRequestHeaders(), + cacheKey: "user-${partner.id}-profile", + ), + // silence errors if user has no profile image, use initials as fallback + onForegroundImageError: (exception, stackTrace) {}, + child: Text(nameFirstLetter.toUpperCase()), + ); + } +} diff --git a/mobile/lib/presentation/widgets/people/person_edit_birthday_modal.widget.dart b/mobile/lib/presentation/widgets/people/person_edit_birthday_modal.widget.dart new file mode 100644 index 0000000000..8d05244617 --- /dev/null +++ b/mobile/lib/presentation/widgets/people/person_edit_birthday_modal.widget.dart @@ -0,0 +1,116 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/providers/infrastructure/people.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; +import 'package:scroll_date_picker/scroll_date_picker.dart'; + +class DriftPersonBirthdayEditForm extends ConsumerStatefulWidget { + final DriftPerson person; + + const DriftPersonBirthdayEditForm({super.key, required this.person}); + + @override + ConsumerState createState() => _DriftPersonNameEditFormState(); +} + +class _DriftPersonNameEditFormState extends ConsumerState { + late DateTime _selectedDate; + + @override + void initState() { + super.initState(); + _selectedDate = widget.person.birthDate ?? DateTime.now(); + } + + void saveBirthday() async { + try { + final result = await ref.read(driftPeopleServiceProvider).updateBrithday(widget.person.id, _selectedDate); + + if (result != 0) { + ref.invalidate(driftGetAllPeopleProvider); + context.pop(_selectedDate); + } + } catch (error) { + debugPrint('Error updating birthday: $error'); + + if (!context.mounted) { + return; + } + + ImmichToast.show( + context: context, + msg: 'scaffold_body_error_occurred'.t(context: context), + gravity: ToastGravity.BOTTOM, + toastType: ToastType.error, + ); + } + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text( + "edit_birthday".t(context: context), + style: const TextStyle(fontWeight: FontWeight.bold), + ), + content: SizedBox( + width: double.maxFinite, + height: 300, + child: ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(16.0)), + + child: ScrollDatePicker( + options: DatePickerOptions( + backgroundColor: context.colorScheme.surfaceContainerHigh, + itemExtent: 50, + diameterRatio: 5, + ), + scrollViewOptions: DatePickerScrollViewOptions( + day: ScrollViewDetailOptions( + margin: const EdgeInsets.all(12), + selectedTextStyle: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold, fontSize: 16), + ), + month: ScrollViewDetailOptions( + margin: const EdgeInsets.all(12), + selectedTextStyle: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold, fontSize: 16), + ), + year: ScrollViewDetailOptions( + margin: const EdgeInsets.all(12), + selectedTextStyle: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold, fontSize: 16), + ), + ), + selectedDate: _selectedDate, + locale: context.locale, + minimumDate: DateTime(1800, 1, 1), + onDateTimeChanged: (DateTime value) { + setState(() { + _selectedDate = value; + }); + }, + ), + ), + ), + actions: [ + TextButton( + onPressed: () => context.pop(null), + child: Text( + "cancel", + style: TextStyle(color: Colors.red[300], fontWeight: FontWeight.bold), + ).tr(), + ), + TextButton( + onPressed: () => saveBirthday(), + child: Text( + "save", + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold), + ).tr(), + ), + ], + ); + } +} diff --git a/mobile/lib/presentation/widgets/people/person_edit_name_modal.widget.dart b/mobile/lib/presentation/widgets/people/person_edit_name_modal.widget.dart new file mode 100644 index 0000000000..46fd683b81 --- /dev/null +++ b/mobile/lib/presentation/widgets/people/person_edit_name_modal.widget.dart @@ -0,0 +1,82 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/providers/infrastructure/people.provider.dart'; +import 'package:immich_mobile/widgets/common/immich_toast.dart'; + +class DriftPersonNameEditForm extends ConsumerStatefulWidget { + final DriftPerson person; + + const DriftPersonNameEditForm({super.key, required this.person}); + + @override + ConsumerState createState() => _DriftPersonNameEditFormState(); +} + +class _DriftPersonNameEditFormState extends ConsumerState { + late TextEditingController _formController; + + @override + void initState() { + super.initState(); + _formController = TextEditingController(text: widget.person.name); + } + + void onEdit(String personId, String newName) async { + try { + final result = await ref.read(driftPeopleServiceProvider).updateName(personId, newName); + if (result != 0) { + ref.invalidate(driftGetAllPeopleProvider); + context.pop(newName); + } + } catch (error) { + debugPrint('Error updating name: $error'); + + if (!context.mounted) { + return; + } + + ImmichToast.show( + context: context, + msg: 'scaffold_body_error_occurred'.t(context: context), + gravity: ToastGravity.BOTTOM, + toastType: ToastType.error, + ); + } + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const Text("edit_name", style: TextStyle(fontWeight: FontWeight.bold)).tr(), + content: SingleChildScrollView( + child: TextFormField( + controller: _formController, + textCapitalization: TextCapitalization.words, + autofocus: true, + decoration: InputDecoration(hintText: 'name'.tr(), border: const OutlineInputBorder()), + ), + ), + actions: [ + TextButton( + onPressed: () => context.pop(null), + child: Text( + "cancel", + style: TextStyle(color: Colors.red[300], fontWeight: FontWeight.bold), + ).tr(), + ), + TextButton( + onPressed: () => onEdit(widget.person.id, _formController.text), + child: Text( + "save", + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold), + ).tr(), + ), + ], + ); + } +} diff --git a/mobile/lib/presentation/widgets/people/person_option_sheet.widget.dart b/mobile/lib/presentation/widgets/people/person_option_sheet.widget.dart new file mode 100644 index 0000000000..6d46b37761 --- /dev/null +++ b/mobile/lib/presentation/widgets/people/person_option_sheet.widget.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; + +class PersonOptionSheet extends ConsumerWidget { + const PersonOptionSheet({super.key, this.onEditName, this.onEditBirthday}); + + final VoidCallback? onEditName; + final VoidCallback? onEditBirthday; + + @override + Widget build(BuildContext context, WidgetRef ref) { + TextStyle textStyle = Theme.of(context).textTheme.bodyLarge!.copyWith(fontWeight: FontWeight.w600); + + return SafeArea( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 24.0), + child: ListView( + shrinkWrap: true, + children: [ + ListTile( + leading: const Icon(Icons.edit), + title: Text('edit_name'.t(context: context), style: textStyle), + onTap: onEditName, + ), + ListTile( + leading: const Icon(Icons.cake), + title: Text('edit_birthday'.t(context: context), style: textStyle), + onTap: onEditBirthday, + ), + ], + ), + ), + ); + } +} diff --git a/mobile/lib/presentation/widgets/remote_album/drift_album_option.widget.dart b/mobile/lib/presentation/widgets/remote_album/drift_album_option.widget.dart index d32608a8b4..c08348e2af 100644 --- a/mobile/lib/presentation/widgets/remote_album/drift_album_option.widget.dart +++ b/mobile/lib/presentation/widgets/remote_album/drift_album_option.widget.dart @@ -25,9 +25,7 @@ class DriftRemoteAlbumOption extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - TextStyle textStyle = Theme.of(context).textTheme.bodyLarge!.copyWith( - fontWeight: FontWeight.w600, - ); + TextStyle textStyle = Theme.of(context).textTheme.bodyLarge!.copyWith(fontWeight: FontWeight.w600); return SafeArea( child: Padding( @@ -38,74 +36,46 @@ class DriftRemoteAlbumOption extends ConsumerWidget { if (onEditAlbum != null) ListTile( leading: const Icon(Icons.edit), - title: Text( - 'edit_album'.t(context: context), - style: textStyle, - ), + title: Text('edit_album'.t(context: context), style: textStyle), onTap: onEditAlbum, ), if (onAddPhotos != null) ListTile( leading: const Icon(Icons.add_a_photo), - title: Text( - 'add_photos'.t(context: context), - style: textStyle, - ), + title: Text('add_photos'.t(context: context), style: textStyle), onTap: onAddPhotos, ), if (onAddUsers != null) ListTile( leading: const Icon(Icons.group_add), - title: Text( - 'album_viewer_page_share_add_users'.t(context: context), - style: textStyle, - ), + title: Text('album_viewer_page_share_add_users'.t(context: context), style: textStyle), onTap: onAddUsers, ), if (onLeaveAlbum != null) ListTile( leading: const Icon(Icons.person_remove_rounded), - title: Text( - 'leave_album'.t(context: context), - style: textStyle, - ), + title: Text('leave_album'.t(context: context), style: textStyle), onTap: onLeaveAlbum, ), if (onToggleAlbumOrder != null) ListTile( leading: const Icon(Icons.swap_vert_rounded), - title: Text( - 'change_display_order'.t(context: context), - style: textStyle, - ), + title: Text('change_display_order'.t(context: context), style: textStyle), onTap: onToggleAlbumOrder, ), if (onCreateSharedLink != null) ListTile( leading: const Icon(Icons.link), - title: Text( - 'create_shared_link'.t(context: context), - style: textStyle, - ), + title: Text('create_shared_link'.t(context: context), style: textStyle), onTap: onCreateSharedLink, ), if (onDeleteAlbum != null) ...[ - const Divider( - indent: 16, - endIndent: 16, - ), + const Divider(indent: 16, endIndent: 16), ListTile( - leading: Icon( - Icons.delete, - color: - context.isDarkTheme ? Colors.red[400] : Colors.red[800], - ), + leading: Icon(Icons.delete, color: context.isDarkTheme ? Colors.red[400] : Colors.red[800]), title: Text( 'delete_album'.t(context: context), - style: textStyle.copyWith( - color: - context.isDarkTheme ? Colors.red[400] : Colors.red[800], - ), + style: textStyle.copyWith(color: context.isDarkTheme ? Colors.red[400] : Colors.red[800]), ), onTap: onDeleteAlbum, ), diff --git a/mobile/lib/presentation/widgets/timeline/fixed/row.dart b/mobile/lib/presentation/widgets/timeline/fixed/row.dart index 1062c00740..3fe3cea3c9 100644 --- a/mobile/lib/presentation/widgets/timeline/fixed/row.dart +++ b/mobile/lib/presentation/widgets/timeline/fixed/row.dart @@ -16,11 +16,7 @@ class FixedTimelineRow extends MultiChildRenderObjectWidget { @override RenderObject createRenderObject(BuildContext context) { - return RenderFixedRow( - dimension: dimension, - spacing: spacing, - textDirection: textDirection, - ); + return RenderFixedRow(dimension: dimension, spacing: spacing, textDirection: textDirection); } @override @@ -50,9 +46,9 @@ class RenderFixedRow extends RenderBox required double dimension, required double spacing, required TextDirection textDirection, - }) : _dimension = dimension, - _spacing = spacing, - _textDirection = textDirection { + }) : _dimension = dimension, + _spacing = spacing, + _textDirection = textDirection { addAll(children); } @@ -90,8 +86,7 @@ class RenderFixedRow extends RenderBox } } - double get intrinsicWidth => - dimension * childCount + spacing * (childCount - 1); + double get intrinsicWidth => dimension * childCount + spacing * (childCount - 1); @override double computeMinIntrinsicWidth(double height) => intrinsicWidth; diff --git a/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart b/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart index 7fff6d7d2d..05f96d49de 100644 --- a/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart +++ b/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart @@ -1,15 +1,17 @@ import 'dart:math' as math; import 'package:auto_route/auto_route.dart'; -import 'package:flutter/widgets.dart'; +import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/services/timeline.service.dart'; import 'package:immich_mobile/presentation/widgets/images/thumbnail_tile.widget.dart'; import 'package:immich_mobile/presentation/widgets/timeline/fixed/row.dart'; import 'package:immich_mobile/presentation/widgets/timeline/header.widget.dart'; import 'package:immich_mobile/presentation/widgets/timeline/segment.model.dart'; import 'package:immich_mobile/presentation/widgets/timeline/segment_builder.dart'; import 'package:immich_mobile/presentation/widgets/timeline/timeline.state.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/timeline_drag_region.dart'; import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.provider.dart'; import 'package:immich_mobile/providers/haptic_feedback.provider.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; @@ -33,15 +35,13 @@ class FixedSegment extends Segment { required super.headerExtent, required super.spacing, required super.header, - }) : assert(tileHeight != 0), - mainAxisExtend = tileHeight + spacing; + }) : assert(tileHeight != 0), + mainAxisExtend = tileHeight + spacing; @override double indexToLayoutOffset(int index) { final relativeIndex = index - gridIndex; - return relativeIndex < 0 - ? startOffset - : gridOffset + (mainAxisExtend * relativeIndex); + return relativeIndex < 0 ? startOffset : gridOffset + (mainAxisExtend * relativeIndex); } @override @@ -66,12 +66,7 @@ class FixedSegment extends Segment { final numberOfAssets = math.min(columnCount, assetCount - assetIndex); if (index == firstIndex) { - return TimelineHeader( - bucket: bucket, - header: header, - height: headerExtent, - assetOffset: firstAssetIndex, - ); + return TimelineHeader(bucket: bucket, header: header, height: headerExtent, assetOffset: firstAssetIndex); } return _FixedSegmentRow( @@ -98,8 +93,7 @@ class _FixedSegmentRow extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final isScrubbing = - ref.watch(timelineStateProvider.select((s) => s.isScrubbing)); + final isScrubbing = ref.watch(timelineStateProvider.select((s) => s.isScrubbing)); final timelineService = ref.read(timelineServiceProvider); if (isScrubbing) { @@ -107,10 +101,7 @@ class _FixedSegmentRow extends ConsumerWidget { } if (timelineService.hasRange(assetIndex, assetCount)) { - return _buildAssetRow( - context, - timelineService.getAssets(assetIndex, assetCount), - ); + return _buildAssetRow(context, timelineService.getAssets(assetIndex, assetCount), timelineService); } return FutureBuilder>( @@ -119,31 +110,30 @@ class _FixedSegmentRow extends ConsumerWidget { if (snapshot.connectionState != ConnectionState.done) { return _buildPlaceholder(context); } - return _buildAssetRow(context, snapshot.requireData); + return _buildAssetRow(context, snapshot.requireData, timelineService); }, ); } Widget _buildPlaceholder(BuildContext context) { - return SegmentBuilder.buildPlaceholder( - context, - assetCount, - size: Size.square(tileHeight), - spacing: spacing, - ); + return SegmentBuilder.buildPlaceholder(context, assetCount, size: Size.square(tileHeight), spacing: spacing); } - Widget _buildAssetRow(BuildContext context, List assets) { + Widget _buildAssetRow(BuildContext context, List assets, TimelineService timelineService) { return FixedTimelineRow( dimension: tileHeight, spacing: spacing, textDirection: Directionality.of(context), children: [ for (int i = 0; i < assets.length; i++) - _AssetTileWidget( - key: ValueKey(assets[i].heroTag), - asset: assets[i], + TimelineAssetIndexWrapper( assetIndex: assetIndex + i, + segmentIndex: 0, // For simplicity, using 0 for now + child: _AssetTileWidget( + key: ValueKey(Object.hash(assets[i].heroTag, assetIndex + i, timelineService.hashCode)), + asset: assets[i], + assetIndex: assetIndex + i, + ), ), ], ); @@ -154,18 +144,9 @@ class _AssetTileWidget extends ConsumerWidget { final BaseAsset asset; final int assetIndex; - const _AssetTileWidget({ - super.key, - required this.asset, - required this.assetIndex, - }); + const _AssetTileWidget({super.key, required this.asset, required this.assetIndex}); - Future _handleOnTap( - BuildContext ctx, - WidgetRef ref, - int assetIndex, - BaseAsset asset, - ) async { + Future _handleOnTap(BuildContext ctx, WidgetRef ref, int assetIndex, BaseAsset asset, int? heroOffset) async { final multiSelectState = ref.read(multiSelectProvider); if (multiSelectState.forceEnable || multiSelectState.isEnabled) { @@ -177,6 +158,7 @@ class _AssetTileWidget extends ConsumerWidget { AssetViewerRoute( initialIndex: assetIndex, timelineService: ref.read(timelineServiceProvider), + heroOffset: heroOffset, ), ); } @@ -193,11 +175,7 @@ class _AssetTileWidget extends ConsumerWidget { } bool _getLockSelectionStatus(WidgetRef ref) { - final lockSelectionAssets = ref.read( - multiSelectProvider.select( - (state) => state.lockedSelectionAssets, - ), - ); + final lockSelectionAssets = ref.read(multiSelectProvider.select((state) => state.lockedSelectionAssets)); if (lockSelectionAssets.isEmpty) { return false; @@ -208,22 +186,20 @@ class _AssetTileWidget extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final heroOffset = TabsRouterScope.of(context)?.controller.activeIndex ?? 0; + final lockSelection = _getLockSelectionStatus(ref); - final showStorageIndicator = ref.watch( - timelineArgsProvider.select((args) => args.showStorageIndicator), - ); + final showStorageIndicator = ref.watch(timelineArgsProvider.select((args) => args.showStorageIndicator)); return RepaintBoundary( child: GestureDetector( - onTap: () => lockSelection - ? null - : _handleOnTap(context, ref, assetIndex, asset), - onLongPress: () => - lockSelection ? null : _handleOnLongPress(ref, asset), + onTap: () => lockSelection ? null : _handleOnTap(context, ref, assetIndex, asset, heroOffset), + onLongPress: () => lockSelection ? null : _handleOnLongPress(ref, asset), child: ThumbnailTile( asset, lockSelection: lockSelection, showStorageIndicator: showStorageIndicator, + heroOffset: heroOffset, ), ), ); diff --git a/mobile/lib/presentation/widgets/timeline/fixed/segment_builder.dart b/mobile/lib/presentation/widgets/timeline/fixed/segment_builder.dart index 327e690267..b65582f976 100644 --- a/mobile/lib/presentation/widgets/timeline/fixed/segment_builder.dart +++ b/mobile/lib/presentation/widgets/timeline/fixed/segment_builder.dart @@ -35,18 +35,14 @@ class FixedSegmentBuilder extends SegmentBuilder { final timelineHeader = switch (groupBy) { GroupAssetsBy.month => HeaderType.month, - GroupAssetsBy.day => - bucket is TimeBucket && bucket.date.month != previousDate?.month - ? HeaderType.monthAndDay - : HeaderType.day, + GroupAssetsBy.day || GroupAssetsBy.auto => + bucket is TimeBucket && bucket.date.month != previousDate?.month ? HeaderType.monthAndDay : HeaderType.day, GroupAssetsBy.none => HeaderType.none, }; final headerExtent = SegmentBuilder.headerExtent(timelineHeader); final segmentStartOffset = startOffset; - startOffset += headerExtent + - (tileHeight * numberOfRows) + - spacing * (numberOfRows - 1); + startOffset += headerExtent + (tileHeight * numberOfRows) + spacing * (numberOfRows - 1); final segmentEndOffset = startOffset; segments.add( diff --git a/mobile/lib/presentation/widgets/timeline/header.widget.dart b/mobile/lib/presentation/widgets/timeline/header.widget.dart index c6c10c26c9..b8c6668a38 100644 --- a/mobile/lib/presentation/widgets/timeline/header.widget.dart +++ b/mobile/lib/presentation/widgets/timeline/header.widget.dart @@ -43,17 +43,11 @@ class TimelineHeader extends StatelessWidget { final date = (bucket as TimeBucket).date; - final isMonthHeader = - header == HeaderType.month || header == HeaderType.monthAndDay; - final isDayHeader = - header == HeaderType.day || header == HeaderType.monthAndDay; + final isMonthHeader = header == HeaderType.month || header == HeaderType.monthAndDay; + final isDayHeader = header == HeaderType.day || header == HeaderType.monthAndDay; return Padding( - padding: EdgeInsets.only( - top: isMonthHeader ? 8.0 : 0.0, - left: 12.0, - right: 12.0, - ), + padding: EdgeInsets.only(top: isMonthHeader ? 8.0 : 0.0, left: 12.0, right: 12.0), child: SizedBox( height: height, child: Column( @@ -63,32 +57,17 @@ class TimelineHeader extends StatelessWidget { if (isMonthHeader) Row( children: [ - Text( - _formatMonth(context, date), - style: context.textTheme.labelLarge?.copyWith(fontSize: 24), - ), + Text(_formatMonth(context, date), style: context.textTheme.labelLarge?.copyWith(fontSize: 24)), const Spacer(), - if (header != HeaderType.monthAndDay) - _BulkSelectIconButton( - bucket: bucket, - assetOffset: assetOffset, - ), + if (header != HeaderType.monthAndDay) _BulkSelectIconButton(bucket: bucket, assetOffset: assetOffset), ], ), if (isDayHeader) Row( children: [ - Text( - _formatDay(context, date), - style: context.textTheme.labelLarge?.copyWith( - fontSize: 15, - ), - ), + Text(_formatDay(context, date), style: context.textTheme.labelLarge?.copyWith(fontSize: 15)), const Spacer(), - _BulkSelectIconButton( - bucket: bucket, - assetOffset: assetOffset, - ), + _BulkSelectIconButton(bucket: bucket, assetOffset: assetOffset), ], ), ], @@ -102,18 +81,13 @@ class _BulkSelectIconButton extends ConsumerWidget { final Bucket bucket; final int assetOffset; - const _BulkSelectIconButton({ - required this.bucket, - required this.assetOffset, - }); + const _BulkSelectIconButton({required this.bucket, required this.assetOffset}); @override Widget build(BuildContext context, WidgetRef ref) { List bucketAssets; try { - bucketAssets = ref - .watch(timelineServiceProvider) - .getAssets(assetOffset, bucket.assetCount); + bucketAssets = ref.watch(timelineServiceProvider).getAssets(assetOffset, bucket.assetCount); } catch (e) { bucketAssets = []; } @@ -122,23 +96,12 @@ class _BulkSelectIconButton extends ConsumerWidget { return IconButton( onPressed: () { - ref.read(multiSelectProvider.notifier).toggleBucketSelection( - assetOffset, - bucket.assetCount, - ); + ref.read(multiSelectProvider.notifier).toggleBucketSelection(assetOffset, bucket.assetCount); ref.read(hapticFeedbackProvider.notifier).heavyImpact(); }, icon: isAllSelected - ? Icon( - Icons.check_circle_rounded, - size: 26, - color: context.primaryColor, - ) - : Icon( - Icons.check_circle_outline_rounded, - size: 26, - color: context.colorScheme.onSurfaceSecondary, - ), + ? Icon(Icons.check_circle_rounded, size: 26, color: context.primaryColor) + : Icon(Icons.check_circle_outline_rounded, size: 26, color: context.colorScheme.onSurfaceSecondary), ); } } diff --git a/mobile/lib/presentation/widgets/timeline/scrubber.widget.dart b/mobile/lib/presentation/widgets/timeline/scrubber.widget.dart index 248fc6b579..be1d0f0873 100644 --- a/mobile/lib/presentation/widgets/timeline/scrubber.widget.dart +++ b/mobile/lib/presentation/widgets/timeline/scrubber.widget.dart @@ -43,10 +43,7 @@ class Scrubber extends ConsumerStatefulWidget { ConsumerState createState() => ScrubberState(); } -List<_Segment> _buildSegments({ - required List layoutSegments, - required double timelineHeight, -}) { +List<_Segment> _buildSegments({required List layoutSegments, required double timelineHeight}) { const double offsetThreshold = 20.0; final segments = <_Segment>[]; @@ -58,24 +55,15 @@ List<_Segment> _buildSegments({ DateTime? lastDate; double lastOffset = -offsetThreshold; for (final layoutSegment in layoutSegments) { - final scrollPercentage = - layoutSegment.startOffset / layoutSegments.last.endOffset; + final scrollPercentage = layoutSegment.startOffset / layoutSegments.last.endOffset; final startOffset = scrollPercentage * timelineHeight; final date = (layoutSegment.bucket as TimeBucket).date; final label = formatter.format(date); - final showSegment = lastOffset + offsetThreshold <= startOffset && - (lastDate == null || date.year != lastDate.year); + final showSegment = lastOffset + offsetThreshold <= startOffset && (lastDate == null || date.year != lastDate.year); - segments.add( - _Segment( - date: date, - startOffset: startOffset, - scrollLabel: label, - showSegment: showSegment, - ), - ); + segments.add(_Segment(date: date, startOffset: startOffset, scrollLabel: label, showSegment: showSegment)); lastDate = date; if (showSegment) { lastOffset = startOffset; @@ -85,8 +73,7 @@ List<_Segment> _buildSegments({ return segments; } -class ScrubberState extends ConsumerState - with TickerProviderStateMixin { +class ScrubberState extends ConsumerState with TickerProviderStateMixin { double _thumbTopOffset = 0.0; bool _isDragging = false; List<_Segment> _segments = []; @@ -98,44 +85,26 @@ class ScrubberState extends ConsumerState late AnimationController _labelAnimationController; late Animation _labelAnimation; - double get _scrubberHeight => - widget.timelineHeight - widget.topPadding - widget.bottomPadding; + double get _scrubberHeight => widget.timelineHeight - widget.topPadding - widget.bottomPadding; late ScrollController _scrollController; double get _currentOffset { if (_scrollController.hasClients != true) return 0.0; - return _scrollController.offset * - _scrubberHeight / - _scrollController.position.maxScrollExtent; + return _scrollController.offset * _scrubberHeight / _scrollController.position.maxScrollExtent; } @override void initState() { super.initState(); _isDragging = false; - _segments = _buildSegments( - layoutSegments: widget.layoutSegments, - timelineHeight: _scrubberHeight, - ); - _thumbAnimationController = AnimationController( - vsync: this, - duration: kTimelineScrubberFadeInDuration, - ); - _thumbAnimation = CurvedAnimation( - parent: _thumbAnimationController, - curve: Curves.fastEaseInToSlowEaseOut, - ); - _labelAnimationController = AnimationController( - vsync: this, - duration: kTimelineScrubberFadeInDuration, - ); + _segments = _buildSegments(layoutSegments: widget.layoutSegments, timelineHeight: _scrubberHeight); + _thumbAnimationController = AnimationController(vsync: this, duration: kTimelineScrubberFadeInDuration); + _thumbAnimation = CurvedAnimation(parent: _thumbAnimationController, curve: Curves.fastEaseInToSlowEaseOut); + _labelAnimationController = AnimationController(vsync: this, duration: kTimelineScrubberFadeInDuration); - _labelAnimation = CurvedAnimation( - parent: _labelAnimationController, - curve: Curves.fastOutSlowIn, - ); + _labelAnimation = CurvedAnimation(parent: _labelAnimationController, curve: Curves.fastOutSlowIn); } @override @@ -148,12 +117,8 @@ class ScrubberState extends ConsumerState void didUpdateWidget(covariant Scrubber oldWidget) { super.didUpdateWidget(oldWidget); - if (oldWidget.layoutSegments.lastOrNull?.endOffset != - widget.layoutSegments.lastOrNull?.endOffset) { - _segments = _buildSegments( - layoutSegments: widget.layoutSegments, - timelineHeight: _scrubberHeight, - ); + if (oldWidget.layoutSegments.lastOrNull?.endOffset != widget.layoutSegments.lastOrNull?.endOffset) { + _segments = _buildSegments(layoutSegments: widget.layoutSegments, timelineHeight: _scrubberHeight); } } @@ -179,8 +144,7 @@ class ScrubberState extends ConsumerState return false; } - if (notification is ScrollStartNotification || - notification is ScrollUpdateNotification) { + if (notification is ScrollStartNotification || notification is ScrollUpdateNotification) { ref.read(timelineStateProvider.notifier).setScrolling(true); } else if (notification is ScrollEndNotification) { ref.read(timelineStateProvider.notifier).setScrolling(false); @@ -284,13 +248,10 @@ class ScrubberState extends ConsumerState } int _findLayoutSegmentIndex(_Segment segment) { - return widget.layoutSegments.indexWhere( - (layoutSegment) { - final bucket = layoutSegment.bucket as TimeBucket; - return bucket.date.year == segment.date.year && - bucket.date.month == segment.date.month; - }, - ); + return widget.layoutSegments.indexWhere((layoutSegment) { + final bucket = layoutSegment.bucket as TimeBucket; + return bucket.date.year == segment.date.year && bucket.date.month == segment.date.month; + }); } void _scrollToLayoutSegment(int layoutSegmentIndex) { @@ -299,10 +260,7 @@ class ScrubberState extends ConsumerState final viewportHeight = _scrollController.position.viewportDimension; final targetScrollOffset = layoutSegment.startOffset; - final centeredOffset = targetScrollOffset - - (viewportHeight / 4) + - 100 + - (widget.monthSegmentSnappingOffset ?? 0.0); + final centeredOffset = targetScrollOffset - (viewportHeight / 4) + 100 + (widget.monthSegmentSnappingOffset ?? 0.0); _scrollController.jumpTo(centeredOffset.clamp(0.0, maxScrollExtent)); } @@ -323,19 +281,13 @@ class ScrubberState extends ConsumerState if (_scrollController.hasClients == true) { // Cache to avoid multiple calls to [_currentOffset] final scrollOffset = _currentOffset; - final labelText = _segments - .lastWhereOrNull( - (segment) => segment.startOffset <= scrollOffset, - ) - ?.scrollLabel ?? + final labelText = + _segments.lastWhereOrNull((segment) => segment.startOffset <= scrollOffset)?.scrollLabel ?? _segments.firstOrNull?.scrollLabel; label = labelText != null ? Text( labelText, - style: ctx.textTheme.bodyLarge?.copyWith( - color: Colors.white, - fontWeight: FontWeight.bold, - ), + style: ctx.textTheme.bodyLarge?.copyWith(color: Colors.white, fontWeight: FontWeight.bold), ) : null; } @@ -354,8 +306,7 @@ class ScrubberState extends ConsumerState isDragging: _isDragging, ), ), - if (_scrollController.hasClients && - _scrollController.position.maxScrollExtent > 0) + if (_scrollController.hasClients && _scrollController.position.maxScrollExtent > 0) PositionedDirectional( top: _thumbTopOffset + widget.topPadding, end: 0, @@ -364,11 +315,7 @@ class ScrubberState extends ConsumerState onVerticalDragStart: _onDragStart, onVerticalDragUpdate: _onDragUpdate, onVerticalDragEnd: _onDragEnd, - child: _Scrubber( - thumbAnimation: _thumbAnimation, - labelAnimation: _labelAnimation, - label: label, - ), + child: _Scrubber(thumbAnimation: _thumbAnimation, labelAnimation: _labelAnimation, label: label), ), ), ), @@ -383,12 +330,7 @@ class _SegmentsLayer extends StatelessWidget { final double topPadding; final bool isDragging; - const _SegmentsLayer({ - super.key, - required this.segments, - required this.topPadding, - required this.isDragging, - }); + const _SegmentsLayer({super.key, required this.segments, required this.topPadding, required this.isDragging}); @override Widget build(BuildContext context) { @@ -402,9 +344,7 @@ class _SegmentsLayer extends StatelessWidget { key: ValueKey('segment_${segment.date.millisecondsSinceEpoch}'), top: topPadding + segment.startOffset, end: 100, - child: RepaintBoundary( - child: _SegmentWidget(segment), - ), + child: RepaintBoundary(child: _SegmentWidget(segment)), ), ) .toList(), @@ -432,10 +372,7 @@ class _SegmentWidget extends StatelessWidget { alignment: Alignment.center, child: Text( _segment.date.year.toString(), - style: context.textTheme.labelMedium?.copyWith( - fontFamily: "OverpassMono", - fontWeight: FontWeight.w600, - ), + style: context.textTheme.labelMedium?.copyWith(fontFamily: "OverpassMono", fontWeight: FontWeight.w600), ), ), ), @@ -449,11 +386,7 @@ class _ScrollLabel extends StatelessWidget { final Color backgroundColor; final Animation animation; - const _ScrollLabel({ - required this.label, - required this.backgroundColor, - required this.animation, - }); + const _ScrollLabel({required this.label, required this.backgroundColor, required this.animation}); @override Widget build(BuildContext context) { @@ -484,11 +417,7 @@ class _Scrubber extends StatelessWidget { final Animation thumbAnimation; final Animation labelAnimation; - const _Scrubber({ - this.label, - required this.thumbAnimation, - required this.labelAnimation, - }); + const _Scrubber({this.label, required this.thumbAnimation, required this.labelAnimation}); @override Widget build(BuildContext context) { @@ -502,12 +431,7 @@ class _Scrubber extends StatelessWidget { mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.end, children: [ - if (label != null) - _ScrollLabel( - label: label!, - backgroundColor: backgroundColor, - animation: labelAnimation, - ), + if (label != null) _ScrollLabel(label: label!, backgroundColor: backgroundColor, animation: labelAnimation), _CircularThumb(backgroundColor), ], ), @@ -533,9 +457,7 @@ class _CircularThumb extends StatelessWidget { topRight: Radius.circular(4.0), bottomRight: Radius.circular(4.0), ), - child: Container( - constraints: BoxConstraints.tight(const Size(48.0 * 0.6, 48.0)), - ), + child: Container(constraints: BoxConstraints.tight(const Size(48.0 * 0.6, 48.0))), ), ); } @@ -557,14 +479,8 @@ class _ArrowPainter extends CustomPainter { final baseX = size.width / 2; final baseY = size.height / 2; - canvas.drawPath( - _trianglePath(Offset(baseX, baseY - 2.0), width, height, true), - paint, - ); - canvas.drawPath( - _trianglePath(Offset(baseX, baseY + 2.0), width, height, false), - paint, - ); + canvas.drawPath(_trianglePath(Offset(baseX, baseY - 2.0), width, height, true), paint); + canvas.drawPath(_trianglePath(Offset(baseX, baseY + 2.0), width, height, false), paint); } static Path _trianglePath(Offset o, double width, double height, bool isUp) { @@ -580,27 +496,18 @@ class _SlideFadeTransition extends StatelessWidget { final Animation _animation; final Widget _child; - const _SlideFadeTransition({ - required Animation animation, - required Widget child, - }) : _animation = animation, - _child = child; + const _SlideFadeTransition({required Animation animation, required Widget child}) + : _animation = animation, + _child = child; @override Widget build(BuildContext context) { return AnimatedBuilder( animation: _animation, - builder: (context, child) => - _animation.value == 0.0 ? const SizedBox() : child!, + builder: (context, child) => _animation.value == 0.0 ? const SizedBox() : child!, child: SlideTransition( - position: Tween( - begin: const Offset(0.3, 0.0), - end: const Offset(0.0, 0.0), - ).animate(_animation), - child: FadeTransition( - opacity: _animation, - child: _child, - ), + position: Tween(begin: const Offset(0.3, 0.0), end: const Offset(0.0, 0.0)).animate(_animation), + child: FadeTransition(opacity: _animation, child: _child), ), ); } @@ -612,19 +519,9 @@ class _Segment { final String scrollLabel; final bool showSegment; - const _Segment({ - required this.date, - required this.startOffset, - required this.scrollLabel, - this.showSegment = false, - }); + const _Segment({required this.date, required this.startOffset, required this.scrollLabel, this.showSegment = false}); - _Segment copyWith({ - DateTime? date, - double? startOffset, - String? scrollLabel, - bool? showSegment, - }) { + _Segment copyWith({DateTime? date, double? startOffset, String? scrollLabel, bool? showSegment}) { return _Segment( date: date ?? this.date, startOffset: startOffset ?? this.startOffset, diff --git a/mobile/lib/presentation/widgets/timeline/segment.model.dart b/mobile/lib/presentation/widgets/timeline/segment.model.dart index 09d892f69a..bc5f974874 100644 --- a/mobile/lib/presentation/widgets/timeline/segment.model.dart +++ b/mobile/lib/presentation/widgets/timeline/segment.model.dart @@ -37,13 +37,12 @@ abstract class Segment { required this.headerExtent, required this.spacing, required this.header, - }) : gridIndex = firstIndex + 1, - gridOffset = startOffset + headerExtent + spacing; + }) : gridIndex = firstIndex + 1, + gridOffset = startOffset + headerExtent + spacing; bool containsIndex(int index) => firstIndex <= index && index <= lastIndex; - bool isWithinOffset(double offset) => - startOffset <= offset && offset <= endOffset; + bool isWithinOffset(double offset) => startOffset <= offset && offset <= endOffset; int getMinChildIndexForScrollOffset(double scrollOffset); int getMaxChildIndexForScrollOffset(double scrollOffset); @@ -88,13 +87,9 @@ abstract class Segment { } extension SegmentListExtension on List { - bool equals(List other) => - length == other.length && - lastOrNull?.endOffset == other.lastOrNull?.endOffset; + bool equals(List other) => length == other.length && lastOrNull?.endOffset == other.lastOrNull?.endOffset; - Segment? findByIndex(int index) => - firstWhereOrNull((s) => s.containsIndex(index)); + Segment? findByIndex(int index) => firstWhereOrNull((s) => s.containsIndex(index)); - Segment? findByOffset(double offset) => - firstWhereOrNull((s) => s.isWithinOffset(offset)) ?? lastOrNull; + Segment? findByOffset(double offset) => firstWhereOrNull((s) => s.isWithinOffset(offset)) ?? lastOrNull; } diff --git a/mobile/lib/presentation/widgets/timeline/segment_builder.dart b/mobile/lib/presentation/widgets/timeline/segment_builder.dart index a746eab243..c80595a446 100644 --- a/mobile/lib/presentation/widgets/timeline/segment_builder.dart +++ b/mobile/lib/presentation/widgets/timeline/segment_builder.dart @@ -9,34 +9,26 @@ abstract class SegmentBuilder { final double spacing; final GroupAssetsBy groupBy; - const SegmentBuilder({ - required this.buckets, - this.spacing = kTimelineSpacing, - this.groupBy = GroupAssetsBy.day, - }); + const SegmentBuilder({required this.buckets, this.spacing = kTimelineSpacing, this.groupBy = GroupAssetsBy.day}); static double headerExtent(HeaderType header) => switch (header) { - HeaderType.month => kTimelineHeaderExtent, - HeaderType.day => kTimelineHeaderExtent * 0.90, - HeaderType.monthAndDay => kTimelineHeaderExtent * 1.6, - HeaderType.none => 0.0, - }; + HeaderType.month => kTimelineHeaderExtent, + HeaderType.day => kTimelineHeaderExtent * 0.90, + HeaderType.monthAndDay => kTimelineHeaderExtent * 1.6, + HeaderType.none => 0.0, + }; static Widget buildPlaceholder( BuildContext context, int count, { Size size = const Size.square(kTimelineFixedTileExtent), double spacing = kTimelineSpacing, - }) => - RepaintBoundary( - child: FixedTimelineRow( - dimension: size.height, - spacing: spacing, - textDirection: Directionality.of(context), - children: List.generate( - count, - (_) => ThumbnailPlaceholder(width: size.width, height: size.height), - ), - ), - ); + }) => RepaintBoundary( + child: FixedTimelineRow( + dimension: size.height, + spacing: spacing, + textDirection: Directionality.of(context), + children: List.generate(count, (_) => ThumbnailPlaceholder(width: size.width, height: size.height)), + ), + ); } diff --git a/mobile/lib/presentation/widgets/timeline/timeline.state.dart b/mobile/lib/presentation/widgets/timeline/timeline.state.dart index 6faa4da9f7..ad3ae3ccf6 100644 --- a/mobile/lib/presentation/widgets/timeline/timeline.state.dart +++ b/mobile/lib/presentation/widgets/timeline/timeline.state.dart @@ -54,10 +54,7 @@ class TimelineState { final bool isScrubbing; final bool isScrolling; - const TimelineState({ - this.isScrubbing = false, - this.isScrolling = false, - }); + const TimelineState({this.isScrubbing = false, this.isScrolling = false}); bool get isInteracting => isScrubbing || isScrolling; @@ -70,10 +67,7 @@ class TimelineState { int get hashCode => isScrubbing.hashCode ^ isScrolling.hashCode; TimelineState copyWith({bool? isScrubbing, bool? isScrolling}) { - return TimelineState( - isScrubbing: isScrubbing ?? this.isScrubbing, - isScrolling: isScrolling ?? this.isScrolling, - ); + return TimelineState(isScrubbing: isScrubbing ?? this.isScrubbing, isScrolling: isScrolling ?? this.isScrolling); } } @@ -89,41 +83,30 @@ class TimelineStateNotifier extends Notifier { } @override - TimelineState build() => const TimelineState( - isScrubbing: false, - isScrolling: false, - ); + TimelineState build() => const TimelineState(isScrubbing: false, isScrolling: false); } // This provider watches the buckets from the timeline service & args and serves the segments. // It should be used only after the timeline service and timeline args provider is overridden -final timelineSegmentProvider = StreamProvider.autoDispose>( - (ref) async* { - final args = ref.watch(timelineArgsProvider); - final columnCount = args.columnCount; - final spacing = args.spacing; - final availableTileWidth = args.maxWidth - (spacing * (columnCount - 1)); - final tileExtent = math.max(0, availableTileWidth) / columnCount; +final timelineSegmentProvider = StreamProvider.autoDispose>((ref) async* { + final args = ref.watch(timelineArgsProvider); + final columnCount = args.columnCount; + final spacing = args.spacing; + final availableTileWidth = args.maxWidth - (spacing * (columnCount - 1)); + final tileExtent = math.max(0, availableTileWidth) / columnCount; - final groupBy = args.groupBy ?? - GroupAssetsBy - .values[ref.watch(settingsProvider).get(Setting.groupAssetsBy)]; + final groupBy = args.groupBy ?? GroupAssetsBy.values[ref.watch(settingsProvider).get(Setting.groupAssetsBy)]; - final timelineService = ref.watch(timelineServiceProvider); - yield* timelineService.watchBuckets().map((buckets) { - return FixedSegmentBuilder( - buckets: buckets, - tileHeight: tileExtent, - columnCount: columnCount, - spacing: spacing, - groupBy: groupBy, - ).generate(); - }); - }, - dependencies: [timelineServiceProvider, timelineArgsProvider], -); + final timelineService = ref.watch(timelineServiceProvider); + yield* timelineService.watchBuckets().map((buckets) { + return FixedSegmentBuilder( + buckets: buckets, + tileHeight: tileExtent, + columnCount: columnCount, + spacing: spacing, + groupBy: groupBy, + ).generate(); + }); +}, dependencies: [timelineServiceProvider, timelineArgsProvider]); -final timelineStateProvider = - NotifierProvider( - TimelineStateNotifier.new, -); +final timelineStateProvider = NotifierProvider(TimelineStateNotifier.new); diff --git a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart index 873908832b..838edd8a47 100644 --- a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart +++ b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart @@ -1,11 +1,14 @@ import 'dart:async'; +import 'dart:collection'; import 'dart:math' as math; import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/setting.model.dart'; import 'package:immich_mobile/domain/models/timeline.model.dart'; import 'package:immich_mobile/domain/utils/event_stream.dart'; @@ -15,6 +18,7 @@ import 'package:immich_mobile/presentation/widgets/bottom_sheet/general_bottom_s import 'package:immich_mobile/presentation/widgets/timeline/scrubber.widget.dart'; import 'package:immich_mobile/presentation/widgets/timeline/segment.model.dart'; import 'package:immich_mobile/presentation/widgets/timeline/timeline.state.dart'; +import 'package:immich_mobile/presentation/widgets/timeline/timeline_drag_region.dart'; import 'package:immich_mobile/providers/infrastructure/setting.provider.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; @@ -29,11 +33,7 @@ class Timeline extends StatelessWidget { this.topSliverWidgetHeight, this.showStorageIndicator = false, this.withStack = false, - this.appBar = const ImmichSliverAppBar( - floating: true, - pinned: false, - snap: false, - ), + this.appBar = const ImmichSliverAppBar(floating: true, pinned: false, snap: false), this.bottomSheet = const GeneralBottomSheet(), this.groupBy, }); @@ -49,6 +49,7 @@ class Timeline extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( + resizeToAvoidBottomInset: false, body: LayoutBuilder( builder: (_, constraints) => ProviderScope( overrides: [ @@ -56,9 +57,7 @@ class Timeline extends StatelessWidget { (ref) => TimelineArgs( maxWidth: constraints.maxWidth, maxHeight: constraints.maxHeight, - columnCount: ref.watch( - settingsProvider.select((s) => s.get(Setting.tilesPerRow)), - ), + columnCount: ref.watch(settingsProvider.select((s) => s.get(Setting.tilesPerRow))), showStorageIndicator: showStorageIndicator, withStack: withStack, groupBy: groupBy, @@ -78,12 +77,7 @@ class Timeline extends StatelessWidget { } class _SliverTimeline extends ConsumerStatefulWidget { - const _SliverTimeline({ - this.topSliverWidget, - this.topSliverWidgetHeight, - this.appBar, - this.bottomSheet, - }); + const _SliverTimeline({this.topSliverWidget, this.topSliverWidgetHeight, this.appBar, this.bottomSheet}); final Widget? topSliverWidget; final double? topSliverWidgetHeight; @@ -96,130 +90,262 @@ class _SliverTimeline extends ConsumerStatefulWidget { class _SliverTimelineState extends ConsumerState<_SliverTimeline> { final _scrollController = ScrollController(); - StreamSubscription? _reloadSubscription; + StreamSubscription? _eventSubscription; + + // Drag selection state + bool _dragging = false; + TimelineAssetIndex? _dragAnchorIndex; + final Set _draggedAssets = HashSet(); + ScrollPhysics? _scrollPhysics; + + int _perRow = 4; + double _scaleFactor = 3.0; + double _baseScaleFactor = 3.0; @override void initState() { super.initState(); - _reloadSubscription = - EventStream.shared.listen((_) => setState(() {})); + _eventSubscription = EventStream.shared.listen(_onEvent); + + WidgetsBinding.instance.addPostFrameCallback((_) { + final currentTilesPerRow = ref.read(settingsProvider).get(Setting.tilesPerRow); + setState(() { + _perRow = currentTilesPerRow; + _scaleFactor = 7.0 - _perRow; + _baseScaleFactor = _scaleFactor; + }); + }); + } + + void _onEvent(Event event) { + switch (event) { + case ScrollToTopEvent(): + _scrollController.animateTo(0, duration: const Duration(milliseconds: 250), curve: Curves.easeInOut); + case ScrollToDateEvent scrollToDateEvent: + _scrollToDate(scrollToDateEvent.date); + case TimelineReloadEvent(): + setState(() {}); + default: + break; + } } @override void dispose() { _scrollController.dispose(); - _reloadSubscription?.cancel(); + _eventSubscription?.cancel(); super.dispose(); } + void _scrollToDate(DateTime date) { + final asyncSegments = ref.read(timelineSegmentProvider); + asyncSegments.whenData((segments) { + // Find the segment that contains assets from the target date + final targetSegment = segments.firstWhereOrNull((segment) { + if (segment.bucket is TimeBucket) { + final segmentDate = (segment.bucket as TimeBucket).date; + // Check if the segment date matches the target date (year, month, day) + return segmentDate.year == date.year && segmentDate.month == date.month && segmentDate.day == date.day; + } + return false; + }); + + // If exact date not found, try to find the closest month + final fallbackSegment = + targetSegment ?? + segments.firstWhereOrNull((segment) { + if (segment.bucket is TimeBucket) { + final segmentDate = (segment.bucket as TimeBucket).date; + return segmentDate.year == date.year && segmentDate.month == date.month; + } + return false; + }); + + if (fallbackSegment != null) { + // Scroll to the segment with a small offset to show the header + final targetOffset = fallbackSegment.startOffset - 50; + _scrollController.animateTo( + targetOffset.clamp(0.0, _scrollController.position.maxScrollExtent), + duration: const Duration(milliseconds: 500), + curve: Curves.easeInOut, + ); + } + }); + } + + // Drag selection methods + void _setDragStartIndex(TimelineAssetIndex index) { + setState(() { + _scrollPhysics = const ClampingScrollPhysics(); + _dragAnchorIndex = index; + _dragging = true; + }); + } + + void _stopDrag() { + WidgetsBinding.instance.addPostFrameCallback((_) { + // Update the physics post frame to prevent sudden change in physics on iOS. + setState(() { + _scrollPhysics = null; + }); + }); + setState(() { + _dragging = false; + _draggedAssets.clear(); + }); + // Reset the scrolling state after a small delay to allow bottom sheet to expand again + Future.delayed(const Duration(milliseconds: 300), () { + if (mounted) { + ref.read(timelineStateProvider.notifier).setScrolling(false); + } + }); + } + + void _dragScroll(ScrollDirection direction) { + _scrollController.animateTo( + _scrollController.offset + (direction == ScrollDirection.forward ? 175 : -175), + duration: const Duration(milliseconds: 125), + curve: Curves.easeOut, + ); + } + + void _handleDragAssetEnter(TimelineAssetIndex index) { + if (_dragAnchorIndex == null || !_dragging) return; + + final timelineService = ref.read(timelineServiceProvider); + final dragAnchorIndex = _dragAnchorIndex!; + + // Calculate the range of assets to select + final startIndex = math.min(dragAnchorIndex.assetIndex, index.assetIndex); + final endIndex = math.max(dragAnchorIndex.assetIndex, index.assetIndex); + final count = endIndex - startIndex + 1; + + // Load the assets in the range + if (timelineService.hasRange(startIndex, count)) { + final selectedAssets = timelineService.getAssets(startIndex, count); + + // Clear previous drag selection and add new range + final multiSelectNotifier = ref.read(multiSelectProvider.notifier); + for (final asset in _draggedAssets) { + multiSelectNotifier.deselectAsset(asset); + } + _draggedAssets.clear(); + + for (final asset in selectedAssets) { + multiSelectNotifier.selectAsset(asset); + _draggedAssets.add(asset); + } + } + } + @override Widget build(BuildContext _) { final asyncSegments = ref.watch(timelineSegmentProvider); - final maxHeight = - ref.watch(timelineArgsProvider.select((args) => args.maxHeight)); - final isSelectionMode = ref.watch( - multiSelectProvider.select((s) => s.forceEnable), - ); + final maxHeight = ref.watch(timelineArgsProvider.select((args) => args.maxHeight)); + final isSelectionMode = ref.watch(multiSelectProvider.select((s) => s.forceEnable)); + final isMultiSelectEnabled = ref.watch(multiSelectProvider.select((s) => s.isEnabled)); - return asyncSegments.widgetWhen( - onData: (segments) { - final childCount = (segments.lastOrNull?.lastIndex ?? -1) + 1; - final double appBarExpandedHeight = - widget.appBar != null && widget.appBar is MesmerizingSliverAppBar - ? 200 - : 0; - final topPadding = context.padding.top + - (widget.appBar == null ? 0 : kToolbarHeight) + - 10; + return PopScope( + canPop: !isMultiSelectEnabled, + onPopInvokedWithResult: (_, __) { + if (isMultiSelectEnabled) { + ref.read(multiSelectProvider.notifier).reset(); + } + }, + child: asyncSegments.widgetWhen( + onData: (segments) { + final childCount = (segments.lastOrNull?.lastIndex ?? -1) + 1; + final double appBarExpandedHeight = widget.appBar != null && widget.appBar is MesmerizingSliverAppBar + ? 200 + : 0; + final topPadding = context.padding.top + (widget.appBar == null ? 0 : kToolbarHeight) + 10; - const scrubberBottomPadding = 100.0; - final bottomPadding = context.padding.bottom + - (widget.appBar == null ? 0 : scrubberBottomPadding); + const scrubberBottomPadding = 100.0; + final bottomPadding = context.padding.bottom + (widget.appBar == null ? 0 : scrubberBottomPadding); - return PrimaryScrollController( - controller: _scrollController, - child: Stack( - children: [ - Scrubber( - layoutSegments: segments, - timelineHeight: maxHeight, - topPadding: topPadding, - bottomPadding: bottomPadding, - monthSegmentSnappingOffset: - widget.topSliverWidgetHeight ?? 0 + appBarExpandedHeight, - child: CustomScrollView( - primary: true, - cacheExtent: maxHeight * 2, - slivers: [ - if (isSelectionMode) - const SelectionSliverAppBar() - else if (widget.appBar != null) - widget.appBar!, - if (widget.topSliverWidget != null) widget.topSliverWidget!, - _SliverSegmentedList( - segments: segments, - delegate: SliverChildBuilderDelegate( - (ctx, index) { - if (index >= childCount) return null; - final segment = segments.findByIndex(index); - return segment?.builder(ctx, index) ?? - const SizedBox.shrink(); - }, - childCount: childCount, - addAutomaticKeepAlives: false, - // We add repaint boundary around tiles, so skip the auto boundaries - addRepaintBoundaries: false, - ), - ), - const SliverPadding( - padding: EdgeInsets.only( - bottom: scrubberBottomPadding, + return PrimaryScrollController( + controller: _scrollController, + child: RawGestureDetector( + gestures: { + CustomScaleGestureRecognizer: GestureRecognizerFactoryWithHandlers( + () => CustomScaleGestureRecognizer(), + (CustomScaleGestureRecognizer scale) { + scale.onStart = (details) { + _baseScaleFactor = _scaleFactor; + }; + + scale.onUpdate = (details) { + final newScaleFactor = math.max(math.min(5.0, _baseScaleFactor * details.scale), 1.0); + final newPerRow = 7 - newScaleFactor.toInt(); + + if (newPerRow != _perRow) { + setState(() { + _scaleFactor = newScaleFactor; + _perRow = newPerRow; + }); + + ref.read(settingsProvider.notifier).set(Setting.tilesPerRow, _perRow); + } + }; + }, + ), + }, + child: TimelineDragRegion( + onStart: _setDragStartIndex, + onAssetEnter: _handleDragAssetEnter, + onEnd: _stopDrag, + onScroll: _dragScroll, + onScrollStart: () { + // Minimize the bottom sheet when drag selection starts + ref.read(timelineStateProvider.notifier).setScrolling(true); + }, + child: Stack( + children: [ + Scrubber( + layoutSegments: segments, + timelineHeight: maxHeight, + topPadding: topPadding, + bottomPadding: bottomPadding, + monthSegmentSnappingOffset: widget.topSliverWidgetHeight ?? 0 + appBarExpandedHeight, + child: CustomScrollView( + primary: true, + physics: _scrollPhysics, + cacheExtent: maxHeight * 2, + slivers: [ + if (isSelectionMode) + const SelectionSliverAppBar() + else if (widget.appBar != null) + widget.appBar!, + if (widget.topSliverWidget != null) widget.topSliverWidget!, + _SliverSegmentedList( + segments: segments, + delegate: SliverChildBuilderDelegate( + (ctx, index) { + if (index >= childCount) return null; + final segment = segments.findByIndex(index); + return segment?.builder(ctx, index) ?? const SizedBox.shrink(); + }, + childCount: childCount, + addAutomaticKeepAlives: false, + // We add repaint boundary around tiles, so skip the auto boundaries + addRepaintBoundaries: false, + ), + ), + const SliverPadding(padding: EdgeInsets.only(bottom: scrubberBottomPadding)), + ], ), ), + if (!isSelectionMode && isMultiSelectEnabled) ...[ + const Positioned(top: 60, left: 25, child: _MultiSelectStatusButton()), + if (widget.bottomSheet != null) widget.bottomSheet!, + ], ], ), ), - if (!isSelectionMode) ...[ - Consumer( - builder: (_, consumerRef, child) { - final isMultiSelectEnabled = consumerRef.watch( - multiSelectProvider.select( - (s) => s.isEnabled, - ), - ); - - if (isMultiSelectEnabled) { - return child!; - } - return const SizedBox.shrink(); - }, - child: const Positioned( - top: 60, - left: 25, - child: _MultiSelectStatusButton(), - ), - ), - if (widget.bottomSheet != null) - Consumer( - builder: (_, consumerRef, child) { - final isMultiSelectEnabled = consumerRef.watch( - multiSelectProvider.select( - (s) => s.isEnabled, - ), - ); - - if (isMultiSelectEnabled) { - return child!; - } - return const SizedBox.shrink(); - }, - child: widget.bottomSheet, - ), - ], - ], - ), - ); - }, + ), + ); + }, + ), ); } } @@ -227,23 +353,14 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> { class _SliverSegmentedList extends SliverMultiBoxAdaptorWidget { final List _segments; - const _SliverSegmentedList({ - required List segments, - required super.delegate, - }) : _segments = segments; + const _SliverSegmentedList({required List segments, required super.delegate}) : _segments = segments; @override _RenderSliverTimelineBoxAdaptor createRenderObject(BuildContext context) => - _RenderSliverTimelineBoxAdaptor( - childManager: context as SliverMultiBoxAdaptorElement, - segments: _segments, - ); + _RenderSliverTimelineBoxAdaptor(childManager: context as SliverMultiBoxAdaptorElement, segments: _segments); @override - void updateRenderObject( - BuildContext context, - _RenderSliverTimelineBoxAdaptor renderObject, - ) { + void updateRenderObject(BuildContext context, _RenderSliverTimelineBoxAdaptor renderObject) { renderObject.segments = _segments; } } @@ -260,23 +377,17 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor { markNeedsLayout(); } - _RenderSliverTimelineBoxAdaptor({ - required super.childManager, - required List segments, - }) : _segments = segments; + _RenderSliverTimelineBoxAdaptor({required super.childManager, required List segments}) + : _segments = segments; int getMinChildIndexForScrollOffset(double offset) => - _segments.findByOffset(offset)?.getMinChildIndexForScrollOffset(offset) ?? - 0; + _segments.findByOffset(offset)?.getMinChildIndexForScrollOffset(offset) ?? 0; int getMaxChildIndexForScrollOffset(double offset) => - _segments.findByOffset(offset)?.getMaxChildIndexForScrollOffset(offset) ?? - 0; + _segments.findByOffset(offset)?.getMaxChildIndexForScrollOffset(offset) ?? 0; double indexToLayoutOffset(int index) => - (_segments.findByIndex(index) ?? _segments.lastOrNull) - ?.indexToLayoutOffset(index) ?? - 0; + (_segments.findByIndex(index) ?? _segments.lastOrNull)?.indexToLayoutOffset(index) ?? 0; double estimateMaxScrollOffset() => _segments.lastOrNull?.endOffset ?? 0; @@ -288,8 +399,7 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor { // Assume initially that we have enough children to fill the viewport/cache area. childManager.setDidUnderflow(false); - final double scrollOffset = - constraints.scrollOffset + constraints.cacheOrigin; + final double scrollOffset = constraints.scrollOffset + constraints.cacheOrigin; assert(scrollOffset >= 0.0); final double remainingExtent = constraints.remainingCacheExtent; @@ -298,8 +408,7 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor { final double targetScrollOffset = scrollOffset + remainingExtent; // Find the index of the first child that should be visible or in the leading cache area. - final int firstRequiredChildIndex = - getMinChildIndexForScrollOffset(scrollOffset); + final int firstRequiredChildIndex = getMinChildIndexForScrollOffset(scrollOffset); // Find the index of the last child that should be visible or in the trailing cache area. final int? lastRequiredChildIndex = targetScrollOffset.isFinite @@ -310,8 +419,7 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor { if (firstChild == null) { collectGarbage(0, 0); } else { - final int leadingChildrenToRemove = - calculateLeadingGarbage(firstIndex: firstRequiredChildIndex); + final int leadingChildrenToRemove = calculateLeadingGarbage(firstIndex: firstRequiredChildIndex); final int trailingChildrenToRemove = lastRequiredChildIndex == null ? 0 : calculateTrailingGarbage(lastIndex: lastRequiredChildIndex); @@ -321,17 +429,12 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor { // If there are currently no children laid out (e.g., initial load), // try to add the first child needed for the current scroll offset. if (firstChild == null) { - final double firstChildLayoutOffset = - indexToLayoutOffset(firstRequiredChildIndex); - final bool childAdded = addInitialChild( - index: firstRequiredChildIndex, - layoutOffset: firstChildLayoutOffset, - ); + final double firstChildLayoutOffset = indexToLayoutOffset(firstRequiredChildIndex); + final bool childAdded = addInitialChild(index: firstRequiredChildIndex, layoutOffset: firstChildLayoutOffset); if (!childAdded) { // There are either no children, or we are past the end of all our children. - final double max = - firstRequiredChildIndex <= 0 ? 0.0 : computeMaxScrollOffset(); + final double max = firstRequiredChildIndex <= 0 ? 0.0 : computeMaxScrollOffset(); geometry = SliverGeometry(scrollExtent: max, maxPaintExtent: max); childManager.didFinishLayout(); return; @@ -342,26 +445,20 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor { RenderBox? highestLaidOutChild; final childConstraints = constraints.asBoxConstraints(); - for (int currentIndex = indexOf(firstChild!) - 1; - currentIndex >= firstRequiredChildIndex; - --currentIndex) { - final RenderBox? newLeadingChild = - insertAndLayoutLeadingChild(childConstraints); + for (int currentIndex = indexOf(firstChild!) - 1; currentIndex >= firstRequiredChildIndex; --currentIndex) { + final RenderBox? newLeadingChild = insertAndLayoutLeadingChild(childConstraints); if (newLeadingChild == null) { // If a child is missing where we expect one, it indicates // an inconsistency in offset that needs correction. - final Segment? segment = - _segments.findByIndex(currentIndex) ?? _segments.firstOrNull; + final Segment? segment = _segments.findByIndex(currentIndex) ?? _segments.firstOrNull; geometry = SliverGeometry( // Request a scroll correction based on where the missing child should have been. - scrollOffsetCorrection: - segment?.indexToLayoutOffset(currentIndex) ?? 0.0, + scrollOffsetCorrection: segment?.indexToLayoutOffset(currentIndex) ?? 0.0, ); // Parent will re-layout everything. return; } - final childParentData = - newLeadingChild.parentData! as SliverMultiBoxAdaptorParentData; + final childParentData = newLeadingChild.parentData! as SliverMultiBoxAdaptorParentData; childParentData.layoutOffset = indexToLayoutOffset(currentIndex); assert(childParentData.index == currentIndex); highestLaidOutChild ??= newLeadingChild; @@ -375,10 +472,8 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor { // The [firstChild] that existed at the start of performLayout is still the first one we need. if (highestLaidOutChild == null) { firstChild!.layout(childConstraints); - final childParentData = - firstChild!.parentData! as SliverMultiBoxAdaptorParentData; - childParentData.layoutOffset = - indexToLayoutOffset(firstRequiredChildIndex); + final childParentData = firstChild!.parentData! as SliverMultiBoxAdaptorParentData; + childParentData.layoutOffset = indexToLayoutOffset(firstRequiredChildIndex); highestLaidOutChild = firstChild; } @@ -388,23 +483,18 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor { // until we reach the [lastRequiredChildIndex] or run out of children. double calculatedMaxScrollOffset = double.infinity; - for (int currentIndex = indexOf(mostRecentlyLaidOutChild!) + 1; - lastRequiredChildIndex == null || - currentIndex <= lastRequiredChildIndex; - ++currentIndex) { + for ( + int currentIndex = indexOf(mostRecentlyLaidOutChild!) + 1; + lastRequiredChildIndex == null || currentIndex <= lastRequiredChildIndex; + ++currentIndex + ) { RenderBox? child = childAfter(mostRecentlyLaidOutChild!); if (child == null || indexOf(child) != currentIndex) { - child = insertAndLayoutChild( - childConstraints, - after: mostRecentlyLaidOutChild, - ); + child = insertAndLayoutChild(childConstraints, after: mostRecentlyLaidOutChild); if (child == null) { - final Segment? segment = - _segments.findByIndex(currentIndex) ?? _segments.lastOrNull; - calculatedMaxScrollOffset = - segment?.indexToLayoutOffset(currentIndex) ?? - computeMaxScrollOffset(); + final Segment? segment = _segments.findByIndex(currentIndex) ?? _segments.lastOrNull; + calculatedMaxScrollOffset = segment?.indexToLayoutOffset(currentIndex) ?? computeMaxScrollOffset(); break; } } else { @@ -412,49 +502,30 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor { } mostRecentlyLaidOutChild = child; - final childParentData = mostRecentlyLaidOutChild.parentData! - as SliverMultiBoxAdaptorParentData; + final childParentData = mostRecentlyLaidOutChild.parentData! as SliverMultiBoxAdaptorParentData; assert(childParentData.index == currentIndex); childParentData.layoutOffset = indexToLayoutOffset(currentIndex); } final int lastLaidOutChildIndex = indexOf(lastChild!); - final double leadingScrollOffset = - indexToLayoutOffset(firstRequiredChildIndex); - final double trailingScrollOffset = - indexToLayoutOffset(lastLaidOutChildIndex + 1); + final double leadingScrollOffset = indexToLayoutOffset(firstRequiredChildIndex); + final double trailingScrollOffset = indexToLayoutOffset(lastLaidOutChildIndex + 1); assert( firstRequiredChildIndex == 0 || - (childScrollOffset(firstChild!) ?? -1.0) - scrollOffset <= - precisionErrorTolerance, + (childScrollOffset(firstChild!) ?? -1.0) - scrollOffset <= precisionErrorTolerance, ); assert(debugAssertChildListIsNonEmptyAndContiguous()); assert(indexOf(firstChild!) == firstRequiredChildIndex); - assert( - lastRequiredChildIndex == null || - lastLaidOutChildIndex <= lastRequiredChildIndex, - ); + assert(lastRequiredChildIndex == null || lastLaidOutChildIndex <= lastRequiredChildIndex); - calculatedMaxScrollOffset = math.min( - calculatedMaxScrollOffset, - estimateMaxScrollOffset(), - ); + calculatedMaxScrollOffset = math.min(calculatedMaxScrollOffset, estimateMaxScrollOffset()); - final double paintExtent = calculatePaintOffset( - constraints, - from: leadingScrollOffset, - to: trailingScrollOffset, - ); + final double paintExtent = calculatePaintOffset(constraints, from: leadingScrollOffset, to: trailingScrollOffset); - final double cacheExtent = calculateCacheOffset( - constraints, - from: leadingScrollOffset, - to: trailingScrollOffset, - ); + final double cacheExtent = calculateCacheOffset(constraints, from: leadingScrollOffset, to: trailingScrollOffset); - final double targetEndScrollOffsetForPaint = - constraints.scrollOffset + constraints.remainingPaintExtent; + final double targetEndScrollOffsetForPaint = constraints.scrollOffset + constraints.remainingPaintExtent; final int? targetLastIndexForPaint = targetEndScrollOffsetForPaint.isFinite ? getMaxChildIndexForScrollOffset(targetEndScrollOffsetForPaint) : null; @@ -468,8 +539,8 @@ class _RenderSliverTimelineBoxAdaptor extends RenderSliverMultiBoxAdaptor { // Indicates if there's content scrolled off-screen. // This is true if the last child needed for painting is actually laid out, // or if the first child is partially visible. - hasVisualOverflow: (targetLastIndexForPaint != null && - lastLaidOutChildIndex >= targetLastIndexForPaint) || + hasVisualOverflow: + (targetLastIndexForPaint != null && lastLaidOutChildIndex >= targetLastIndexForPaint) || constraints.scrollOffset > 0.0, cacheExtent: cacheExtent, ); @@ -489,21 +560,22 @@ class _MultiSelectStatusButton extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final selectCount = - ref.watch(multiSelectProvider.select((s) => s.selectedAssets.length)); + final selectCount = ref.watch(multiSelectProvider.select((s) => s.selectedAssets.length)); return ElevatedButton.icon( onPressed: () => ref.read(multiSelectProvider.notifier).reset(), - icon: Icon( - Icons.close_rounded, - color: context.colorScheme.onPrimary, - ), + icon: Icon(Icons.close_rounded, color: context.colorScheme.onPrimary), label: Text( selectCount.toString(), - style: context.textTheme.titleMedium?.copyWith( - height: 2.5, - color: context.colorScheme.onPrimary, - ), + style: context.textTheme.titleMedium?.copyWith(height: 2.5, color: context.colorScheme.onPrimary), ), ); } } + +/// accepts a gesture even though it should reject it (because child won) +class CustomScaleGestureRecognizer extends ScaleGestureRecognizer { + @override + void rejectGesture(int pointer) { + acceptGesture(pointer); + } +} diff --git a/mobile/lib/presentation/widgets/timeline/timeline_drag_region.dart b/mobile/lib/presentation/widgets/timeline/timeline_drag_region.dart new file mode 100644 index 0000000000..88d46b143f --- /dev/null +++ b/mobile/lib/presentation/widgets/timeline/timeline_drag_region.dart @@ -0,0 +1,212 @@ +import 'dart:async'; + +import 'package:collection/collection.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; + +class TimelineDragRegion extends StatefulWidget { + final Widget child; + + final void Function(TimelineAssetIndex valueKey)? onStart; + final void Function(TimelineAssetIndex valueKey)? onAssetEnter; + final void Function()? onEnd; + final void Function()? onScrollStart; + final void Function(ScrollDirection direction)? onScroll; + + const TimelineDragRegion({ + super.key, + required this.child, + this.onStart, + this.onAssetEnter, + this.onEnd, + this.onScrollStart, + this.onScroll, + }); + + @override + State createState() => _TimelineDragRegionState(); +} + +class _TimelineDragRegionState extends State { + late TimelineAssetIndex? assetUnderPointer; + late TimelineAssetIndex? anchorAsset; + + // Scroll related state + static const double scrollOffset = 0.10; + double? topScrollOffset; + double? bottomScrollOffset; + Timer? scrollTimer; + late bool scrollNotified; + + @override + void initState() { + super.initState(); + assetUnderPointer = null; + anchorAsset = null; + scrollNotified = false; + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + topScrollOffset = null; + bottomScrollOffset = null; + } + + @override + void dispose() { + scrollTimer?.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return RawGestureDetector( + gestures: { + _CustomLongPressGestureRecognizer: GestureRecognizerFactoryWithHandlers<_CustomLongPressGestureRecognizer>( + () => _CustomLongPressGestureRecognizer(), + _registerCallbacks, + ), + }, + child: widget.child, + ); + } + + void _registerCallbacks(_CustomLongPressGestureRecognizer recognizer) { + recognizer.onLongPressMoveUpdate = (details) => _onLongPressMove(details); + recognizer.onLongPressStart = (details) => _onLongPressStart(details); + recognizer.onLongPressUp = _onLongPressEnd; + } + + TimelineAssetIndex? _getValueKeyAtPosition(Offset position) { + final box = context.findAncestorRenderObjectOfType(); + if (box == null) return null; + + final hitTestResult = BoxHitTestResult(); + final local = box.globalToLocal(position); + if (!box.hitTest(hitTestResult, position: local)) return null; + + return (hitTestResult.path.firstWhereOrNull((hit) => hit.target is _TimelineAssetIndexProxy)?.target + as _TimelineAssetIndexProxy?) + ?.index; + } + + void _onLongPressStart(LongPressStartDetails event) { + /// Calculate widget height and scroll offset when long press starting instead of in [initState] + /// or [didChangeDependencies] as the grid might still be rendering into view to get the actual size + final height = context.size?.height; + if (height != null && (topScrollOffset == null || bottomScrollOffset == null)) { + topScrollOffset = height * scrollOffset; + bottomScrollOffset = height - topScrollOffset!; + } + + final initialHit = _getValueKeyAtPosition(event.globalPosition); + anchorAsset = initialHit; + if (initialHit == null) return; + + if (anchorAsset != null) { + widget.onStart?.call(anchorAsset!); + } + } + + void _onLongPressEnd() { + scrollNotified = false; + scrollTimer?.cancel(); + widget.onEnd?.call(); + } + + void _onLongPressMove(LongPressMoveUpdateDetails event) { + if (anchorAsset == null) return; + if (topScrollOffset == null || bottomScrollOffset == null) return; + + final currentDy = event.localPosition.dy; + + if (currentDy > bottomScrollOffset!) { + scrollTimer ??= Timer.periodic( + const Duration(milliseconds: 50), + (_) => widget.onScroll?.call(ScrollDirection.forward), + ); + } else if (currentDy < topScrollOffset!) { + scrollTimer ??= Timer.periodic( + const Duration(milliseconds: 50), + (_) => widget.onScroll?.call(ScrollDirection.reverse), + ); + } else { + scrollTimer?.cancel(); + scrollTimer = null; + } + + final currentlyTouchingAsset = _getValueKeyAtPosition(event.globalPosition); + if (currentlyTouchingAsset == null) return; + + if (assetUnderPointer != currentlyTouchingAsset) { + if (!scrollNotified) { + scrollNotified = true; + widget.onScrollStart?.call(); + } + + widget.onAssetEnter?.call(currentlyTouchingAsset); + assetUnderPointer = currentlyTouchingAsset; + } + } +} + +class _CustomLongPressGestureRecognizer extends LongPressGestureRecognizer { + @override + void rejectGesture(int pointer) { + acceptGesture(pointer); + } +} + +class TimelineAssetIndexWrapper extends SingleChildRenderObjectWidget { + final int assetIndex; + final int segmentIndex; + + const TimelineAssetIndexWrapper({ + required Widget super.child, + required this.assetIndex, + required this.segmentIndex, + super.key, + }); + + @override + // ignore: library_private_types_in_public_api + _TimelineAssetIndexProxy createRenderObject(BuildContext context) { + return _TimelineAssetIndexProxy( + index: TimelineAssetIndex(assetIndex: assetIndex, segmentIndex: segmentIndex), + ); + } + + @override + void updateRenderObject( + BuildContext context, + // ignore: library_private_types_in_public_api + _TimelineAssetIndexProxy renderObject, + ) { + renderObject.index = TimelineAssetIndex(assetIndex: assetIndex, segmentIndex: segmentIndex); + } +} + +class _TimelineAssetIndexProxy extends RenderProxyBox { + TimelineAssetIndex index; + + _TimelineAssetIndexProxy({required this.index}); +} + +class TimelineAssetIndex { + final int assetIndex; + final int segmentIndex; + + const TimelineAssetIndex({required this.assetIndex, required this.segmentIndex}); + + @override + bool operator ==(covariant TimelineAssetIndex other) { + if (identical(this, other)) return true; + + return other.assetIndex == assetIndex && other.segmentIndex == segmentIndex; + } + + @override + int get hashCode => assetIndex.hashCode ^ segmentIndex.hashCode; +} diff --git a/mobile/lib/providers/activity.provider.dart b/mobile/lib/providers/activity.provider.dart index 0dcc99320b..a867a5a281 100644 --- a/mobile/lib/providers/activity.provider.dart +++ b/mobile/lib/providers/activity.provider.dart @@ -11,9 +11,7 @@ part 'activity.provider.g.dart'; class AlbumActivity extends _$AlbumActivity { @override Future> build(String albumId, [String? assetId]) async { - return ref - .watch(activityServiceProvider) - .getAllActivities(albumId, assetId: assetId); + return ref.watch(activityServiceProvider).getAllActivities(albumId, assetId: assetId); } Future removeActivity(String id) async { @@ -24,17 +22,13 @@ class AlbumActivity extends _$AlbumActivity { state = AsyncData(activities); // Decrement activity count only for comments if (removedActivity.type == ActivityType.comment) { - ref - .watch(activityStatisticsProvider(albumId, assetId).notifier) - .removeActivity(); + ref.watch(activityStatisticsProvider(albumId, assetId).notifier).removeActivity(); } } } Future addLike() async { - final activity = await ref - .watch(activityServiceProvider) - .addActivity(albumId, ActivityType.like, assetId: assetId); + final activity = await ref.watch(activityServiceProvider).addActivity(albumId, ActivityType.like, assetId: assetId); if (activity.hasValue) { final activities = state.asData?.value ?? []; state = AsyncData([...activities, activity.requireValue]); @@ -42,19 +36,14 @@ class AlbumActivity extends _$AlbumActivity { } Future addComment(String comment) async { - final activity = await ref.watch(activityServiceProvider).addActivity( - albumId, - ActivityType.comment, - assetId: assetId, - comment: comment, - ); + final activity = await ref + .watch(activityServiceProvider) + .addActivity(albumId, ActivityType.comment, assetId: assetId, comment: comment); if (activity.hasValue) { final activities = state.valueOrNull ?? []; state = AsyncData([...activities, activity.requireValue]); - ref - .watch(activityStatisticsProvider(albumId, assetId).notifier) - .addActivity(); + ref.watch(activityStatisticsProvider(albumId, assetId).notifier).addActivity(); // The previous addActivity call would increase the count of an asset if assetId != null // To also increase the activity count of the album, calling it once again with assetId set to null if (assetId != null) { diff --git a/mobile/lib/providers/activity.provider.g.dart b/mobile/lib/providers/activity.provider.g.dart index af574b991a..dc927795f8 100644 --- a/mobile/lib/providers/activity.provider.g.dart +++ b/mobile/lib/providers/activity.provider.g.dart @@ -34,10 +34,7 @@ abstract class _$AlbumActivity late final String albumId; late final String? assetId; - FutureOr> build( - String albumId, [ - String? assetId, - ]); + FutureOr> build(String albumId, [String? assetId]); } /// Maintains the current list of all activities for @@ -58,24 +55,15 @@ class AlbumActivityFamily extends Family>> { /// Maintains the current list of all activities for /// /// Copied from [AlbumActivity]. - AlbumActivityProvider call( - String albumId, [ - String? assetId, - ]) { - return AlbumActivityProvider( - albumId, - assetId, - ); + AlbumActivityProvider call(String albumId, [String? assetId]) { + return AlbumActivityProvider(albumId, assetId); } @override AlbumActivityProvider getProviderOverride( covariant AlbumActivityProvider provider, ) { - return call( - provider.albumId, - provider.assetId, - ); + return call(provider.albumId, provider.assetId); } static const Iterable? _dependencies = null; @@ -96,30 +84,28 @@ class AlbumActivityFamily extends Family>> { /// Maintains the current list of all activities for /// /// Copied from [AlbumActivity]. -class AlbumActivityProvider extends AutoDisposeAsyncNotifierProviderImpl< - AlbumActivity, List> { +class AlbumActivityProvider + extends + AutoDisposeAsyncNotifierProviderImpl> { /// Maintains the current list of all activities for /// /// Copied from [AlbumActivity]. - AlbumActivityProvider( - String albumId, [ - String? assetId, - ]) : this._internal( - () => AlbumActivity() - ..albumId = albumId - ..assetId = assetId, - from: albumActivityProvider, - name: r'albumActivityProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$albumActivityHash, - dependencies: AlbumActivityFamily._dependencies, - allTransitiveDependencies: - AlbumActivityFamily._allTransitiveDependencies, - albumId: albumId, - assetId: assetId, - ); + AlbumActivityProvider(String albumId, [String? assetId]) + : this._internal( + () => AlbumActivity() + ..albumId = albumId + ..assetId = assetId, + from: albumActivityProvider, + name: r'albumActivityProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$albumActivityHash, + dependencies: AlbumActivityFamily._dependencies, + allTransitiveDependencies: + AlbumActivityFamily._allTransitiveDependencies, + albumId: albumId, + assetId: assetId, + ); AlbumActivityProvider._internal( super._createNotifier, { @@ -136,13 +122,8 @@ class AlbumActivityProvider extends AutoDisposeAsyncNotifierProviderImpl< final String? assetId; @override - FutureOr> runNotifierBuild( - covariant AlbumActivity notifier, - ) { - return notifier.build( - albumId, - assetId, - ); + FutureOr> runNotifierBuild(covariant AlbumActivity notifier) { + return notifier.build(albumId, assetId); } @override @@ -166,7 +147,7 @@ class AlbumActivityProvider extends AutoDisposeAsyncNotifierProviderImpl< @override AutoDisposeAsyncNotifierProviderElement> - createElement() { + createElement() { return _AlbumActivityProviderElement(this); } @@ -198,8 +179,9 @@ mixin AlbumActivityRef on AutoDisposeAsyncNotifierProviderRef> { } class _AlbumActivityProviderElement - extends AutoDisposeAsyncNotifierProviderElement> with AlbumActivityRef { + extends + AutoDisposeAsyncNotifierProviderElement> + with AlbumActivityRef { _AlbumActivityProviderElement(super.provider); @override @@ -207,5 +189,6 @@ class _AlbumActivityProviderElement @override String? get assetId => (origin as AlbumActivityProvider).assetId; } + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/mobile/lib/providers/activity_service.provider.dart b/mobile/lib/providers/activity_service.provider.dart index 2d63e55354..a7fd0715f8 100644 --- a/mobile/lib/providers/activity_service.provider.dart +++ b/mobile/lib/providers/activity_service.provider.dart @@ -6,5 +6,4 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'activity_service.provider.g.dart'; @riverpod -ActivityService activityService(Ref ref) => - ActivityService(ref.watch(activityApiRepositoryProvider)); +ActivityService activityService(Ref ref) => ActivityService(ref.watch(activityApiRepositoryProvider)); diff --git a/mobile/lib/providers/activity_statistics.provider.dart b/mobile/lib/providers/activity_statistics.provider.dart index c260a7a547..96d2633d1b 100644 --- a/mobile/lib/providers/activity_statistics.provider.dart +++ b/mobile/lib/providers/activity_statistics.provider.dart @@ -9,10 +9,7 @@ part 'activity_statistics.provider.g.dart'; class ActivityStatistics extends _$ActivityStatistics { @override int build(String albumId, [String? assetId]) { - ref - .watch(activityServiceProvider) - .getStatistics(albumId, assetId: assetId) - .then((stats) => state = stats.comments); + ref.watch(activityServiceProvider).getStatistics(albumId, assetId: assetId).then((stats) => state = stats.comments); return 0; } diff --git a/mobile/lib/providers/activity_statistics.provider.g.dart b/mobile/lib/providers/activity_statistics.provider.g.dart index d2de32c0aa..83d887f6dc 100644 --- a/mobile/lib/providers/activity_statistics.provider.g.dart +++ b/mobile/lib/providers/activity_statistics.provider.g.dart @@ -34,10 +34,7 @@ abstract class _$ActivityStatistics extends BuildlessAutoDisposeNotifier { late final String albumId; late final String? assetId; - int build( - String albumId, [ - String? assetId, - ]); + int build(String albumId, [String? assetId]); } /// Maintains the current number of comments by @@ -58,24 +55,15 @@ class ActivityStatisticsFamily extends Family { /// Maintains the current number of comments by /// /// Copied from [ActivityStatistics]. - ActivityStatisticsProvider call( - String albumId, [ - String? assetId, - ]) { - return ActivityStatisticsProvider( - albumId, - assetId, - ); + ActivityStatisticsProvider call(String albumId, [String? assetId]) { + return ActivityStatisticsProvider(albumId, assetId); } @override ActivityStatisticsProvider getProviderOverride( covariant ActivityStatisticsProvider provider, ) { - return call( - provider.albumId, - provider.assetId, - ); + return call(provider.albumId, provider.assetId); } static const Iterable? _dependencies = null; @@ -101,25 +89,22 @@ class ActivityStatisticsProvider /// Maintains the current number of comments by /// /// Copied from [ActivityStatistics]. - ActivityStatisticsProvider( - String albumId, [ - String? assetId, - ]) : this._internal( - () => ActivityStatistics() - ..albumId = albumId - ..assetId = assetId, - from: activityStatisticsProvider, - name: r'activityStatisticsProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$activityStatisticsHash, - dependencies: ActivityStatisticsFamily._dependencies, - allTransitiveDependencies: - ActivityStatisticsFamily._allTransitiveDependencies, - albumId: albumId, - assetId: assetId, - ); + ActivityStatisticsProvider(String albumId, [String? assetId]) + : this._internal( + () => ActivityStatistics() + ..albumId = albumId + ..assetId = assetId, + from: activityStatisticsProvider, + name: r'activityStatisticsProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$activityStatisticsHash, + dependencies: ActivityStatisticsFamily._dependencies, + allTransitiveDependencies: + ActivityStatisticsFamily._allTransitiveDependencies, + albumId: albumId, + assetId: assetId, + ); ActivityStatisticsProvider._internal( super._createNotifier, { @@ -136,13 +121,8 @@ class ActivityStatisticsProvider final String? assetId; @override - int runNotifierBuild( - covariant ActivityStatistics notifier, - ) { - return notifier.build( - albumId, - assetId, - ); + int runNotifierBuild(covariant ActivityStatistics notifier) { + return notifier.build(albumId, assetId); } @override @@ -206,5 +186,6 @@ class _ActivityStatisticsProviderElement @override String? get assetId => (origin as ActivityStatisticsProvider).assetId; } + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/mobile/lib/providers/album/album.provider.dart b/mobile/lib/providers/album/album.provider.dart index 39f5af7344..35634d77c8 100644 --- a/mobile/lib/providers/album/album.provider.dart +++ b/mobile/lib/providers/album/album.provider.dart @@ -18,8 +18,7 @@ class AlbumNotifier extends StateNotifier> { } }); - _streamSub = - albumService.watchRemoteAlbums().listen((data) => state = data); + _streamSub = albumService.watchRemoteAlbums().listen((data) => state = data); } final AlbumService albumService; @@ -36,31 +35,15 @@ class AlbumNotifier extends StateNotifier> { Future deleteAlbum(Album album) => albumService.deleteAlbum(album); - Future createAlbum( - String albumTitle, - Set assets, - ) => - albumService.createAlbum(albumTitle, assets, []); + Future createAlbum(String albumTitle, Set assets) => albumService.createAlbum(albumTitle, assets, []); - Future getAlbumByName( - String albumName, { - bool? remote, - bool? shared, - bool? owner, - }) => - albumService.getAlbumByName( - albumName, - remote: remote, - shared: shared, - owner: owner, - ); + Future getAlbumByName(String albumName, {bool? remote, bool? shared, bool? owner}) => + albumService.getAlbumByName(albumName, remote: remote, shared: shared, owner: owner); /// Create an album on the server with the same name as the selected album for backup /// First this will check if the album already exists on the server with name /// If it does not exist, it will create the album on the server - Future createSyncAlbum( - String albumName, - ) async { + Future createSyncAlbum(String albumName) async { final album = await getAlbumByName(albumName, remote: true, owner: true); if (album != null) { return; @@ -106,16 +89,12 @@ class AlbumNotifier extends StateNotifier> { return await albumService.removeAsset(album, assets); } - Future setActivitystatus( - Album album, - bool enabled, - ) { + Future setActivitystatus(Album album, bool enabled) { return albumService.setActivityStatus(album, enabled); } Future toggleSortOrder(Album album) { - final order = - album.sortOrder == SortOrder.asc ? SortOrder.desc : SortOrder.asc; + final order = album.sortOrder == SortOrder.asc ? SortOrder.desc : SortOrder.asc; return albumService.updateSortOrder(album, order); } @@ -127,16 +106,11 @@ class AlbumNotifier extends StateNotifier> { } } -final albumProvider = - StateNotifierProvider.autoDispose>((ref) { - return AlbumNotifier( - ref.watch(albumServiceProvider), - ref, - ); +final albumProvider = StateNotifierProvider.autoDispose>((ref) { + return AlbumNotifier(ref.watch(albumServiceProvider), ref); }); -final albumWatcher = - StreamProvider.autoDispose.family((ref, id) async* { +final albumWatcher = StreamProvider.autoDispose.family((ref, id) async* { final albumService = ref.watch(albumServiceProvider); final album = await albumService.getAlbumById(id); @@ -172,7 +146,6 @@ class LocalAlbumsNotifier extends StateNotifier> { } } -final localAlbumsProvider = - StateNotifierProvider.autoDispose>((ref) { +final localAlbumsProvider = StateNotifierProvider.autoDispose>((ref) { return LocalAlbumsNotifier(ref.watch(albumServiceProvider)); }); diff --git a/mobile/lib/providers/album/album_sort_by_options.provider.dart b/mobile/lib/providers/album/album_sort_by_options.provider.dart index c89cd43132..3dd09f1282 100644 --- a/mobile/lib/providers/album/album_sort_by_options.provider.dart +++ b/mobile/lib/providers/album/album_sort_by_options.provider.dart @@ -31,8 +31,7 @@ class _AlbumSortHandlers { static const AlbumSortFn assetCount = _sortByAssetCount; static List _sortByAssetCount(List albums, bool isReverse) { - final sorted = - albums.sorted((a, b) => a.assetCount.compareTo(b.assetCount)); + final sorted = albums.sorted((a, b) => a.assetCount.compareTo(b.assetCount)); return (isReverse ? sorted.reversed : sorted).toList(); } @@ -76,22 +75,10 @@ class _AlbumSortHandlers { enum AlbumSortMode { title(1, "library_page_sort_title", _AlbumSortHandlers.title), assetCount(4, "library_page_sort_asset_count", _AlbumSortHandlers.assetCount), - lastModified( - 3, - "library_page_sort_last_modified", - _AlbumSortHandlers.lastModified, - ), + lastModified(3, "library_page_sort_last_modified", _AlbumSortHandlers.lastModified), created(0, "library_page_sort_created", _AlbumSortHandlers.created), - mostRecent( - 2, - "sort_recent", - _AlbumSortHandlers.mostRecent, - ), - mostOldest( - 5, - "sort_oldest", - _AlbumSortHandlers.mostOldest, - ); + mostRecent(2, "sort_recent", _AlbumSortHandlers.mostRecent), + mostOldest(5, "sort_oldest", _AlbumSortHandlers.mostOldest); final int storeIndex; final String label; @@ -104,21 +91,13 @@ enum AlbumSortMode { class AlbumSortByOptions extends _$AlbumSortByOptions { @override AlbumSortMode build() { - final sortOpt = ref - .watch(appSettingsServiceProvider) - .getSetting(AppSettingsEnum.selectedAlbumSortOrder); - return AlbumSortMode.values.firstWhere( - (e) => e.storeIndex == sortOpt, - orElse: () => AlbumSortMode.title, - ); + final sortOpt = ref.watch(appSettingsServiceProvider).getSetting(AppSettingsEnum.selectedAlbumSortOrder); + return AlbumSortMode.values.firstWhere((e) => e.storeIndex == sortOpt, orElse: () => AlbumSortMode.title); } void changeSortMode(AlbumSortMode sortOption) { state = sortOption; - ref.watch(appSettingsServiceProvider).setSetting( - AppSettingsEnum.selectedAlbumSortOrder, - sortOption.storeIndex, - ); + ref.watch(appSettingsServiceProvider).setSetting(AppSettingsEnum.selectedAlbumSortOrder, sortOption.storeIndex); } } @@ -126,15 +105,11 @@ class AlbumSortByOptions extends _$AlbumSortByOptions { class AlbumSortOrder extends _$AlbumSortOrder { @override bool build() { - return ref - .watch(appSettingsServiceProvider) - .getSetting(AppSettingsEnum.selectedAlbumSortReverse); + return ref.watch(appSettingsServiceProvider).getSetting(AppSettingsEnum.selectedAlbumSortReverse); } void changeSortDirection(bool isReverse) { state = isReverse; - ref - .watch(appSettingsServiceProvider) - .setSetting(AppSettingsEnum.selectedAlbumSortReverse, isReverse); + ref.watch(appSettingsServiceProvider).setSetting(AppSettingsEnum.selectedAlbumSortReverse, isReverse); } } diff --git a/mobile/lib/providers/album/album_sort_by_options.provider.g.dart b/mobile/lib/providers/album/album_sort_by_options.provider.g.dart index ba20e7eb66..750329c9d5 100644 --- a/mobile/lib/providers/album/album_sort_by_options.provider.g.dart +++ b/mobile/lib/providers/album/album_sort_by_options.provider.g.dart @@ -13,14 +13,14 @@ String _$albumSortByOptionsHash() => @ProviderFor(AlbumSortByOptions) final albumSortByOptionsProvider = AutoDisposeNotifierProvider.internal( - AlbumSortByOptions.new, - name: r'albumSortByOptionsProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$albumSortByOptionsHash, - dependencies: null, - allTransitiveDependencies: null, -); + AlbumSortByOptions.new, + name: r'albumSortByOptionsProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$albumSortByOptionsHash, + dependencies: null, + allTransitiveDependencies: null, + ); typedef _$AlbumSortByOptions = AutoDisposeNotifier; String _$albumSortOrderHash() => r'573dea45b4519e69386fc7104c72522e35713440'; @@ -29,14 +29,14 @@ String _$albumSortOrderHash() => r'573dea45b4519e69386fc7104c72522e35713440'; @ProviderFor(AlbumSortOrder) final albumSortOrderProvider = AutoDisposeNotifierProvider.internal( - AlbumSortOrder.new, - name: r'albumSortOrderProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$albumSortOrderHash, - dependencies: null, - allTransitiveDependencies: null, -); + AlbumSortOrder.new, + name: r'albumSortOrderProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$albumSortOrderHash, + dependencies: null, + allTransitiveDependencies: null, + ); typedef _$AlbumSortOrder = AutoDisposeNotifier; // ignore_for_file: type=lint diff --git a/mobile/lib/providers/album/album_title.provider.dart b/mobile/lib/providers/album/album_title.provider.dart index 126b3499a3..bf812a01d8 100644 --- a/mobile/lib/providers/album/album_title.provider.dart +++ b/mobile/lib/providers/album/album_title.provider.dart @@ -12,6 +12,4 @@ class AlbumTitleNotifier extends StateNotifier { } } -final albumTitleProvider = StateNotifierProvider( - (ref) => AlbumTitleNotifier(), -); +final albumTitleProvider = StateNotifierProvider((ref) => AlbumTitleNotifier()); diff --git a/mobile/lib/providers/album/album_viewer.provider.dart b/mobile/lib/providers/album/album_viewer.provider.dart index d1290df298..f4ce047464 100644 --- a/mobile/lib/providers/album/album_viewer.provider.dart +++ b/mobile/lib/providers/album/album_viewer.provider.dart @@ -5,13 +5,7 @@ import 'package:immich_mobile/services/album.service.dart'; class AlbumViewerNotifier extends StateNotifier { AlbumViewerNotifier(this.ref) - : super( - const AlbumViewerPageState( - editTitleText: "", - isEditAlbum: false, - editDescriptionText: "", - ), - ); + : super(const AlbumViewerPageState(editTitleText: "", isEditAlbum: false, editDescriptionText: "")); final Ref ref; @@ -40,17 +34,10 @@ class AlbumViewerNotifier extends StateNotifier { } void resetState() { - state = state.copyWith( - editTitleText: "", - isEditAlbum: false, - editDescriptionText: "", - ); + state = state.copyWith(editTitleText: "", isEditAlbum: false, editDescriptionText: ""); } - Future changeAlbumTitle( - Album album, - String newAlbumTitle, - ) async { + Future changeAlbumTitle(Album album, String newAlbumTitle) async { AlbumService service = ref.watch(albumServiceProvider); bool isSuccess = await service.changeTitleAlbum(album, newAlbumTitle); @@ -65,16 +52,10 @@ class AlbumViewerNotifier extends StateNotifier { return false; } - Future changeAlbumDescription( - Album album, - String newAlbumDescription, - ) async { + Future changeAlbumDescription(Album album, String newAlbumDescription) async { AlbumService service = ref.watch(albumServiceProvider); - bool isSuccess = await service.changeDescriptionAlbum( - album, - newAlbumDescription, - ); + bool isSuccess = await service.changeDescriptionAlbum(album, newAlbumDescription); if (isSuccess) { state = state.copyWith(editDescriptionText: "", isEditAlbum: false); @@ -88,7 +69,6 @@ class AlbumViewerNotifier extends StateNotifier { } } -final albumViewerProvider = - StateNotifierProvider((ref) { +final albumViewerProvider = StateNotifierProvider((ref) { return AlbumViewerNotifier(ref); }); diff --git a/mobile/lib/providers/album/current_album.provider.g.dart b/mobile/lib/providers/album/current_album.provider.g.dart index 60ebe3e333..b6d079231f 100644 --- a/mobile/lib/providers/album/current_album.provider.g.dart +++ b/mobile/lib/providers/album/current_album.provider.g.dart @@ -12,13 +12,14 @@ String _$currentAlbumHash() => r'61f00273d6b69da45add1532cc3d3a076ee55110'; @ProviderFor(CurrentAlbum) final currentAlbumProvider = AutoDisposeNotifierProvider.internal( - CurrentAlbum.new, - name: r'currentAlbumProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$currentAlbumHash, - dependencies: null, - allTransitiveDependencies: null, -); + CurrentAlbum.new, + name: r'currentAlbumProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$currentAlbumHash, + dependencies: null, + allTransitiveDependencies: null, + ); typedef _$CurrentAlbum = AutoDisposeNotifier; // ignore_for_file: type=lint diff --git a/mobile/lib/providers/album/suggested_shared_users.provider.dart b/mobile/lib/providers/album/suggested_shared_users.provider.dart index 3c8dcb6733..51146748c7 100644 --- a/mobile/lib/providers/album/suggested_shared_users.provider.dart +++ b/mobile/lib/providers/album/suggested_shared_users.provider.dart @@ -4,8 +4,7 @@ import 'package:immich_mobile/domain/services/user.service.dart'; import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; -final otherUsersProvider = - FutureProvider.autoDispose>((ref) async { +final otherUsersProvider = FutureProvider.autoDispose>((ref) async { UserService userService = ref.watch(userServiceProvider); final currentUser = ref.watch(currentUserProvider); diff --git a/mobile/lib/providers/api.provider.g.dart b/mobile/lib/providers/api.provider.g.dart index 8a6f514ce6..ee1781c24c 100644 --- a/mobile/lib/providers/api.provider.g.dart +++ b/mobile/lib/providers/api.provider.g.dart @@ -13,8 +13,9 @@ String _$apiServiceHash() => r'187a7de59b064fab1104c23717f18ce0ae3e426c'; final apiServiceProvider = Provider.internal( apiService, name: r'apiServiceProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$apiServiceHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$apiServiceHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/mobile/lib/providers/app_life_cycle.provider.dart b/mobile/lib/providers/app_life_cycle.provider.dart index 997058d763..31342bf23e 100644 --- a/mobile/lib/providers/app_life_cycle.provider.dart +++ b/mobile/lib/providers/app_life_cycle.provider.dart @@ -6,10 +6,12 @@ import 'package:immich_mobile/domain/services/log.service.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/models/backup/backup_state.model.dart'; import 'package:immich_mobile/providers/album/album.provider.dart'; +import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/providers/asset.provider.dart'; import 'package:immich_mobile/providers/auth.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/drift_backup.provider.dart'; import 'package:immich_mobile/providers/backup/ios_background_settings.provider.dart'; import 'package:immich_mobile/providers/backup/manual_upload.provider.dart'; import 'package:immich_mobile/providers/gallery_permission.provider.dart'; @@ -17,20 +19,15 @@ import 'package:immich_mobile/providers/memory.provider.dart'; import 'package:immich_mobile/providers/notification_permission.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/providers/tab.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/providers/websocket.provider.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/services/background.service.dart'; import 'package:isar/isar.dart'; import 'package:logging/logging.dart'; import 'package:permission_handler/permission_handler.dart'; -enum AppLifeCycleEnum { - active, - inactive, - paused, - resumed, - detached, - hidden, -} +enum AppLifeCycleEnum { active, inactive, paused, resumed, detached, hidden } class AppLifeCycleNotifier extends StateNotifier { final Ref _ref; @@ -54,8 +51,7 @@ class AppLifeCycleNotifier extends StateNotifier { // Needs to be logged in if (isAuthenticated) { // switch endpoint if needed - final endpoint = - await _ref.read(authProvider.notifier).setOpenApiServiceEndpoint(); + final endpoint = await _ref.read(authProvider.notifier).setOpenApiServiceEndpoint(); if (kDebugMode) { debugPrint("Using server URL: $endpoint"); } @@ -75,16 +71,12 @@ class AppLifeCycleNotifier extends StateNotifier { switch (_ref.read(tabProvider)) { case TabEnum.home: await _ref.read(assetProvider.notifier).getAllAsset(); - break; - case TabEnum.search: - // nothing to do - break; case TabEnum.albums: await _ref.read(albumProvider.notifier).refreshRemoteAlbums(); - break; + case TabEnum.library: - // nothing to do + case TabEnum.search: break; } } else { @@ -94,36 +86,36 @@ class AppLifeCycleNotifier extends StateNotifier { // Ensure proper cleanup before starting new background tasks try { await Future.wait([ - backgroundManager.syncLocal().then( - (_) { - Logger("AppLifeCycleNotifier") - .fine("Hashing assets after syncLocal"); - // Check if app is still active before hashing - if (state == AppLifeCycleEnum.resumed) { - backgroundManager.hashAssets(); - } - }, - ), + backgroundManager.syncLocal().then((_) { + Logger("AppLifeCycleNotifier").fine("Hashing assets after syncLocal"); + // Check if app is still active before hashing + if (state == AppLifeCycleEnum.resumed) { + backgroundManager.hashAssets(); + } + }), backgroundManager.syncRemote(), - ]); + ]).then((_) async { + final isEnableBackup = _ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup); + + if (isEnableBackup) { + final currentUser = _ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + + await _ref.read(driftBackupProvider.notifier).handleBackupResume(currentUser.id); + } + }); } catch (e, stackTrace) { - Logger("AppLifeCycleNotifier").severe( - "Error during background sync", - e, - stackTrace, - ); + Logger("AppLifeCycleNotifier").severe("Error during background sync", e, stackTrace); } } _ref.read(websocketProvider.notifier).connect(); - await _ref - .read(notificationPermissionProvider.notifier) - .getNotificationPermission(); + await _ref.read(notificationPermissionProvider.notifier).getNotificationPermission(); - await _ref - .read(galleryPermissionNotifier.notifier) - .getGalleryPermissionStatus(); + await _ref.read(galleryPermissionNotifier.notifier).getGalleryPermissionStatus(); if (!Store.isBetaTimelineEnabled) { await _ref.read(iOSBackgroundSettingsProvider.notifier).refresh(); @@ -144,8 +136,7 @@ class AppLifeCycleNotifier extends StateNotifier { if (_ref.read(authProvider).isAuthenticated) { if (!Store.isBetaTimelineEnabled) { // Do not cancel backup if manual upload is in progress - if (_ref.read(backupProvider.notifier).backupProgress != - BackUpProgressEnum.manualInProgress) { + if (_ref.read(backupProvider.notifier).backupProgress != BackUpProgressEnum.manualInProgress) { _ref.read(backupProvider.notifier).cancelBackup(); } } @@ -198,7 +189,6 @@ class AppLifeCycleNotifier extends StateNotifier { } } -final appStateProvider = - StateNotifierProvider((ref) { +final appStateProvider = StateNotifierProvider((ref) { return AppLifeCycleNotifier(ref); }); diff --git a/mobile/lib/providers/asset.provider.dart b/mobile/lib/providers/asset.provider.dart index 7fbacc3afb..9c8b28e6cf 100644 --- a/mobile/lib/providers/asset.provider.dart +++ b/mobile/lib/providers/asset.provider.dart @@ -68,9 +68,7 @@ class AssetNotifier extends StateNotifier { } final bool newRemote = await _assetService.refreshRemoteAssets(); final bool newLocal = await _albumService.refreshDeviceAlbums(); - debugPrint( - "changedUsers: $changedUsers, newRemote: $newRemote, newLocal: $newLocal", - ); + debugPrint("changedUsers: $changedUsers, newRemote: $newRemote, newLocal: $newLocal"); if (newRemote) { _ref.invalidate(memoryFutureProvider); } @@ -122,17 +120,11 @@ class AssetNotifier extends StateNotifier { /// Delete remote asset only /// /// Default behavior is trashing the asset - Future deleteRemoteAssets( - Iterable deleteAssets, { - bool shouldDeletePermanently = false, - }) async { + Future deleteRemoteAssets(Iterable deleteAssets, {bool shouldDeletePermanently = false}) async { _deleteInProgress = true; state = true; try { - await _assetService.deleteRemoteAssets( - deleteAssets, - shouldDeletePermanently: shouldDeletePermanently, - ); + await _assetService.deleteRemoteAssets(deleteAssets, shouldDeletePermanently: shouldDeletePermanently); return true; } catch (error) { log.severe("Failed to delete remote assets", error); @@ -143,17 +135,11 @@ class AssetNotifier extends StateNotifier { } } - Future deleteAssets( - Iterable deleteAssets, { - bool force = false, - }) async { + Future deleteAssets(Iterable deleteAssets, {bool force = false}) async { _deleteInProgress = true; state = true; try { - await _assetService.deleteAssets( - deleteAssets, - shouldDeletePermanently: force, - ); + await _assetService.deleteAssets(deleteAssets, shouldDeletePermanently: force); return true; } catch (error) { log.severe("Failed to delete assets", error); @@ -174,16 +160,12 @@ class AssetNotifier extends StateNotifier { return _assetService.changeArchiveStatus(assets, status); } - Future setLockedView( - List selection, - AssetVisibilityEnum visibility, - ) { + Future setLockedView(List selection, AssetVisibilityEnum visibility) { return _assetService.setVisibility(selection, visibility); } } -final assetDetailProvider = - StreamProvider.autoDispose.family((ref, asset) async* { +final assetDetailProvider = StreamProvider.autoDispose.family((ref, asset) async* { final assetService = ref.watch(assetServiceProvider); yield await assetService.loadExif(asset); @@ -194,8 +176,7 @@ final assetDetailProvider = } }); -final assetWatcher = - StreamProvider.autoDispose.family((ref, asset) { +final assetWatcher = StreamProvider.autoDispose.family((ref, asset) { final assetService = ref.watch(assetServiceProvider); return assetService.watchAsset(asset.id, fireImmediately: true); }); diff --git a/mobile/lib/providers/asset_viewer/asset_people.provider.dart b/mobile/lib/providers/asset_viewer/asset_people.provider.dart index b334ef193a..e2227920c7 100644 --- a/mobile/lib/providers/asset_viewer/asset_people.provider.dart +++ b/mobile/lib/providers/asset_viewer/asset_people.provider.dart @@ -17,9 +17,7 @@ class AssetPeopleNotifier extends _$AssetPeopleNotifier { return []; } - final list = await ref - .watch(assetServiceProvider) - .getRemotePeopleOfAsset(asset.remoteId!); + final list = await ref.watch(assetServiceProvider).getRemotePeopleOfAsset(asset.remoteId!); if (list == null) { return []; } diff --git a/mobile/lib/providers/asset_viewer/asset_people.provider.g.dart b/mobile/lib/providers/asset_viewer/asset_people.provider.g.dart index ebe8a14186..031a70e0d9 100644 --- a/mobile/lib/providers/asset_viewer/asset_people.provider.g.dart +++ b/mobile/lib/providers/asset_viewer/asset_people.provider.g.dart @@ -30,13 +30,12 @@ class _SystemHash { } } -abstract class _$AssetPeopleNotifier extends BuildlessAutoDisposeAsyncNotifier< - List> { +abstract class _$AssetPeopleNotifier + extends + BuildlessAutoDisposeAsyncNotifier> { late final Asset asset; - FutureOr> build( - Asset asset, - ); + FutureOr> build(Asset asset); } /// Maintains the list of people for an asset. @@ -58,21 +57,15 @@ class AssetPeopleNotifierFamily /// Maintains the list of people for an asset. /// /// Copied from [AssetPeopleNotifier]. - AssetPeopleNotifierProvider call( - Asset asset, - ) { - return AssetPeopleNotifierProvider( - asset, - ); + AssetPeopleNotifierProvider call(Asset asset) { + return AssetPeopleNotifierProvider(asset); } @override AssetPeopleNotifierProvider getProviderOverride( covariant AssetPeopleNotifierProvider provider, ) { - return call( - provider.asset, - ); + return call(provider.asset); } static const Iterable? _dependencies = null; @@ -93,26 +86,28 @@ class AssetPeopleNotifierFamily /// Maintains the list of people for an asset. /// /// Copied from [AssetPeopleNotifier]. -class AssetPeopleNotifierProvider extends AutoDisposeAsyncNotifierProviderImpl< - AssetPeopleNotifier, List> { +class AssetPeopleNotifierProvider + extends + AutoDisposeAsyncNotifierProviderImpl< + AssetPeopleNotifier, + List + > { /// Maintains the list of people for an asset. /// /// Copied from [AssetPeopleNotifier]. - AssetPeopleNotifierProvider( - Asset asset, - ) : this._internal( - () => AssetPeopleNotifier()..asset = asset, - from: assetPeopleNotifierProvider, - name: r'assetPeopleNotifierProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$assetPeopleNotifierHash, - dependencies: AssetPeopleNotifierFamily._dependencies, - allTransitiveDependencies: - AssetPeopleNotifierFamily._allTransitiveDependencies, - asset: asset, - ); + AssetPeopleNotifierProvider(Asset asset) + : this._internal( + () => AssetPeopleNotifier()..asset = asset, + from: assetPeopleNotifierProvider, + name: r'assetPeopleNotifierProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$assetPeopleNotifierHash, + dependencies: AssetPeopleNotifierFamily._dependencies, + allTransitiveDependencies: + AssetPeopleNotifierFamily._allTransitiveDependencies, + asset: asset, + ); AssetPeopleNotifierProvider._internal( super._createNotifier, { @@ -130,9 +125,7 @@ class AssetPeopleNotifierProvider extends AutoDisposeAsyncNotifierProviderImpl< FutureOr> runNotifierBuild( covariant AssetPeopleNotifier notifier, ) { - return notifier.build( - asset, - ); + return notifier.build(asset); } @override @@ -152,8 +145,11 @@ class AssetPeopleNotifierProvider extends AutoDisposeAsyncNotifierProviderImpl< } @override - AutoDisposeAsyncNotifierProviderElement> createElement() { + AutoDisposeAsyncNotifierProviderElement< + AssetPeopleNotifier, + List + > + createElement() { return _AssetPeopleNotifierProviderElement(this); } @@ -180,12 +176,17 @@ mixin AssetPeopleNotifierRef } class _AssetPeopleNotifierProviderElement - extends AutoDisposeAsyncNotifierProviderElement> with AssetPeopleNotifierRef { + extends + AutoDisposeAsyncNotifierProviderElement< + AssetPeopleNotifier, + List + > + with AssetPeopleNotifierRef { _AssetPeopleNotifierProviderElement(super.provider); @override Asset get asset => (origin as AssetPeopleNotifierProvider).asset; } + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/mobile/lib/providers/asset_viewer/asset_stack.provider.dart b/mobile/lib/providers/asset_viewer/asset_stack.provider.dart index 9bbbfb49aa..8772e3d0cb 100644 --- a/mobile/lib/providers/asset_viewer/asset_stack.provider.dart +++ b/mobile/lib/providers/asset_viewer/asset_stack.provider.dart @@ -32,10 +32,8 @@ class AssetStackNotifier extends StateNotifier> { } } -final assetStackStateProvider = StateNotifierProvider.autoDispose - .family, String>( - (ref, stackId) => - AssetStackNotifier(ref.watch(assetServiceProvider), stackId), +final assetStackStateProvider = StateNotifierProvider.autoDispose.family, String>( + (ref, stackId) => AssetStackNotifier(ref.watch(assetServiceProvider), stackId), ); @riverpod diff --git a/mobile/lib/providers/asset_viewer/current_asset.provider.g.dart b/mobile/lib/providers/asset_viewer/current_asset.provider.g.dart index 53b02c2ace..e0d8d47d3a 100644 --- a/mobile/lib/providers/asset_viewer/current_asset.provider.g.dart +++ b/mobile/lib/providers/asset_viewer/current_asset.provider.g.dart @@ -12,13 +12,14 @@ String _$currentAssetHash() => r'2def10ea594152c984ae2974d687ab6856d7bdd0'; @ProviderFor(CurrentAsset) final currentAssetProvider = AutoDisposeNotifierProvider.internal( - CurrentAsset.new, - name: r'currentAssetProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$currentAssetHash, - dependencies: null, - allTransitiveDependencies: null, -); + CurrentAsset.new, + name: r'currentAssetProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$currentAssetHash, + dependencies: null, + allTransitiveDependencies: null, + ); typedef _$CurrentAsset = AutoDisposeNotifier; // ignore_for_file: type=lint diff --git a/mobile/lib/providers/asset_viewer/download.provider.dart b/mobile/lib/providers/asset_viewer/download.provider.dart index 7f0d913a02..36b935abe7 100644 --- a/mobile/lib/providers/asset_viewer/download.provider.dart +++ b/mobile/lib/providers/asset_viewer/download.provider.dart @@ -18,17 +18,14 @@ class DownloadStateNotifier extends StateNotifier { final ShareService _shareService; final AlbumService _albumService; - DownloadStateNotifier( - this._downloadService, - this._shareService, - this._albumService, - ) : super( - const DownloadState( - downloadStatus: TaskStatus.complete, - showProgress: false, - taskProgress: {}, - ), - ) { + DownloadStateNotifier(this._downloadService, this._shareService, this._albumService) + : super( + const DownloadState( + downloadStatus: TaskStatus.complete, + showProgress: false, + taskProgress: {}, + ), + ) { _downloadService.onImageDownloadStatus = _downloadImageCallback; _downloadService.onVideoDownloadStatus = _downloadVideoCallback; _downloadService.onLivePhotoDownloadStatus = _downloadLivePhotoCallback; @@ -62,8 +59,7 @@ class DownloadStateNotifier extends StateNotifier { if (update.task.metaData.isEmpty) { return; } - final livePhotosId = - LivePhotosMetadata.fromJson(update.task.metaData).id; + final livePhotosId = LivePhotosMetadata.fromJson(update.task.metaData).id; _downloadService.saveLivePhotos(update.task, livePhotosId); _onDownloadComplete(update.task.taskId); break; @@ -132,9 +128,7 @@ class DownloadStateNotifier extends StateNotifier { ); if (state.taskProgress.isEmpty) { - state = state.copyWith( - showProgress: false, - ); + state = state.copyWith(showProgress: false); } _albumService.refreshDeviceAlbums(); }); @@ -160,9 +154,7 @@ class DownloadStateNotifier extends StateNotifier { } if (state.taskProgress.isEmpty) { - state = state.copyWith( - showProgress: false, - ); + state = state.copyWith(showProgress: false); } } @@ -170,19 +162,17 @@ class DownloadStateNotifier extends StateNotifier { showDialog( context: context, builder: (BuildContext buildContext) { - _shareService.shareAsset(asset, context).then( - (bool status) { - if (!status) { - ImmichToast.show( - context: context, - msg: 'image_viewer_page_state_provider_share_error'.tr(), - toastType: ToastType.error, - gravity: ToastGravity.BOTTOM, - ); - } - buildContext.pop(); - }, - ); + _shareService.shareAsset(asset, context).then((bool status) { + if (!status) { + ImmichToast.show( + context: context, + msg: 'image_viewer_page_state_provider_share_error'.tr(), + toastType: ToastType.error, + gravity: ToastGravity.BOTTOM, + ); + } + buildContext.pop(); + }); return const ShareDialog(); }, barrierDismissible: false, @@ -191,11 +181,10 @@ class DownloadStateNotifier extends StateNotifier { } } -final downloadStateProvider = - StateNotifierProvider( +final downloadStateProvider = StateNotifierProvider( ((ref) => DownloadStateNotifier( - ref.watch(downloadServiceProvider), - ref.watch(shareServiceProvider), - ref.watch(albumServiceProvider), - )), + ref.watch(downloadServiceProvider), + ref.watch(shareServiceProvider), + ref.watch(albumServiceProvider), + )), ); diff --git a/mobile/lib/providers/asset_viewer/is_motion_video_playing.provider.dart b/mobile/lib/providers/asset_viewer/is_motion_video_playing.provider.dart index 4af061f954..08722dc896 100644 --- a/mobile/lib/providers/asset_viewer/is_motion_video_playing.provider.dart +++ b/mobile/lib/providers/asset_viewer/is_motion_video_playing.provider.dart @@ -1,8 +1,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; /// Whether to display the video part of a motion photo -final isPlayingMotionVideoProvider = - StateNotifierProvider((ref) { +final isPlayingMotionVideoProvider = StateNotifierProvider((ref) { return IsPlayingMotionVideo(ref); }); diff --git a/mobile/lib/providers/asset_viewer/render_list_status_provider.dart b/mobile/lib/providers/asset_viewer/render_list_status_provider.dart index 903007031e..189ac85452 100644 --- a/mobile/lib/providers/asset_viewer/render_list_status_provider.dart +++ b/mobile/lib/providers/asset_viewer/render_list_status_provider.dart @@ -2,8 +2,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; enum RenderListStatusEnum { complete, empty, error, loading } -final renderListStatusProvider = - StateNotifierProvider((ref) { +final renderListStatusProvider = StateNotifierProvider((ref) { return RenderListStatus(ref); }); diff --git a/mobile/lib/providers/asset_viewer/share_intent_upload.provider.dart b/mobile/lib/providers/asset_viewer/share_intent_upload.provider.dart index ed2c485b13..7b2ab5b27a 100644 --- a/mobile/lib/providers/asset_viewer/share_intent_upload.provider.dart +++ b/mobile/lib/providers/asset_viewer/share_intent_upload.provider.dart @@ -1,37 +1,34 @@ import 'dart:io'; import 'package:background_downloader/background_downloader.dart'; -import 'package:flutter/foundation.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/constants.dart'; +import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/extensions/string_extensions.dart'; import 'package:immich_mobile/models/upload/share_intent_attachment.model.dart'; import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/services/api.service.dart'; import 'package:immich_mobile/services/share_intent_service.dart'; import 'package:immich_mobile/services/upload.service.dart'; +import 'package:path/path.dart'; -final shareIntentUploadProvider = StateNotifierProvider< - ShareIntentUploadStateNotifier, List>( +final shareIntentUploadProvider = StateNotifierProvider>( ((ref) => ShareIntentUploadStateNotifier( - ref.watch(appRouterProvider), - ref.watch(uploadServiceProvider), - ref.watch(shareIntentServiceProvider), - )), + ref.watch(appRouterProvider), + ref.watch(uploadServiceProvider), + ref.watch(shareIntentServiceProvider), + )), ); -class ShareIntentUploadStateNotifier - extends StateNotifier> { +class ShareIntentUploadStateNotifier extends StateNotifier> { final AppRouter router; final UploadService _uploadService; final ShareIntentService _shareIntentService; - ShareIntentUploadStateNotifier( - this.router, - this._uploadService, - this._shareIntentService, - ) : super([]) { - _uploadService.onUploadStatus = _uploadStatusCallback; - _uploadService.onTaskProgress = _taskProgressCallback; + ShareIntentUploadStateNotifier(this.router, this._uploadService, this._shareIntentService) : super([]) { + _uploadService.taskStatusStream.listen(_updateUploadStatus); + _uploadService.taskProgressStream.listen(_taskProgressCallback); } void init() { @@ -54,8 +51,7 @@ class ShareIntentUploadStateNotifier } void removeAttachment(ShareIntentAttachment attachment) { - final updatedState = - state.where((element) => element != attachment).toList(); + final updatedState = state.where((element) => element != attachment).toList(); if (updatedState.length != state.length) { state = updatedState; } @@ -69,8 +65,8 @@ class ShareIntentUploadStateNotifier state = []; } - void _updateUploadStatus(TaskStatusUpdate task, TaskStatus status) async { - if (status == TaskStatus.canceled) { + void _updateUploadStatus(TaskStatusUpdate task) async { + if (task.status == TaskStatus.canceled) { return; } @@ -83,61 +79,68 @@ class ShareIntentUploadStateNotifier TaskStatus.running => UploadStatus.running, TaskStatus.paused => UploadStatus.paused, TaskStatus.notFound => UploadStatus.notFound, - TaskStatus.waitingToRetry => UploadStatus.waitingtoRetry + TaskStatus.waitingToRetry => UploadStatus.waitingToRetry, }; state = [ for (final attachment in state) - if (attachment.id == taskId.toInt()) - attachment.copyWith(status: uploadStatus) - else - attachment, + if (attachment.id == taskId.toInt()) attachment.copyWith(status: uploadStatus) else attachment, ]; } - void _uploadStatusCallback(TaskStatusUpdate update) { - _updateUploadStatus(update, update.status); - - switch (update.status) { - case TaskStatus.complete: - if (update.responseStatusCode == 200) { - if (kDebugMode) { - debugPrint("[COMPLETE] ${update.task.taskId} - DUPLICATE"); - } - } else { - if (kDebugMode) { - debugPrint("[COMPLETE] ${update.task.taskId}"); - } - } - break; - - default: - break; - } - } - void _taskProgressCallback(TaskProgressUpdate update) { // Ignore if the task is canceled or completed - if (update.progress == downloadFailed || - update.progress == downloadCompleted) { + if (update.progress == downloadFailed || update.progress == downloadCompleted) { return; } final taskId = update.task.taskId; state = [ for (final attachment in state) - if (attachment.id == taskId.toInt()) - attachment.copyWith(uploadProgress: update.progress) - else - attachment, + if (attachment.id == taskId.toInt()) attachment.copyWith(uploadProgress: update.progress) else attachment, ]; } - Future upload(File file) { - return _uploadService.upload(file); + Future upload(File file) async { + final task = await _buildUploadTask(hash(file.path).toString(), file); + + _uploadService.enqueueTasks([task]); } - Future cancelUpload(String id) { - return _uploadService.cancelUpload(id); + Future _buildUploadTask(String id, File file, {Map? fields}) async { + final serverEndpoint = Store.get(StoreKey.serverEndpoint); + final url = Uri.parse('$serverEndpoint/assets').toString(); + final headers = ApiService.getRequestHeaders(); + final deviceId = Store.get(StoreKey.deviceId); + + 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 = { + 'filename': filename, + 'deviceAssetId': id, + 'deviceId': deviceId, + 'fileCreatedAt': fileCreatedAt.toUtc().toIso8601String(), + 'fileModifiedAt': fileModifiedAt.toUtc().toIso8601String(), + 'isFavorite': 'false', + 'duration': '0', + if (fields != null) ...fields, + }; + + return UploadTask( + taskId: id, + httpRequestMethod: 'POST', + url: url, + headers: headers, + filename: filename, + fields: fieldsMap, + baseDirectory: baseDirectory, + directory: directory, + fileField: 'assetData', + group: kManualUploadGroup, + updates: Updates.statusAndProgress, + ); } } diff --git a/mobile/lib/providers/asset_viewer/video_player_controls_provider.dart b/mobile/lib/providers/asset_viewer/video_player_controls_provider.dart index 69be91480f..3cfc2e2f6f 100644 --- a/mobile/lib/providers/asset_viewer/video_player_controls_provider.dart +++ b/mobile/lib/providers/asset_viewer/video_player_controls_provider.dart @@ -2,24 +2,18 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider.dart'; class VideoPlaybackControls { - const VideoPlaybackControls({ - required this.position, - required this.pause, - this.restarted = false, - }); + const VideoPlaybackControls({required this.position, required this.pause, this.restarted = false}); final double position; final bool pause; final bool restarted; } -final videoPlayerControlsProvider = - StateNotifierProvider((ref) { +final videoPlayerControlsProvider = StateNotifierProvider((ref) { return VideoPlayerControls(ref); }); -const videoPlayerControlsDefault = - VideoPlaybackControls(position: 0, pause: false); +const videoPlayerControlsDefault = VideoPlaybackControls(position: 0, pause: false); class VideoPlayerControls extends StateNotifier { VideoPlayerControls(this.ref) : super(videoPlayerControlsDefault); @@ -64,17 +58,14 @@ class VideoPlayerControls extends StateNotifier { } void togglePlay() { - state = - VideoPlaybackControls(position: state.position, pause: !state.pause); + state = VideoPlaybackControls(position: state.position, pause: !state.pause); } void restart() { - state = - const VideoPlaybackControls(position: 0, pause: false, restarted: true); - ref.read(videoPlaybackValueProvider.notifier).value = - ref.read(videoPlaybackValueProvider.notifier).value.copyWith( - state: VideoPlaybackState.playing, - position: Duration.zero, - ); + state = const VideoPlaybackControls(position: 0, pause: false, restarted: true); + ref.read(videoPlaybackValueProvider.notifier).value = ref + .read(videoPlaybackValueProvider.notifier) + .value + .copyWith(state: VideoPlaybackState.playing, position: Duration.zero); } } diff --git a/mobile/lib/providers/asset_viewer/video_player_value_provider.dart b/mobile/lib/providers/asset_viewer/video_player_value_provider.dart index 1a3c54e9e9..c478ddd6f5 100644 --- a/mobile/lib/providers/asset_viewer/video_player_value_provider.dart +++ b/mobile/lib/providers/asset_viewer/video_player_value_provider.dart @@ -1,13 +1,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:native_video_player/native_video_player.dart'; -enum VideoPlaybackState { - initializing, - paused, - playing, - buffering, - completed, -} +enum VideoPlaybackState { initializing, paused, playing, buffering, completed } class VideoPlaybackValue { /// The current position of the video @@ -22,16 +16,9 @@ class VideoPlaybackValue { /// The volume of the video final double volume; - const VideoPlaybackValue({ - required this.position, - required this.duration, - required this.state, - required this.volume, - }); + const VideoPlaybackValue({required this.position, required this.duration, required this.state, required this.volume}); - factory VideoPlaybackValue.fromNativeController( - NativeVideoPlayerController controller, - ) { + factory VideoPlaybackValue.fromNativeController(NativeVideoPlayerController controller) { final playbackInfo = controller.playbackInfo; final videoInfo = controller.videoInfo; @@ -53,12 +40,7 @@ class VideoPlaybackValue { ); } - VideoPlaybackValue copyWith({ - Duration? position, - Duration? duration, - VideoPlaybackState? state, - double? volume, - }) { + VideoPlaybackValue copyWith({Duration? position, Duration? duration, VideoPlaybackState? state, double? volume}) { return VideoPlaybackValue( position: position ?? this.position, duration: duration ?? this.duration, @@ -75,8 +57,7 @@ const VideoPlaybackValue videoPlaybackValueDefault = VideoPlaybackValue( volume: 0.0, ); -final videoPlaybackValueProvider = - StateNotifierProvider((ref) { +final videoPlaybackValueProvider = StateNotifierProvider((ref) { return VideoPlaybackValueState(ref); }); @@ -93,22 +74,12 @@ class VideoPlaybackValueState extends StateNotifier { set position(Duration value) { if (state.position == value) return; - state = VideoPlaybackValue( - position: value, - duration: state.duration, - state: state.state, - volume: state.volume, - ); + state = VideoPlaybackValue(position: value, duration: state.duration, state: state.state, volume: state.volume); } set status(VideoPlaybackState value) { if (state.state == value) return; - state = VideoPlaybackValue( - position: state.position, - duration: state.duration, - state: value, - volume: state.volume, - ); + state = VideoPlaybackValue(position: state.position, duration: state.duration, state: value, volume: state.volume); } void reset() { diff --git a/mobile/lib/providers/auth.provider.dart b/mobile/lib/providers/auth.provider.dart index 2d3de11257..02f7920d6f 100644 --- a/mobile/lib/providers/auth.provider.dart +++ b/mobile/lib/providers/auth.provider.dart @@ -13,6 +13,7 @@ import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; import 'package:immich_mobile/services/api.service.dart'; import 'package:immich_mobile/services/auth.service.dart'; import 'package:immich_mobile/services/secure_storage.service.dart'; +import 'package:immich_mobile/services/upload.service.dart'; import 'package:immich_mobile/services/widget.service.dart'; import 'package:immich_mobile/utils/hash.dart'; import 'package:logging/logging.dart'; @@ -23,6 +24,7 @@ final authProvider = StateNotifierProvider((ref) { ref.watch(authServiceProvider), ref.watch(apiServiceProvider), ref.watch(userServiceProvider), + ref.watch(uploadServiceProvider), ref.watch(secureStorageServiceProvider), ref.watch(widgetServiceProvider), ); @@ -32,6 +34,7 @@ class AuthNotifier extends StateNotifier { final AuthService _authService; final ApiService _apiService; final UserService _userService; + final UploadService _uploadService; final SecureStorageService _secureStorageService; final WidgetService _widgetService; final _log = Logger("AuthenticationNotifier"); @@ -42,19 +45,20 @@ class AuthNotifier extends StateNotifier { this._authService, this._apiService, this._userService, + this._uploadService, this._secureStorageService, this._widgetService, ) : super( - const AuthState( - deviceId: "", - userId: "", - userEmail: "", - name: '', - profileImagePath: '', - isAdmin: false, - isAuthenticated: false, - ), - ); + const AuthState( + deviceId: "", + userId: "", + userEmail: "", + name: '', + profileImagePath: '', + isAdmin: false, + isAuthenticated: false, + ), + ); Future validateServerUrl(String url) { return _authService.validateServerUrl(url); @@ -83,6 +87,7 @@ class AuthNotifier extends StateNotifier { await _widgetService.clearCredentials(); await _authService.logout(); + await _uploadService.cancelBackup(); } finally { await _cleanUp(); } @@ -113,25 +118,18 @@ class AuthNotifier extends StateNotifier { } } - Future saveAuthInfo({ - required String accessToken, - }) async { + Future saveAuthInfo({required String accessToken}) async { await _apiService.setAccessToken(accessToken); - await _widgetService.writeCredentials( - Store.get(StoreKey.serverEndpoint), - accessToken, - ); + await _widgetService.writeCredentials(Store.get(StoreKey.serverEndpoint), accessToken); // Get the deviceid from the store if it exists, otherwise generate a new one - String deviceId = - Store.tryGet(StoreKey.deviceId) ?? await FlutterUdid.consistentUdid; + String deviceId = Store.tryGet(StoreKey.deviceId) ?? await FlutterUdid.consistentUdid; UserDto? user = _userService.tryGetMyUser(); try { - final serverUser = - await _userService.refreshMyUser().timeout(_timeoutDuration); + final serverUser = await _userService.refreshMyUser().timeout(_timeoutDuration); if (serverUser == null) { _log.severe("Unable to get user information from the server."); } else { @@ -147,21 +145,12 @@ class AuthNotifier extends StateNotifier { _log.severe("Unauthorized access, token likely expired. Logging out."); return false; } - _log.severe( - "Error getting user information from the server [API EXCEPTION]", - stackTrace, - ); + _log.severe("Error getting user information from the server [API EXCEPTION]", stackTrace); } catch (error, stackTrace) { - _log.severe( - "Error getting user information from the server [CATCH ALL]", - error, - stackTrace, - ); + _log.severe("Error getting user information from the server [CATCH ALL]", error, stackTrace); if (kDebugMode) { - debugPrint( - "Error getting user information from the server [CATCH ALL] $error $stackTrace", - ); + debugPrint("Error getting user information from the server [CATCH ALL] $error $stackTrace"); } } @@ -178,7 +167,6 @@ class AuthNotifier extends StateNotifier { isAuthenticated: true, name: user.name, isAdmin: user.isAdmin, - profileImagePath: user.profileImagePath, ); return true; diff --git a/mobile/lib/providers/background_sync.provider.dart b/mobile/lib/providers/background_sync.provider.dart index 83d103bb3b..e6e83b64df 100644 --- a/mobile/lib/providers/background_sync.provider.dart +++ b/mobile/lib/providers/background_sync.provider.dart @@ -1,8 +1,20 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/utils/background_sync.dart'; +import 'package:immich_mobile/providers/sync_status.provider.dart'; final backgroundSyncProvider = Provider((ref) { - final manager = BackgroundSyncManager(); + final syncStatusNotifier = ref.read(syncStatusProvider.notifier); + final manager = BackgroundSyncManager( + onRemoteSyncStart: syncStatusNotifier.startRemoteSync, + onRemoteSyncComplete: syncStatusNotifier.completeRemoteSync, + onRemoteSyncError: syncStatusNotifier.errorRemoteSync, + onLocalSyncStart: syncStatusNotifier.startLocalSync, + onLocalSyncComplete: syncStatusNotifier.completeLocalSync, + onLocalSyncError: syncStatusNotifier.errorLocalSync, + onHashingStart: syncStatusNotifier.startHashJob, + onHashingComplete: syncStatusNotifier.completeHashJob, + onHashingError: syncStatusNotifier.errorHashJob, + ); ref.onDispose(manager.cancel); return manager; }); diff --git a/mobile/lib/providers/backup/backup.provider.dart b/mobile/lib/providers/backup/backup.provider.dart index 69246398ed..76cb383465 100644 --- a/mobile/lib/providers/backup/backup.provider.dart +++ b/mobile/lib/providers/backup/backup.provider.dart @@ -34,8 +34,7 @@ import 'package:logging/logging.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:photo_manager/photo_manager.dart' show PMProgressHandler; -final backupProvider = - StateNotifierProvider((ref) { +final backupProvider = StateNotifierProvider((ref) { return BackupNotifier( ref.watch(backupServiceProvider), ref.watch(serverInfoServiceProvider), @@ -61,44 +60,38 @@ class BackupNotifier extends StateNotifier { this._backupAlbumService, this.ref, ) : super( - BackUpState( - backupProgress: BackUpProgressEnum.idle, - allAssetsInDatabase: const [], - progressInPercentage: 0, - progressInFileSize: "0 B / 0 B", - progressInFileSpeed: 0, - progressInFileSpeeds: const [], - progressInFileSpeedUpdateTime: DateTime.now(), - progressInFileSpeedUpdateSentBytes: 0, - cancelToken: CancellationToken(), - autoBackup: Store.get(StoreKey.autoBackup, false), - backgroundBackup: Store.get(StoreKey.backgroundBackup, false), - backupRequireWifi: Store.get(StoreKey.backupRequireWifi, true), - backupRequireCharging: - Store.get(StoreKey.backupRequireCharging, false), - backupTriggerDelay: Store.get(StoreKey.backupTriggerDelay, 5000), - serverInfo: const ServerDiskInfo( - diskAvailable: "0", - diskSize: "0", - diskUse: "0", - diskUsagePercentage: 0, - ), - availableAlbums: const [], - selectedBackupAlbums: const {}, - excludedBackupAlbums: const {}, - allUniqueAssets: const {}, - selectedAlbumsBackupAssetsIds: const {}, - currentUploadAsset: CurrentUploadAsset( - id: '...', - fileCreatedAt: DateTime.parse('2020-10-04'), - fileName: '...', - fileType: '...', - fileSize: 0, - iCloudAsset: false, - ), - iCloudDownloadProgress: 0.0, + BackUpState( + backupProgress: BackUpProgressEnum.idle, + allAssetsInDatabase: const [], + progressInPercentage: 0, + progressInFileSize: "0 B / 0 B", + progressInFileSpeed: 0, + progressInFileSpeeds: const [], + progressInFileSpeedUpdateTime: DateTime.now(), + progressInFileSpeedUpdateSentBytes: 0, + cancelToken: CancellationToken(), + autoBackup: Store.get(StoreKey.autoBackup, false), + backgroundBackup: Store.get(StoreKey.backgroundBackup, false), + backupRequireWifi: Store.get(StoreKey.backupRequireWifi, true), + backupRequireCharging: Store.get(StoreKey.backupRequireCharging, false), + backupTriggerDelay: Store.get(StoreKey.backupTriggerDelay, 5000), + serverInfo: const ServerDiskInfo(diskAvailable: "0", diskSize: "0", diskUse: "0", diskUsagePercentage: 0), + availableAlbums: const [], + selectedBackupAlbums: const {}, + excludedBackupAlbums: const {}, + allUniqueAssets: const {}, + selectedAlbumsBackupAssetsIds: const {}, + currentUploadAsset: CurrentUploadAsset( + id: '...', + fileCreatedAt: DateTime.parse('2020-10-04'), + fileName: '...', + fileType: '...', + fileSize: 0, + iCloudAsset: false, ), - ); + iCloudDownloadProgress: 0.0, + ), + ); final log = Logger('BackupNotifier'); final BackupService _backupService; @@ -124,16 +117,14 @@ class BackupNotifier extends StateNotifier { removeExcludedAlbumForBackup(album); } - state = state - .copyWith(selectedBackupAlbums: {...state.selectedBackupAlbums, album}); + state = state.copyWith(selectedBackupAlbums: {...state.selectedBackupAlbums, album}); } void addExcludedAlbumForBackup(AvailableAlbum album) { if (state.selectedBackupAlbums.contains(album)) { removeAlbumForBackup(album); } - state = state - .copyWith(excludedBackupAlbums: {...state.excludedBackupAlbums, album}); + state = state.copyWith(excludedBackupAlbums: {...state.excludedBackupAlbums, album}); } void removeAlbumForBackup(AvailableAlbum album) { @@ -157,11 +148,7 @@ class BackupNotifier extends StateNotifier { // disable any backup cancelBackup(); setAutoBackup(false); - configureBackgroundBackup( - enabled: false, - onError: (msg) {}, - onBatteryInfo: () {}, - ); + configureBackgroundBackup(enabled: false, onError: (msg) {}, onBatteryInfo: () {}); } return _updateBackupAssetCount(); } @@ -179,12 +166,7 @@ class BackupNotifier extends StateNotifier { required void Function(String msg) onError, required void Function() onBatteryInfo, }) async { - assert( - enabled != null || - requireWifi != null || - requireCharging != null || - triggerDelay != null, - ); + assert(enabled != null || requireWifi != null || requireCharging != null || triggerDelay != null); final bool wasEnabled = state.backgroundBackup; final bool wasWifi = state.backupRequireWifi; final bool wasCharging = state.backupRequireCharging; @@ -204,7 +186,8 @@ class BackupNotifier extends StateNotifier { } success &= await _backgroundService.enableService(immediate: true); } - success &= success && + success &= + success && await _backgroundService.configureService( requireUnmetered: state.backupRequireWifi, requireCharging: state.backupRequireCharging, @@ -213,10 +196,7 @@ class BackupNotifier extends StateNotifier { ); if (success) { await Store.put(StoreKey.backupRequireWifi, state.backupRequireWifi); - await Store.put( - StoreKey.backupRequireCharging, - state.backupRequireCharging, - ); + await Store.put(StoreKey.backupRequireCharging, state.backupRequireCharging); await Store.put(StoreKey.backupTriggerDelay, state.backupTriggerDelay); await Store.put(StoreKey.backgroundBackup, state.backgroundBackup); } else { @@ -257,9 +237,7 @@ class BackupNotifier extends StateNotifier { for (Album album in albums) { AvailableAlbum availableAlbum = AvailableAlbum( album: album, - assetCount: await ref - .read(albumMediaRepositoryProvider) - .getAssetCount(album.localId!), + assetCount: await ref.read(albumMediaRepositoryProvider).getAssetCount(album.localId!), ); availableAlbums.add(availableAlbum); @@ -268,10 +246,8 @@ class BackupNotifier extends StateNotifier { } state = state.copyWith(availableAlbums: availableAlbums); - final List excludedBackupAlbums = - await _backupAlbumService.getAllBySelection(BackupSelection.exclude); - final List selectedBackupAlbums = - await _backupAlbumService.getAllBySelection(BackupSelection.select); + final List excludedBackupAlbums = await _backupAlbumService.getAllBySelection(BackupSelection.exclude); + final List selectedBackupAlbums = await _backupAlbumService.getAllBySelection(BackupSelection.select); final Set selectedAlbums = {}; for (final BackupAlbum ba in selectedBackupAlbums) { @@ -281,8 +257,7 @@ class BackupNotifier extends StateNotifier { selectedAlbums.add( AvailableAlbum( album: albumAsset, - assetCount: - await _albumMediaRepository.getAssetCount(albumAsset.localId!), + assetCount: await _albumMediaRepository.getAssetCount(albumAsset.localId!), lastBackup: ba.lastBackup, ), ); @@ -299,9 +274,7 @@ class BackupNotifier extends StateNotifier { excludedAlbums.add( AvailableAlbum( album: albumAsset, - assetCount: await ref - .read(albumMediaRepositoryProvider) - .getAssetCount(albumAsset.localId!), + assetCount: await ref.read(albumMediaRepositoryProvider).getAssetCount(albumAsset.localId!), lastBackup: ba.lastBackup, ), ); @@ -310,14 +283,9 @@ class BackupNotifier extends StateNotifier { } } - state = state.copyWith( - selectedBackupAlbums: selectedAlbums, - excludedBackupAlbums: excludedAlbums, - ); + state = state.copyWith(selectedBackupAlbums: selectedAlbums, excludedBackupAlbums: excludedAlbums); - log.info( - "_getBackupAlbumsInfo: Found ${availableAlbums.length} available albums", - ); + log.info("_getBackupAlbumsInfo: Found ${availableAlbums.length} available albums"); debugPrint("_getBackupAlbumsInfo takes ${stopwatch.elapsedMilliseconds}ms"); } @@ -335,62 +303,44 @@ class BackupNotifier extends StateNotifier { final Set assetsFromExcludedAlbums = {}; for (final album in state.selectedBackupAlbums) { - final assetCount = await ref - .read(albumMediaRepositoryProvider) - .getAssetCount(album.album.localId!); + final assetCount = await ref.read(albumMediaRepositoryProvider).getAssetCount(album.album.localId!); if (assetCount == 0) { continue; } - final assets = await ref - .read(albumMediaRepositoryProvider) - .getAssets(album.album.localId!); + final assets = await ref.read(albumMediaRepositoryProvider).getAssets(album.album.localId!); // Add album's name to the asset info for (final asset in assets) { List albumNames = [album.name]; - final existingAsset = assetsFromSelectedAlbums.firstWhereOrNull( - (a) => a.asset.localId == asset.localId, - ); + final existingAsset = assetsFromSelectedAlbums.firstWhereOrNull((a) => a.asset.localId == asset.localId); if (existingAsset != null) { albumNames.addAll(existingAsset.albumNames); assetsFromSelectedAlbums.remove(existingAsset); } - assetsFromSelectedAlbums.add( - BackupCandidate( - asset: asset, - albumNames: albumNames, - ), - ); + assetsFromSelectedAlbums.add(BackupCandidate(asset: asset, albumNames: albumNames)); } } for (final album in state.excludedBackupAlbums) { - final assetCount = await ref - .read(albumMediaRepositoryProvider) - .getAssetCount(album.album.localId!); + final assetCount = await ref.read(albumMediaRepositoryProvider).getAssetCount(album.album.localId!); if (assetCount == 0) { continue; } - final assets = await ref - .read(albumMediaRepositoryProvider) - .getAssets(album.album.localId!); + final assets = await ref.read(albumMediaRepositoryProvider).getAssets(album.album.localId!); for (final asset in assets) { - assetsFromExcludedAlbums.add( - BackupCandidate(asset: asset, albumNames: [album.name]), - ); + assetsFromExcludedAlbums.add(BackupCandidate(asset: asset, albumNames: [album.name])); } } - final Set allUniqueAssets = - assetsFromSelectedAlbums.difference(assetsFromExcludedAlbums); + final Set allUniqueAssets = assetsFromSelectedAlbums.difference(assetsFromExcludedAlbums); final allAssetsInDatabase = await _backupService.getDeviceBackupAsset(); @@ -399,16 +349,12 @@ class BackupNotifier extends StateNotifier { } // Find asset that were backup from selected albums - final Set selectedAlbumsBackupAssets = - Set.from(allUniqueAssets.map((e) => e.asset.localId)); + final Set selectedAlbumsBackupAssets = Set.from(allUniqueAssets.map((e) => e.asset.localId)); - selectedAlbumsBackupAssets - .removeWhere((assetId) => !allAssetsInDatabase.contains(assetId)); + selectedAlbumsBackupAssets.removeWhere((assetId) => !allAssetsInDatabase.contains(assetId)); // Remove duplicated asset from all unique assets - allUniqueAssets.removeWhere( - (candidate) => duplicatedAssetIds.contains(candidate.asset.localId), - ); + allUniqueAssets.removeWhere((candidate) => duplicatedAssetIds.contains(candidate.asset.localId)); if (allUniqueAssets.isEmpty) { log.info("No assets are selected for back up"); @@ -459,8 +405,7 @@ class BackupNotifier extends StateNotifier { final candidates = selected.followedBy(excluded).toList(); candidates.sortBy((e) => e.id); - final savedBackupAlbums = - await _backupAlbumService.getAll(sort: BackupAlbumSort.id); + final savedBackupAlbums = await _backupAlbumService.getAll(sort: BackupAlbumSort.id); final List toDelete = []; final List toUpsert = []; @@ -469,8 +414,7 @@ class BackupNotifier extends StateNotifier { candidates, compare: (BackupAlbum a, BackupAlbum b) => a.id.compareTo(b.id), both: (BackupAlbum a, BackupAlbum b) { - b.lastBackup = - a.lastBackup.isAfter(b.lastBackup) ? a.lastBackup : b.lastBackup; + b.lastBackup = a.lastBackup.isAfter(b.lastBackup) ? a.lastBackup : b.lastBackup; toUpsert.add(b); return true; }, @@ -536,9 +480,7 @@ class BackupNotifier extends StateNotifier { } void setAvailableAlbums(availableAlbums) { - state = state.copyWith( - availableAlbums: availableAlbums, - ); + state = state.copyWith(availableAlbums: availableAlbums); } void _onBackupError(ErrorUploadAsset errorAssetInfo) { @@ -568,40 +510,23 @@ class BackupNotifier extends StateNotifier { if (result.isDuplicate) { state = state.copyWith( allUniqueAssets: state.allUniqueAssets - .where( - (candidate) => - candidate.asset.localId != result.candidate.asset.localId, - ) + .where((candidate) => candidate.asset.localId != result.candidate.asset.localId) .toSet(), ); } else { state = state.copyWith( - selectedAlbumsBackupAssetsIds: { - ...state.selectedAlbumsBackupAssetsIds, - result.candidate.asset.localId!, - }, - allAssetsInDatabase: [ - ...state.allAssetsInDatabase, - result.candidate.asset.localId!, - ], + selectedAlbumsBackupAssetsIds: {...state.selectedAlbumsBackupAssetsIds, result.candidate.asset.localId!}, + allAssetsInDatabase: [...state.allAssetsInDatabase, result.candidate.asset.localId!], ); } - if (state.allUniqueAssets.length - - state.selectedAlbumsBackupAssetsIds.length == - 0) { + if (state.allUniqueAssets.length - state.selectedAlbumsBackupAssetsIds.length == 0) { final latestAssetBackup = state.allUniqueAssets .map((candidate) => candidate.asset.fileModifiedAt) - .reduce( - (v, e) => e.isAfter(v) ? e : v, - ); + .reduce((v, e) => e.isAfter(v) ? e : v); state = state.copyWith( - selectedBackupAlbums: state.selectedBackupAlbums - .map((e) => e.copyWith(lastBackup: latestAssetBackup)) - .toSet(), - excludedBackupAlbums: state.excludedBackupAlbums - .map((e) => e.copyWith(lastBackup: latestAssetBackup)) - .toSet(), + selectedBackupAlbums: state.selectedBackupAlbums.map((e) => e.copyWith(lastBackup: latestAssetBackup)).toSet(), + excludedBackupAlbums: state.excludedBackupAlbums.map((e) => e.copyWith(lastBackup: latestAssetBackup)).toSet(), backupProgress: BackUpProgressEnum.done, progressInPercentage: 0.0, progressInFileSize: "0 B / 0 B", @@ -630,9 +555,7 @@ class BackupNotifier extends StateNotifier { } if (duration.inSeconds > 0) { - lastUploadSpeeds.add( - ((sent - lastSentBytes) / duration.inSeconds).abs().roundToDouble(), - ); + lastUploadSpeeds.add(((sent - lastSentBytes) / duration.inSeconds).abs().roundToDouble()); lastUploadSpeed = lastUploadSpeeds.average.abs().roundToDouble(); lastUpdateTime = now; @@ -654,9 +577,7 @@ class BackupNotifier extends StateNotifier { // Update server info if (diskInfo != null) { - state = state.copyWith( - serverInfo: diskInfo, - ); + state = state.copyWith(serverInfo: diskInfo); } } @@ -696,24 +617,16 @@ class BackupNotifier extends StateNotifier { } Future resumeBackup() async { - final List selectedBackupAlbums = - await _backupAlbumService.getAllBySelection(BackupSelection.select); - final List excludedBackupAlbums = - await _backupAlbumService.getAllBySelection(BackupSelection.exclude); + final List selectedBackupAlbums = await _backupAlbumService.getAllBySelection(BackupSelection.select); + final List excludedBackupAlbums = await _backupAlbumService.getAllBySelection(BackupSelection.exclude); Set selectedAlbums = state.selectedBackupAlbums; Set excludedAlbums = state.excludedBackupAlbums; if (selectedAlbums.isNotEmpty) { - selectedAlbums = _updateAlbumsBackupTime( - selectedAlbums, - selectedBackupAlbums, - ); + selectedAlbums = _updateAlbumsBackupTime(selectedAlbums, selectedBackupAlbums); } if (excludedAlbums.isNotEmpty) { - excludedAlbums = _updateAlbumsBackupTime( - excludedAlbums, - excludedBackupAlbums, - ); + excludedAlbums = _updateAlbumsBackupTime(excludedAlbums, excludedBackupAlbums); } final BackUpProgressEnum previous = state.backupProgress; state = state.copyWith( @@ -730,32 +643,21 @@ class BackupNotifier extends StateNotifier { return _resumeBackup(); } - Set _updateAlbumsBackupTime( - Set albums, - List backupAlbums, - ) { + Set _updateAlbumsBackupTime(Set albums, List backupAlbums) { Set result = {}; for (BackupAlbum ba in backupAlbums) { try { AvailableAlbum a = albums.firstWhere((e) => e.id == ba.id); result.add(a.copyWith(lastBackup: ba.lastBackup)); } on StateError { - log.severe( - "[_updateAlbumBackupTime] failed to find album in state", - "State Error", - StackTrace.current, - ); + log.severe("[_updateAlbumBackupTime] failed to find album in state", "State Error", StackTrace.current); } } return result; } Future notifyBackgroundServiceCanRun() async { - const allowedStates = [ - AppLifeCycleEnum.inactive, - AppLifeCycleEnum.paused, - AppLifeCycleEnum.detached, - ]; + const allowedStates = [AppLifeCycleEnum.inactive, AppLifeCycleEnum.paused, AppLifeCycleEnum.detached]; if (allowedStates.contains(ref.read(appStateProvider.notifier).state)) { _backgroundService.releaseLock(); } diff --git a/mobile/lib/providers/backup/backup_album.provider.dart b/mobile/lib/providers/backup/backup_album.provider.dart new file mode 100644 index 0000000000..f81f905c2f --- /dev/null +++ b/mobile/lib/providers/backup/backup_album.provider.dart @@ -0,0 +1,59 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/album/local_album.model.dart'; +import 'package:immich_mobile/domain/services/local_album.service.dart'; +import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart'; +import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; + +final backupAlbumProvider = StateNotifierProvider>( + (ref) => BackupAlbumNotifier(ref.watch(localAlbumServiceProvider)), +); + +class BackupAlbumNotifier extends StateNotifier> { + BackupAlbumNotifier(this._localAlbumService) : super([]) { + getAll(); + } + + final LocalAlbumService _localAlbumService; + + Future getAll() async { + state = await _localAlbumService.getAll(sortBy: {SortLocalAlbumsBy.assetCount}); + } + + Future selectAlbum(LocalAlbum album) async { + album = album.copyWith(backupSelection: BackupSelection.selected); + await _localAlbumService.update(album); + + state = state + .map( + (currentAlbum) => currentAlbum.id == album.id + ? currentAlbum.copyWith(backupSelection: BackupSelection.selected) + : currentAlbum, + ) + .toList(); + } + + Future deselectAlbum(LocalAlbum album) async { + album = album.copyWith(backupSelection: BackupSelection.none); + await _localAlbumService.update(album); + + state = state + .map( + (currentAlbum) => + currentAlbum.id == album.id ? currentAlbum.copyWith(backupSelection: BackupSelection.none) : currentAlbum, + ) + .toList(); + } + + Future excludeAlbum(LocalAlbum album) async { + album = album.copyWith(backupSelection: BackupSelection.excluded); + await _localAlbumService.update(album); + + state = state + .map( + (currentAlbum) => currentAlbum.id == album.id + ? currentAlbum.copyWith(backupSelection: BackupSelection.excluded) + : currentAlbum, + ) + .toList(); + } +} diff --git a/mobile/lib/providers/backup/backup_verification.provider.dart b/mobile/lib/providers/backup/backup_verification.provider.dart index 5881814320..da4253576b 100644 --- a/mobile/lib/providers/backup/backup_verification.provider.dart +++ b/mobile/lib/providers/backup/backup_verification.provider.dart @@ -23,8 +23,7 @@ class BackupVerification extends _$BackupVerification { state = true; final backupState = ref.read(backupProvider); - if (backupState.allUniqueAssets.length > - backupState.selectedAlbumsBackupAssetsIds.length) { + if (backupState.allUniqueAssets.length > backupState.selectedAlbumsBackupAssetsIds.length) { if (context.mounted) { ImmichToast.show( context: context, @@ -48,9 +47,7 @@ class BackupVerification extends _$BackupVerification { WakelockPlus.enable(); const limit = 100; - final toDelete = await ref - .read(backupVerificationServiceProvider) - .findWronglyBackedUpAssets(limit: limit); + final toDelete = await ref.read(backupVerificationServiceProvider).findWronglyBackedUpAssets(limit: limit); if (toDelete.isEmpty) { if (context.mounted) { ImmichToast.show( @@ -81,23 +78,18 @@ class BackupVerification extends _$BackupVerification { } } - Future _performDeletion( - BuildContext context, - List assets, - ) async { + Future _performDeletion(BuildContext context, List assets) async { try { state = true; if (context.mounted) { - ImmichToast.show( - context: context, - msg: "Deleting ${assets.length} assets on the server...", - ); + ImmichToast.show(context: context, msg: "Deleting ${assets.length} assets on the server..."); } await ref.read(assetProvider.notifier).deleteAssets(assets, force: true); if (context.mounted) { ImmichToast.show( context: context, - msg: "Deleted ${assets.length} assets on the server. " + msg: + "Deleted ${assets.length} assets on the server. " "You can now start a manual backup", toastType: ToastType.success, ); diff --git a/mobile/lib/providers/backup/backup_verification.provider.g.dart b/mobile/lib/providers/backup/backup_verification.provider.g.dart index bae3ec366b..727e06a12c 100644 --- a/mobile/lib/providers/backup/backup_verification.provider.g.dart +++ b/mobile/lib/providers/backup/backup_verification.provider.g.dart @@ -13,14 +13,14 @@ String _$backupVerificationHash() => @ProviderFor(BackupVerification) final backupVerificationProvider = AutoDisposeNotifierProvider.internal( - BackupVerification.new, - name: r'backupVerificationProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$backupVerificationHash, - dependencies: null, - allTransitiveDependencies: null, -); + BackupVerification.new, + name: r'backupVerificationProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$backupVerificationHash, + dependencies: null, + allTransitiveDependencies: null, + ); typedef _$BackupVerification = AutoDisposeNotifier; // ignore_for_file: type=lint diff --git a/mobile/lib/providers/backup/drift_backup.provider.dart b/mobile/lib/providers/backup/drift_backup.provider.dart new file mode 100644 index 0000000000..39949fd526 --- /dev/null +++ b/mobile/lib/providers/backup/drift_backup.provider.dart @@ -0,0 +1,356 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'dart:async'; +import 'dart:convert'; + +import 'package:background_downloader/background_downloader.dart'; +import 'package:collection/collection.dart'; +import 'package:flutter/widgets.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; + +import 'package:immich_mobile/constants/constants.dart'; +import 'package:immich_mobile/services/upload.service.dart'; + +class EnqueueStatus { + final int enqueueCount; + final int totalCount; + + const EnqueueStatus({required this.enqueueCount, required this.totalCount}); + + EnqueueStatus copyWith({int? enqueueCount, int? totalCount}) { + return EnqueueStatus(enqueueCount: enqueueCount ?? this.enqueueCount, totalCount: totalCount ?? this.totalCount); + } + + @override + String toString() => 'EnqueueStatus(enqueueCount: $enqueueCount, totalCount: $totalCount)'; +} + +class DriftUploadStatus { + final String taskId; + final String filename; + final double progress; + final int fileSize; + final String networkSpeedAsString; + final bool? isFailed; + + const DriftUploadStatus({ + required this.taskId, + required this.filename, + required this.progress, + required this.fileSize, + required this.networkSpeedAsString, + this.isFailed, + }); + + DriftUploadStatus copyWith({ + String? taskId, + String? filename, + double? progress, + int? fileSize, + String? networkSpeedAsString, + bool? isFailed, + }) { + return DriftUploadStatus( + taskId: taskId ?? this.taskId, + filename: filename ?? this.filename, + progress: progress ?? this.progress, + fileSize: fileSize ?? this.fileSize, + networkSpeedAsString: networkSpeedAsString ?? this.networkSpeedAsString, + isFailed: isFailed ?? this.isFailed, + ); + } + + @override + String toString() { + return 'DriftUploadStatus(taskId: $taskId, filename: $filename, progress: $progress, fileSize: $fileSize, networkSpeedAsString: $networkSpeedAsString, isFailed: $isFailed)'; + } + + @override + bool operator ==(covariant DriftUploadStatus other) { + if (identical(this, other)) return true; + + return other.taskId == taskId && + other.filename == filename && + other.progress == progress && + other.fileSize == fileSize && + other.networkSpeedAsString == networkSpeedAsString && + other.isFailed == isFailed; + } + + @override + int get hashCode { + return taskId.hashCode ^ + filename.hashCode ^ + progress.hashCode ^ + fileSize.hashCode ^ + networkSpeedAsString.hashCode ^ + isFailed.hashCode; + } + + Map toMap() { + return { + 'taskId': taskId, + 'filename': filename, + 'progress': progress, + 'fileSize': fileSize, + 'networkSpeedAsString': networkSpeedAsString, + 'isFailed': isFailed, + }; + } + + factory DriftUploadStatus.fromMap(Map map) { + return DriftUploadStatus( + taskId: map['taskId'] as String, + filename: map['filename'] as String, + progress: map['progress'] as double, + fileSize: map['fileSize'] as int, + networkSpeedAsString: map['networkSpeedAsString'] as String, + isFailed: map['isFailed'] != null ? map['isFailed'] as bool : null, + ); + } + + String toJson() => json.encode(toMap()); + + factory DriftUploadStatus.fromJson(String source) => + DriftUploadStatus.fromMap(json.decode(source) as Map); +} + +class DriftBackupState { + final int totalCount; + final int backupCount; + final int remainderCount; + + final int enqueueCount; + final int enqueueTotalCount; + + final bool isCanceling; + + final Map uploadItems; + + const DriftBackupState({ + required this.totalCount, + required this.backupCount, + required this.remainderCount, + required this.enqueueCount, + required this.enqueueTotalCount, + required this.isCanceling, + required this.uploadItems, + }); + + DriftBackupState copyWith({ + int? totalCount, + int? backupCount, + int? remainderCount, + int? enqueueCount, + int? enqueueTotalCount, + bool? isCanceling, + Map? uploadItems, + }) { + return DriftBackupState( + totalCount: totalCount ?? this.totalCount, + backupCount: backupCount ?? this.backupCount, + remainderCount: remainderCount ?? this.remainderCount, + enqueueCount: enqueueCount ?? this.enqueueCount, + enqueueTotalCount: enqueueTotalCount ?? this.enqueueTotalCount, + isCanceling: isCanceling ?? this.isCanceling, + uploadItems: uploadItems ?? this.uploadItems, + ); + } + + @override + String toString() { + return 'DriftBackupState(totalCount: $totalCount, backupCount: $backupCount, remainderCount: $remainderCount, enqueueCount: $enqueueCount, enqueueTotalCount: $enqueueTotalCount, isCanceling: $isCanceling, uploadItems: $uploadItems)'; + } + + @override + bool operator ==(covariant DriftBackupState other) { + if (identical(this, other)) return true; + final mapEquals = const DeepCollectionEquality().equals; + + return other.totalCount == totalCount && + other.backupCount == backupCount && + other.remainderCount == remainderCount && + other.enqueueCount == enqueueCount && + other.enqueueTotalCount == enqueueTotalCount && + other.isCanceling == isCanceling && + mapEquals(other.uploadItems, uploadItems); + } + + @override + int get hashCode { + return totalCount.hashCode ^ + backupCount.hashCode ^ + remainderCount.hashCode ^ + enqueueCount.hashCode ^ + enqueueTotalCount.hashCode ^ + isCanceling.hashCode ^ + uploadItems.hashCode; + } +} + +final driftBackupProvider = StateNotifierProvider((ref) { + return DriftBackupNotifier(ref.watch(uploadServiceProvider)); +}); + +class DriftBackupNotifier extends StateNotifier { + DriftBackupNotifier(this._uploadService) + : super( + const DriftBackupState( + totalCount: 0, + backupCount: 0, + remainderCount: 0, + enqueueCount: 0, + enqueueTotalCount: 0, + isCanceling: false, + uploadItems: {}, + ), + ) { + { + _uploadService.taskStatusStream.listen(_handleTaskStatusUpdate); + _uploadService.taskProgressStream.listen(_handleTaskProgressUpdate); + } + } + + final UploadService _uploadService; + StreamSubscription? _statusSubscription; + StreamSubscription? _progressSubscription; + + /// Remove upload item from state + void _removeUploadItem(String taskId) { + if (state.uploadItems.containsKey(taskId)) { + final updatedItems = Map.from(state.uploadItems); + updatedItems.remove(taskId); + state = state.copyWith(uploadItems: updatedItems); + } + } + + void _handleTaskStatusUpdate(TaskStatusUpdate update) { + final taskId = update.task.taskId; + + switch (update.status) { + case TaskStatus.complete: + if (update.task.group == kBackupGroup) { + state = state.copyWith(backupCount: state.backupCount + 1, remainderCount: state.remainderCount - 1); + } + + // Remove the completed task from the upload items + if (state.uploadItems.containsKey(taskId)) { + Future.delayed(const Duration(milliseconds: 1000), () { + _removeUploadItem(taskId); + }); + } + + case TaskStatus.failed: + final currentItem = state.uploadItems[taskId]; + if (currentItem == null) { + return; + } + + state = state.copyWith(uploadItems: {...state.uploadItems, taskId: currentItem.copyWith(isFailed: true)}); + break; + + case TaskStatus.canceled: + _removeUploadItem(update.task.taskId); + break; + + default: + break; + } + } + + void _handleTaskProgressUpdate(TaskProgressUpdate update) { + final taskId = update.task.taskId; + final filename = update.task.displayName; + final progress = update.progress; + final currentItem = state.uploadItems[taskId]; + if (currentItem != null) { + if (progress == kUploadStatusCanceled) { + _removeUploadItem(update.task.taskId); + return; + } + + state = state.copyWith( + uploadItems: { + ...state.uploadItems, + taskId: update.hasExpectedFileSize + ? currentItem.copyWith( + progress: progress, + fileSize: update.expectedFileSize, + networkSpeedAsString: update.networkSpeedAsString, + ) + : currentItem.copyWith(progress: progress), + }, + ); + + return; + } + + state = state.copyWith( + uploadItems: { + ...state.uploadItems, + taskId: DriftUploadStatus( + taskId: taskId, + filename: filename, + progress: progress, + fileSize: update.expectedFileSize, + networkSpeedAsString: update.networkSpeedAsString, + ), + }, + ); + } + + Future getBackupStatus(String userId) async { + final [totalCount, backupCount, remainderCount] = await Future.wait([ + _uploadService.getBackupTotalCount(), + _uploadService.getBackupFinishedCount(userId), + _uploadService.getBackupRemainderCount(userId), + ]); + + state = state.copyWith(totalCount: totalCount, backupCount: backupCount, remainderCount: remainderCount); + } + + Future startBackup(String userId) { + return _uploadService.startBackup(userId, _updateEnqueueCount); + } + + void _updateEnqueueCount(EnqueueStatus status) { + state = state.copyWith(enqueueCount: status.enqueueCount, enqueueTotalCount: status.totalCount); + } + + Future cancel() async { + debugPrint("Canceling backup tasks..."); + state = state.copyWith(enqueueCount: 0, enqueueTotalCount: 0, isCanceling: true); + + final activeTaskCount = await _uploadService.cancelBackup(); + + if (activeTaskCount > 0) { + debugPrint("$activeTaskCount tasks left, continuing to cancel..."); + await cancel(); + } else { + debugPrint("All tasks canceled successfully."); + // Clear all upload items when cancellation is complete + state = state.copyWith(isCanceling: false, uploadItems: {}); + } + } + + Future handleBackupResume(String userId) async { + debugPrint("handleBackupResume"); + final tasks = await _uploadService.getActiveTasks(kBackupGroup); + debugPrint("Found ${tasks.length} tasks"); + + if (tasks.isEmpty) { + // Start a new backup queue + debugPrint("Start a new backup queue"); + await startBackup(userId); + } + + debugPrint("Tasks to resume: ${tasks.length}"); + await _uploadService.resumeBackup(); + } + + @override + void dispose() { + _statusSubscription?.cancel(); + _progressSubscription?.cancel(); + super.dispose(); + } +} diff --git a/mobile/lib/providers/backup/error_backup_list.provider.dart b/mobile/lib/providers/backup/error_backup_list.provider.dart index 22ff995905..db116e4bb9 100644 --- a/mobile/lib/providers/backup/error_backup_list.provider.dart +++ b/mobile/lib/providers/backup/error_backup_list.provider.dart @@ -17,7 +17,6 @@ class ErrorBackupListNotifier extends StateNotifier> { } } -final errorBackupListProvider = - StateNotifierProvider>( +final errorBackupListProvider = StateNotifierProvider>( (ref) => ErrorBackupListNotifier(), ); diff --git a/mobile/lib/providers/backup/ios_background_settings.provider.dart b/mobile/lib/providers/backup/ios_background_settings.provider.dart index d70e674845..98d55882cc 100644 --- a/mobile/lib/providers/backup/ios_background_settings.provider.dart +++ b/mobile/lib/providers/backup/ios_background_settings.provider.dart @@ -15,21 +15,17 @@ class IOSBackgroundSettings { }); } -class IOSBackgroundSettingsNotifier - extends StateNotifier { +class IOSBackgroundSettingsNotifier extends StateNotifier { final BackgroundService _service; IOSBackgroundSettingsNotifier(this._service) : super(null); IOSBackgroundSettings? get settings => state; Future refresh() async { - final lastFetchTime = - await _service.getIOSBackupLastRun(IosBackgroundTask.fetch); - final lastProcessingTime = - await _service.getIOSBackupLastRun(IosBackgroundTask.processing); + final lastFetchTime = await _service.getIOSBackupLastRun(IosBackgroundTask.fetch); + final lastProcessingTime = await _service.getIOSBackupLastRun(IosBackgroundTask.processing); int numberOfProcesses = await _service.getIOSBackupNumberOfProcesses(); - final appRefreshEnabled = - await _service.getIOSBackgroundAppRefreshEnabled(); + final appRefreshEnabled = await _service.getIOSBackgroundAppRefreshEnabled(); // If this is enabled and there are no background processes, // the user just enabled app refresh in Settings. @@ -53,7 +49,6 @@ class IOSBackgroundSettingsNotifier } } -final iOSBackgroundSettingsProvider = StateNotifierProvider< - IOSBackgroundSettingsNotifier, IOSBackgroundSettings?>( +final iOSBackgroundSettingsProvider = StateNotifierProvider( (ref) => IOSBackgroundSettingsNotifier(ref.watch(backgroundServiceProvider)), ); diff --git a/mobile/lib/providers/backup/manual_upload.provider.dart b/mobile/lib/providers/backup/manual_upload.provider.dart index 646a03cebc..1aea7f64cb 100644 --- a/mobile/lib/providers/backup/manual_upload.provider.dart +++ b/mobile/lib/providers/backup/manual_upload.provider.dart @@ -31,8 +31,7 @@ import 'package:logging/logging.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:photo_manager/photo_manager.dart' show PMProgressHandler; -final manualUploadProvider = - StateNotifierProvider((ref) { +final manualUploadProvider = StateNotifierProvider((ref) { return ManualUploadNotifier( ref.watch(localNotificationService), ref.watch(backupProvider.notifier), @@ -57,45 +56,43 @@ class ManualUploadNotifier extends StateNotifier { this._backupAlbumService, this.ref, ) : super( - ManualUploadState( - progressInPercentage: 0, - progressInFileSize: "0 B / 0 B", - progressInFileSpeed: 0, - progressInFileSpeeds: const [], - progressInFileSpeedUpdateTime: DateTime.now(), - progressInFileSpeedUpdateSentBytes: 0, - cancelToken: CancellationToken(), - currentUploadAsset: CurrentUploadAsset( - id: '...', - fileCreatedAt: DateTime.parse('2020-10-04'), - fileName: '...', - fileType: '...', - ), - totalAssetsToUpload: 0, - successfulUploads: 0, - currentAssetIndex: 0, - showDetailedNotification: false, + ManualUploadState( + progressInPercentage: 0, + progressInFileSize: "0 B / 0 B", + progressInFileSpeed: 0, + progressInFileSpeeds: const [], + progressInFileSpeedUpdateTime: DateTime.now(), + progressInFileSpeedUpdateSentBytes: 0, + cancelToken: CancellationToken(), + currentUploadAsset: CurrentUploadAsset( + id: '...', + fileCreatedAt: DateTime.parse('2020-10-04'), + fileName: '...', + fileType: '...', ), - ); + totalAssetsToUpload: 0, + successfulUploads: 0, + currentAssetIndex: 0, + showDetailedNotification: false, + ), + ); String _lastPrintedDetailContent = ''; String? _lastPrintedDetailTitle; static const notifyInterval = Duration(milliseconds: 500); - late final ThrottleProgressUpdate _throttledNotifiy = - ThrottleProgressUpdate(_updateProgress, notifyInterval); - late final ThrottleProgressUpdate _throttledDetailNotify = - ThrottleProgressUpdate(_updateDetailProgress, notifyInterval); + late final ThrottleProgressUpdate _throttledNotifiy = ThrottleProgressUpdate(_updateProgress, notifyInterval); + late final ThrottleProgressUpdate _throttledDetailNotify = ThrottleProgressUpdate( + _updateDetailProgress, + notifyInterval, + ); void _updateProgress(String? title, int progress, int total) { // Guard against throttling calling this method after the upload is done if (_backupProvider.backupProgress == BackUpProgressEnum.manualInProgress) { _localNotificationService.showOrUpdateManualUploadStatus( "backup_background_service_in_progress_notification".tr(), - formatAssetBackupProgress( - state.currentAssetIndex, - state.totalAssetsToUpload, - ), + formatAssetBackupProgress(state.currentAssetIndex, state.totalAssetsToUpload), maxProgress: state.totalAssetsToUpload, progress: state.currentAssetIndex, showActions: true, @@ -106,11 +103,9 @@ class ManualUploadNotifier extends StateNotifier { void _updateDetailProgress(String? title, int progress, int total) { // Guard against throttling calling this method after the upload is done if (_backupProvider.backupProgress == BackUpProgressEnum.manualInProgress) { - final String msg = - total > 0 ? humanReadableBytesProgress(progress, total) : ""; + final String msg = total > 0 ? humanReadableBytesProgress(progress, total) : ""; // only update if message actually differs (to stop many useless notification updates on large assets or slow connections) - if (msg != _lastPrintedDetailContent || - title != _lastPrintedDetailTitle) { + if (msg != _lastPrintedDetailContent || title != _lastPrintedDetailTitle) { _lastPrintedDetailContent = msg; _lastPrintedDetailTitle = title; _localNotificationService.showOrUpdateManualUploadStatus( @@ -150,9 +145,7 @@ class ManualUploadNotifier extends StateNotifier { } if (duration.inSeconds > 0) { - lastUploadSpeeds.add( - ((sent - lastSentBytes) / duration.inSeconds).abs().roundToDouble(), - ); + lastUploadSpeeds.add(((sent - lastSentBytes) / duration.inSeconds).abs().roundToDouble()); lastUploadSpeed = lastUploadSpeeds.average.abs().roundToDouble(); lastUpdateTime = now; @@ -169,24 +162,22 @@ class ManualUploadNotifier extends StateNotifier { ); if (state.showDetailedNotification) { - final title = "backup_background_service_current_upload_notification" - .tr(namedArgs: {'filename': state.currentUploadAsset.fileName}); + final title = "backup_background_service_current_upload_notification".tr( + namedArgs: {'filename': state.currentUploadAsset.fileName}, + ); _throttledDetailNotify(title: title, progress: sent, total: total); } } void _onSetCurrentBackupAsset(CurrentUploadAsset currentUploadAsset) { - state = state.copyWith( - currentUploadAsset: currentUploadAsset, - currentAssetIndex: state.currentAssetIndex + 1, - ); + state = state.copyWith(currentUploadAsset: currentUploadAsset, currentAssetIndex: state.currentAssetIndex + 1); if (state.totalAssetsToUpload > 1) { _throttledNotifiy(); } if (state.showDetailedNotification) { - _throttledDetailNotify.title = - "backup_background_service_current_upload_notification" - .tr(namedArgs: {'filename': currentUploadAsset.fileName}); + _throttledDetailNotify.title = "backup_background_service_current_upload_notification".tr( + namedArgs: {'filename': currentUploadAsset.fileName}, + ); _throttledDetailNotify.progress = 0; _throttledDetailNotify.total = 0; } @@ -200,8 +191,7 @@ class ManualUploadNotifier extends StateNotifier { if (ref.read(galleryPermissionNotifier.notifier).hasPermission) { await ref.read(fileMediaRepositoryProvider).clearFileCache(); - final allAssetsFromDevice = - allManualUploads.where((e) => e.isLocal && !e.isRemote).toList(); + final allAssetsFromDevice = allManualUploads.where((e) => e.isLocal && !e.isRemote).toList(); if (allAssetsFromDevice.length != allManualUploads.length) { _log.warning( @@ -209,14 +199,11 @@ class ManualUploadNotifier extends StateNotifier { ); } - final selectedBackupAlbums = - await _backupAlbumService.getAllBySelection(BackupSelection.select); - final excludedBackupAlbums = await _backupAlbumService - .getAllBySelection(BackupSelection.exclude); + final selectedBackupAlbums = await _backupAlbumService.getAllBySelection(BackupSelection.select); + final excludedBackupAlbums = await _backupAlbumService.getAllBySelection(BackupSelection.exclude); // Get candidates from selected albums and excluded albums - Set candidates = - await _backupService.buildUploadCandidates( + Set candidates = await _backupService.buildUploadCandidates( selectedBackupAlbums, excludedBackupAlbums, useTimeFilter: false, @@ -225,10 +212,7 @@ class ManualUploadNotifier extends StateNotifier { // Extrack candidate from allAssetsFromDevice final uploadAssets = candidates.where( (candidate) => - allAssetsFromDevice.firstWhereOrNull( - (asset) => asset.localId == candidate.asset.localId, - ) != - null, + allAssetsFromDevice.firstWhereOrNull((asset) => asset.localId == candidate.asset.localId) != null, ); if (uploadAssets.isEmpty) { @@ -261,15 +245,14 @@ class ManualUploadNotifier extends StateNotifier { // Show detailed asset if enabled in settings or if a single asset is uploaded bool showDetailedNotification = - ref.read(appSettingsServiceProvider).getSetting( - AppSettingsEnum.backgroundBackupSingleProgress, - ) || - state.totalAssetsToUpload == 1; - state = - state.copyWith(showDetailedNotification: showDetailedNotification); + ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.backgroundBackupSingleProgress) || + state.totalAssetsToUpload == 1; + state = state.copyWith(showDetailedNotification: showDetailedNotification); final pmProgressHandler = Platform.isIOS ? PMProgressHandler() : null; - final bool ok = await ref.read(backupServiceProvider).backupAsset( + final bool ok = await ref + .read(backupServiceProvider) + .backupAsset( uploadAssets, state.cancelToken, pmProgressHandler: pmProgressHandler, @@ -280,9 +263,7 @@ class ManualUploadNotifier extends StateNotifier { ); // Close detailed notification - await _localNotificationService.closeNotification( - LocalNotificationService.manualUploadDetailedNotificationID, - ); + await _localNotificationService.closeNotification(LocalNotificationService.manualUploadDetailedNotificationID); _log.info( '[_startUpload] Manual Upload Completed - success: ${state.successfulUploads},' @@ -297,8 +278,7 @@ class ManualUploadNotifier extends StateNotifier { presentBanner: true, ); hasErrors = true; - } else if (state.successfulUploads == 0 || - (!ok && !state.cancelToken.isCancelled)) { + } else if (state.successfulUploads == 0 || (!ok && !state.cancelToken.isCancelled)) { await _localNotificationService.showOrUpdateManualUploadStatus( "backup_manual_title".tr(), "failed".tr(), @@ -322,9 +302,7 @@ class ManualUploadNotifier extends StateNotifier { } finally { _backupProvider.updateBackupProgress(BackUpProgressEnum.idle); _handleAppInActivity(); - await _localNotificationService.closeNotification( - LocalNotificationService.manualUploadDetailedNotificationID, - ); + await _localNotificationService.closeNotification(LocalNotificationService.manualUploadDetailedNotificationID); await _backupProvider.notifyBackgroundServiceCanRun(); } return !hasErrors; @@ -334,8 +312,7 @@ class ManualUploadNotifier extends StateNotifier { final appState = ref.read(appStateProvider.notifier).getAppState(); // The app is currently in background. Perform the necessary cleanups which // are on-hold for upload completion - if (appState != AppLifeCycleEnum.active && - appState != AppLifeCycleEnum.resumed) { + if (appState != AppLifeCycleEnum.active && appState != AppLifeCycleEnum.resumed) { ref.read(backupProvider.notifier).cancelBackup(); } } @@ -358,14 +335,10 @@ class ManualUploadNotifier extends StateNotifier { ); } - Future uploadAssets( - BuildContext context, - Iterable allManualUploads, - ) async { + Future uploadAssets(BuildContext context, Iterable allManualUploads) async { // assumes the background service is currently running and // waits until it has stopped to start the backup. - final bool hasLock = - await ref.read(backgroundServiceProvider).acquireLock(); + final bool hasLock = await ref.read(backgroundServiceProvider).acquireLock(); if (!hasLock) { debugPrint("[uploadAssets] could not acquire lock, exiting"); ImmichToast.show( diff --git a/mobile/lib/providers/cast.provider.dart b/mobile/lib/providers/cast.provider.dart index 11cdcd54c5..75a2a35fb6 100644 --- a/mobile/lib/providers/cast.provider.dart +++ b/mobile/lib/providers/cast.provider.dart @@ -15,15 +15,15 @@ class CastNotifier extends StateNotifier { List<(String, CastDestinationType, dynamic)> discovered = List.empty(); CastNotifier(this._gCastService) - : super( - const CastManagerState( - isCasting: false, - currentTime: Duration.zero, - duration: Duration.zero, - receiverName: '', - castState: CastState.idle, - ), - ) { + : super( + const CastManagerState( + isCasting: false, + currentTime: Duration.zero, + duration: Duration.zero, + receiverName: '', + castState: CastState.idle, + ), + ) { _gCastService.onConnectionState = _onConnectionState; _gCastService.onCurrentTime = _onCurrentTime; _gCastService.onDuration = _onDuration; @@ -65,8 +65,8 @@ class CastNotifier extends StateNotifier { type: asset.type == old_asset_entity.AssetType.image ? AssetType.image : asset.type == old_asset_entity.AssetType.video - ? AssetType.video - : AssetType.other, + ? AssetType.video + : AssetType.other, createdAt: asset.fileCreatedAt, updatedAt: asset.updatedAt, ); diff --git a/mobile/lib/providers/folder.provider.dart b/mobile/lib/providers/folder.provider.dart index 810c2cea73..696d7e19fd 100644 --- a/mobile/lib/providers/folder.provider.dart +++ b/mobile/lib/providers/folder.provider.dart @@ -22,12 +22,8 @@ class FolderStructureNotifier extends StateNotifier> { } } -final folderStructureProvider = - StateNotifierProvider>( - (ref) { - return FolderStructureNotifier( - ref.watch(folderServiceProvider), - ); +final folderStructureProvider = StateNotifierProvider>((ref) { + return FolderStructureNotifier(ref.watch(folderServiceProvider)); }); class FolderRenderListNotifier extends StateNotifier> { @@ -35,14 +31,12 @@ class FolderRenderListNotifier extends StateNotifier> { final RootFolder _folder; final Logger _log = Logger("FolderAssetsNotifier"); - FolderRenderListNotifier(this._folderService, this._folder) - : super(const AsyncLoading()); + FolderRenderListNotifier(this._folderService, this._folder) : super(const AsyncLoading()); Future fetchAssets(SortOrder order) async { try { final assets = await _folderService.getFolderAssets(_folder, order); - final renderList = - await RenderList.fromAssets(assets, GroupAssetsBy.none); + final renderList = await RenderList.fromAssets(assets, GroupAssetsBy.none); state = AsyncData(renderList); } catch (e, stack) { _log.severe("Failed to fetch folder assets", e, stack); @@ -51,12 +45,7 @@ class FolderRenderListNotifier extends StateNotifier> { } } -final folderRenderListProvider = StateNotifierProvider.family< - FolderRenderListNotifier, - AsyncValue, - RootFolder>((ref, folder) { - return FolderRenderListNotifier( - ref.watch(folderServiceProvider), - folder, - ); -}); +final folderRenderListProvider = + StateNotifierProvider.family, RootFolder>((ref, folder) { + return FolderRenderListNotifier(ref.watch(folderServiceProvider), folder); + }); diff --git a/mobile/lib/providers/gallery_permission.provider.dart b/mobile/lib/providers/gallery_permission.provider.dart index 07d9cca591..6e4fc69926 100644 --- a/mobile/lib/providers/gallery_permission.provider.dart +++ b/mobile/lib/providers/gallery_permission.provider.dart @@ -6,8 +6,8 @@ import 'package:permission_handler/permission_handler.dart'; class GalleryPermissionNotifier extends StateNotifier { GalleryPermissionNotifier() - : super(PermissionStatus.denied) // Denied is the initial state - { + : super(PermissionStatus.denied) // Denied is the initial state + { // Sets the initial state getGalleryPermissionStatus(); } @@ -36,8 +36,7 @@ class GalleryPermissionNotifier extends StateNotifier { // Return the joint result of those two permissions final PermissionStatus status; - if ((photos.isGranted && videos.isGranted) || - (photos.isLimited && videos.isLimited)) { + if ((photos.isGranted && videos.isGranted) || (photos.isLimited && videos.isLimited)) { status = PermissionStatus.granted; } else if (photos.isDenied || videos.isDenied) { status = PermissionStatus.denied; @@ -49,8 +48,7 @@ class GalleryPermissionNotifier extends StateNotifier { result = status; } - if (result == PermissionStatus.granted && - androidInfo.version.sdkInt >= 29) { + if (result == PermissionStatus.granted && androidInfo.version.sdkInt >= 29) { result = await Permission.accessMediaLocation.request(); } } else { @@ -80,8 +78,7 @@ class GalleryPermissionNotifier extends StateNotifier { // Return the joint result of those two permissions final PermissionStatus status; - if ((photos.isGranted && videos.isGranted) || - (photos.isLimited && videos.isLimited)) { + if ((photos.isGranted && videos.isGranted) || (photos.isLimited && videos.isLimited)) { status = PermissionStatus.granted; } else if (photos.isDenied || videos.isDenied) { status = PermissionStatus.denied; @@ -93,8 +90,7 @@ class GalleryPermissionNotifier extends StateNotifier { result = status; } - if (state == PermissionStatus.granted && - androidInfo.version.sdkInt >= 29) { + if (state == PermissionStatus.granted && androidInfo.version.sdkInt >= 29) { result = await Permission.accessMediaLocation.status; } } else { @@ -107,7 +103,6 @@ class GalleryPermissionNotifier extends StateNotifier { } } -final galleryPermissionNotifier = - StateNotifierProvider( +final galleryPermissionNotifier = StateNotifierProvider( (ref) => GalleryPermissionNotifier(), ); diff --git a/mobile/lib/providers/haptic_feedback.provider.dart b/mobile/lib/providers/haptic_feedback.provider.dart index ce8997c85c..711c6fa4e2 100644 --- a/mobile/lib/providers/haptic_feedback.provider.dart +++ b/mobile/lib/providers/haptic_feedback.provider.dart @@ -3,8 +3,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; -final hapticFeedbackProvider = - StateNotifierProvider((ref) { +final hapticFeedbackProvider = StateNotifierProvider((ref) { return HapticNotifier(ref); }); @@ -15,41 +14,31 @@ class HapticNotifier extends StateNotifier { HapticNotifier(this._ref) : super(null); selectionClick() { - if (_ref - .read(appSettingsServiceProvider) - .getSetting(AppSettingsEnum.enableHapticFeedback)) { + if (_ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableHapticFeedback)) { HapticFeedback.selectionClick(); } } lightImpact() { - if (_ref - .read(appSettingsServiceProvider) - .getSetting(AppSettingsEnum.enableHapticFeedback)) { + if (_ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableHapticFeedback)) { HapticFeedback.lightImpact(); } } mediumImpact() { - if (_ref - .read(appSettingsServiceProvider) - .getSetting(AppSettingsEnum.enableHapticFeedback)) { + if (_ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableHapticFeedback)) { HapticFeedback.mediumImpact(); } } heavyImpact() { - if (_ref - .read(appSettingsServiceProvider) - .getSetting(AppSettingsEnum.enableHapticFeedback)) { + if (_ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableHapticFeedback)) { HapticFeedback.heavyImpact(); } } vibrate() { - if (_ref - .read(appSettingsServiceProvider) - .getSetting(AppSettingsEnum.enableHapticFeedback)) { + if (_ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableHapticFeedback)) { HapticFeedback.vibrate(); } } diff --git a/mobile/lib/providers/image/cache/image_loader.dart b/mobile/lib/providers/image/cache/image_loader.dart index f88d54e4f1..50530f7cdf 100644 --- a/mobile/lib/providers/image/cache/image_loader.dart +++ b/mobile/lib/providers/image/cache/image_loader.dart @@ -19,20 +19,13 @@ class ImageLoader { }) async { final headers = ApiService.getRequestHeaders(); - final stream = cache.getFileStream( - uri, - withProgress: chunkEvents != null, - headers: headers, - ); + final stream = cache.getFileStream(uri, withProgress: chunkEvents != null, headers: headers); await for (final result in stream) { if (result is DownloadProgress) { // We are downloading the file, so update the [chunkEvents] chunkEvents?.add( - ImageChunkEvent( - cumulativeBytesLoaded: result.downloaded, - expectedTotalBytes: result.totalSize, - ), + ImageChunkEvent(cumulativeBytesLoaded: result.downloaded, expectedTotalBytes: result.totalSize), ); } else if (result is FileInfo) { // We have the file diff --git a/mobile/lib/providers/image/cache/remote_image_cache_manager.dart b/mobile/lib/providers/image/cache/remote_image_cache_manager.dart index da20f46c62..b9e2880c04 100644 --- a/mobile/lib/providers/image/cache/remote_image_cache_manager.dart +++ b/mobile/lib/providers/image/cache/remote_image_cache_manager.dart @@ -9,12 +9,5 @@ class RemoteImageCacheManager extends CacheManager { return _instance; } - RemoteImageCacheManager._() - : super( - Config( - key, - maxNrOfCacheObjects: 500, - stalePeriod: const Duration(days: 30), - ), - ); + RemoteImageCacheManager._() : super(Config(key, maxNrOfCacheObjects: 500, stalePeriod: const Duration(days: 30))); } diff --git a/mobile/lib/providers/image/cache/thumbnail_image_cache_manager.dart b/mobile/lib/providers/image/cache/thumbnail_image_cache_manager.dart index dd7ad35277..bfea36eef6 100644 --- a/mobile/lib/providers/image/cache/thumbnail_image_cache_manager.dart +++ b/mobile/lib/providers/image/cache/thumbnail_image_cache_manager.dart @@ -3,19 +3,11 @@ import 'package:flutter_cache_manager/flutter_cache_manager.dart'; /// The cache manager for thumbnail images [ImmichRemoteThumbnailProvider] class ThumbnailImageCacheManager extends CacheManager { static const key = 'thumbnailImageCacheKey'; - static final ThumbnailImageCacheManager _instance = - ThumbnailImageCacheManager._(); + static final ThumbnailImageCacheManager _instance = ThumbnailImageCacheManager._(); factory ThumbnailImageCacheManager() { return _instance; } - ThumbnailImageCacheManager._() - : super( - Config( - key, - maxNrOfCacheObjects: 5000, - stalePeriod: const Duration(days: 30), - ), - ); + ThumbnailImageCacheManager._() : super(Config(key, maxNrOfCacheObjects: 5000, stalePeriod: const Duration(days: 30))); } diff --git a/mobile/lib/providers/image/immich_local_image_provider.dart b/mobile/lib/providers/image/immich_local_image_provider.dart index 4c77ee4b56..8c46c52906 100644 --- a/mobile/lib/providers/image/immich_local_image_provider.dart +++ b/mobile/lib/providers/image/immich_local_image_provider.dart @@ -18,11 +18,8 @@ class ImmichLocalImageProvider extends ImageProvider { final double height; final Logger log = Logger('ImmichLocalImageProvider'); - ImmichLocalImageProvider({ - required this.asset, - required this.width, - required this.height, - }) : assert(asset.local != null, 'Only usable when asset.local is set'); + ImmichLocalImageProvider({required this.asset, required this.width, required this.height}) + : assert(asset.local != null, 'Only usable when asset.local is set'); /// Converts an [ImageProvider]'s settings plus an [ImageConfiguration] to a key /// that describes the precise image to load. @@ -32,10 +29,7 @@ class ImmichLocalImageProvider extends ImageProvider { } @override - ImageStreamCompleter loadImage( - ImmichLocalImageProvider key, - ImageDecoderCallback decode, - ) { + ImageStreamCompleter loadImage(ImmichLocalImageProvider key, ImageDecoderCallback decode) { final chunkEvents = StreamController(); return MultiImageStreamCompleter( codec: _codec(key.asset, decode, chunkEvents), diff --git a/mobile/lib/providers/image/immich_local_thumbnail_provider.dart b/mobile/lib/providers/image/immich_local_thumbnail_provider.dart index edcf8a9458..5edb0fc79e 100644 --- a/mobile/lib/providers/image/immich_local_thumbnail_provider.dart +++ b/mobile/lib/providers/image/immich_local_thumbnail_provider.dart @@ -13,8 +13,7 @@ import 'package:logging/logging.dart'; /// The local image provider for an asset /// Only viable -class ImmichLocalThumbnailProvider - extends ImageProvider { +class ImmichLocalThumbnailProvider extends ImageProvider { final Asset asset; final int height; final int width; @@ -33,17 +32,12 @@ class ImmichLocalThumbnailProvider /// Converts an [ImageProvider]'s settings plus an [ImageConfiguration] to a key /// that describes the precise image to load. @override - Future obtainKey( - ImageConfiguration configuration, - ) { + Future obtainKey(ImageConfiguration configuration) { return SynchronousFuture(this); } @override - ImageStreamCompleter loadImage( - ImmichLocalThumbnailProvider key, - ImageDecoderCallback decode, - ) { + ImageStreamCompleter loadImage(ImmichLocalThumbnailProvider key, ImageDecoderCallback decode) { final cache = cacheManager ?? ThumbnailImageCacheManager(); return MultiImageStreamCompleter( codec: _codec(key.asset, cache, decode), @@ -55,18 +49,12 @@ class ImmichLocalThumbnailProvider } // Streams in each stage of the image as we ask for it - Stream _codec( - Asset assetData, - CacheManager cache, - ImageDecoderCallback decode, - ) async* { - final cacheKey = - '$userId${assetData.localId}${assetData.checksum}$width$height'; + Stream _codec(Asset assetData, CacheManager cache, ImageDecoderCallback decode) async* { + final cacheKey = '$userId${assetData.localId}${assetData.checksum}$width$height'; final fileFromCache = await cache.getFileFromCache(cacheKey); if (fileFromCache != null) { try { - final buffer = - await ui.ImmutableBuffer.fromFilePath(fileFromCache.file.path); + final buffer = await ui.ImmutableBuffer.fromFilePath(fileFromCache.file.path); final codec = await decode(buffer); yield codec; return; @@ -75,14 +63,9 @@ class ImmichLocalThumbnailProvider } } - final thumbnailBytes = await assetData.local?.thumbnailDataWithSize( - ThumbnailSize(width, height), - quality: 80, - ); + final thumbnailBytes = await assetData.local?.thumbnailDataWithSize(ThumbnailSize(width, height), quality: 80); if (thumbnailBytes == null) { - throw StateError( - "Loading thumb for local photo ${assetData.fileName} failed", - ); + throw StateError("Loading thumb for local photo ${assetData.fileName} failed"); } final buffer = await ui.ImmutableBuffer.fromUint8List(thumbnailBytes); diff --git a/mobile/lib/providers/image/immich_remote_image_provider.dart b/mobile/lib/providers/image/immich_remote_image_provider.dart index fe0811583c..16d5312e4c 100644 --- a/mobile/lib/providers/image/immich_remote_image_provider.dart +++ b/mobile/lib/providers/image/immich_remote_image_provider.dart @@ -15,33 +15,24 @@ import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/utils/image_url_builder.dart'; /// The remote image provider for full size remote images -class ImmichRemoteImageProvider - extends ImageProvider { +class ImmichRemoteImageProvider extends ImageProvider { /// The [Asset.remoteId] of the asset to fetch final String assetId; /// The image cache manager final CacheManager? cacheManager; - const ImmichRemoteImageProvider({ - required this.assetId, - this.cacheManager, - }); + const ImmichRemoteImageProvider({required this.assetId, this.cacheManager}); /// Converts an [ImageProvider]'s settings plus an [ImageConfiguration] to a key /// that describes the precise image to load. @override - Future obtainKey( - ImageConfiguration configuration, - ) { + Future obtainKey(ImageConfiguration configuration) { return SynchronousFuture(this); } @override - ImageStreamCompleter loadImage( - ImmichRemoteImageProvider key, - ImageDecoderCallback decode, - ) { + ImageStreamCompleter loadImage(ImmichRemoteImageProvider key, ImageDecoderCallback decode) { final cache = cacheManager ?? RemoteImageCacheManager(); final chunkEvents = StreamController(); return MultiImageStreamCompleter( @@ -52,10 +43,7 @@ class ImmichRemoteImageProvider } /// Whether to show the original file or load a compressed version - bool get _useOriginal => Store.get( - AppSettingsEnum.loadOriginal.storeKey, - AppSettingsEnum.loadOriginal.defaultValue, - ); + bool get _useOriginal => Store.get(AppSettingsEnum.loadOriginal.storeKey, AppSettingsEnum.loadOriginal.defaultValue); // Streams in each stage of the image as we ask for it Stream _codec( @@ -65,28 +53,15 @@ class ImmichRemoteImageProvider StreamController chunkEvents, ) async* { // Load the higher resolution version of the image - final url = getThumbnailUrlForRemoteId( - key.assetId, - type: api.AssetMediaSize.preview, - ); - final codec = await ImageLoader.loadImageFromCache( - url, - cache: cache, - decode: decode, - chunkEvents: chunkEvents, - ); + final url = getThumbnailUrlForRemoteId(key.assetId, type: api.AssetMediaSize.preview); + final codec = await ImageLoader.loadImageFromCache(url, cache: cache, decode: decode, chunkEvents: chunkEvents); yield codec; // Load the final remote image if (_useOriginal) { // Load the original image final url = getOriginalUrlForRemoteId(key.assetId); - final codec = await ImageLoader.loadImageFromCache( - url, - cache: cache, - decode: decode, - chunkEvents: chunkEvents, - ); + final codec = await ImageLoader.loadImageFromCache(url, cache: cache, decode: decode, chunkEvents: chunkEvents); yield codec; } await chunkEvents.close(); diff --git a/mobile/lib/providers/image/immich_remote_thumbnail_provider.dart b/mobile/lib/providers/image/immich_remote_thumbnail_provider.dart index 8533cb6f08..08ee4325e8 100644 --- a/mobile/lib/providers/image/immich_remote_thumbnail_provider.dart +++ b/mobile/lib/providers/image/immich_remote_thumbnail_provider.dart @@ -13,8 +13,7 @@ import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/utils/image_url_builder.dart'; /// The remote image provider -class ImmichRemoteThumbnailProvider - extends ImageProvider { +class ImmichRemoteThumbnailProvider extends ImageProvider { /// The [Asset.remoteId] of the asset to fetch final String assetId; @@ -24,51 +23,27 @@ class ImmichRemoteThumbnailProvider /// The image cache manager final CacheManager? cacheManager; - const ImmichRemoteThumbnailProvider({ - required this.assetId, - this.height, - this.width, - this.cacheManager, - }); + const ImmichRemoteThumbnailProvider({required this.assetId, this.height, this.width, this.cacheManager}); /// Converts an [ImageProvider]'s settings plus an [ImageConfiguration] to a key /// that describes the precise image to load. @override - Future obtainKey( - ImageConfiguration configuration, - ) { + Future obtainKey(ImageConfiguration configuration) { return SynchronousFuture(this); } @override - ImageStreamCompleter loadImage( - ImmichRemoteThumbnailProvider key, - ImageDecoderCallback decode, - ) { + ImageStreamCompleter loadImage(ImmichRemoteThumbnailProvider key, ImageDecoderCallback decode) { final cache = cacheManager ?? ThumbnailImageCacheManager(); - return MultiImageStreamCompleter( - codec: _codec(key, cache, decode), - scale: 1.0, - ); + return MultiImageStreamCompleter(codec: _codec(key, cache, decode), scale: 1.0); } // Streams in each stage of the image as we ask for it - Stream _codec( - ImmichRemoteThumbnailProvider key, - CacheManager cache, - ImageDecoderCallback decode, - ) async* { + Stream _codec(ImmichRemoteThumbnailProvider key, CacheManager cache, ImageDecoderCallback decode) async* { // Load a preview to the chunk events - final preview = getThumbnailUrlForRemoteId( - key.assetId, - type: api.AssetMediaSize.thumbnail, - ); + final preview = getThumbnailUrlForRemoteId(key.assetId, type: api.AssetMediaSize.thumbnail); - yield await ImageLoader.loadImageFromCache( - preview, - cache: cache, - decode: decode, - ); + yield await ImageLoader.loadImageFromCache(preview, cache: cache, decode: decode); } @override diff --git a/mobile/lib/providers/immich_logo_provider.g.dart b/mobile/lib/providers/immich_logo_provider.g.dart index 90b117d574..f1af433c1b 100644 --- a/mobile/lib/providers/immich_logo_provider.g.dart +++ b/mobile/lib/providers/immich_logo_provider.g.dart @@ -13,8 +13,9 @@ String _$immichLogoHash() => r'6de7fcca1ef9acef6ab7398eb0c664080747e0ea'; final immichLogoProvider = AutoDisposeFutureProvider.internal( immichLogo, name: r'immichLogoProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$immichLogoHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$immichLogoHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/mobile/lib/providers/infrastructure/action.provider.dart b/mobile/lib/providers/infrastructure/action.provider.dart index 456b072a39..21a22e7e5f 100644 --- a/mobile/lib/providers/infrastructure/action.provider.dart +++ b/mobile/lib/providers/infrastructure/action.provider.dart @@ -1,20 +1,21 @@ +import 'package:background_downloader/background_downloader.dart'; import 'package:flutter/material.dart'; import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/models/download/livephotos_medatada.model.dart'; import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/services/action.service.dart'; +import 'package:immich_mobile/services/download.service.dart'; import 'package:immich_mobile/services/timeline.service.dart'; +import 'package:immich_mobile/services/upload.service.dart'; import 'package:logging/logging.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; final actionProvider = NotifierProvider( ActionNotifier.new, - dependencies: [ - multiSelectProvider, - timelineServiceProvider, - ], + dependencies: [multiSelectProvider, timelineServiceProvider], ); class ActionResult { @@ -25,37 +26,68 @@ class ActionResult { const ActionResult({required this.count, required this.success, this.error}); @override - String toString() => - 'ActionResult(count: $count, success: $success, error: $error)'; + String toString() => 'ActionResult(count: $count, success: $success, error: $error)'; } class ActionNotifier extends Notifier { final Logger _logger = Logger('ActionNotifier'); late ActionService _service; + late UploadService _uploadService; + late DownloadService _downloadService; ActionNotifier() : super(); @override void build() { + _uploadService = ref.watch(uploadServiceProvider); _service = ref.watch(actionServiceProvider); + _downloadService = ref.watch(downloadServiceProvider); + _downloadService.onImageDownloadStatus = _downloadImageCallback; + _downloadService.onVideoDownloadStatus = _downloadVideoCallback; + _downloadService.onLivePhotoDownloadStatus = _downloadLivePhotoCallback; + } + + void _downloadImageCallback(TaskStatusUpdate update) { + if (update.status == TaskStatus.complete) { + _downloadService.saveImageWithPath(update.task); + } + } + + void _downloadVideoCallback(TaskStatusUpdate update) { + if (update.status == TaskStatus.complete) { + _downloadService.saveVideo(update.task); + } + } + + void _downloadLivePhotoCallback(TaskStatusUpdate update) async { + if (update.status == TaskStatus.complete) { + final livePhotosId = LivePhotosMetadata.fromJson(update.task.metaData).id; + _downloadService.saveLivePhotos(update.task, livePhotosId); + } } List _getRemoteIdsForSource(ActionSource source) { - return _getIdsForSource(source) - .toIds() - .toList(growable: false); + return _getAssets(source).whereType().toIds().toList(growable: false); } List _getLocalIdsForSource(ActionSource source) { - return _getIdsForSource(source).toIds().toList(growable: false); + final Set assets = _getAssets(source); + final List localIds = []; + + for (final asset in assets) { + if (asset is LocalAsset) { + localIds.add(asset.id); + } else if (asset is RemoteAsset && asset.localId != null) { + localIds.add(asset.localId!); + } + } + + return localIds; } List _getOwnedRemoteIdsForSource(ActionSource source) { final ownerId = ref.read(currentUserProvider)?.id; - return _getIdsForSource(source) - .ownedAssets(ownerId) - .toIds() - .toList(growable: false); + return _getAssets(source).whereType().ownedAssets(ownerId).toIds().toList(growable: false); } List _getOwnedRemoteAssetsForSource(ActionSource source) { @@ -66,37 +98,31 @@ class ActionNotifier extends Notifier { Iterable _getIdsForSource(ActionSource source) { final Set assets = _getAssets(source); return switch (T) { - const (RemoteAsset) => assets.whereType(), - const (LocalAsset) => assets.whereType(), - _ => const [], - } as Iterable; + const (RemoteAsset) => assets.whereType(), + const (LocalAsset) => assets.whereType(), + _ => const [], + } + as Iterable; } Set _getAssets(ActionSource source) { return switch (source) { ActionSource.timeline => ref.read(multiSelectProvider).selectedAssets, ActionSource.viewer => switch (ref.read(currentAssetNotifier)) { - BaseAsset asset => {asset}, - null => const {}, - }, + BaseAsset asset => {asset}, + null => const {}, + }, }; } - Future shareLink( - ActionSource source, - BuildContext context, - ) async { + Future shareLink(ActionSource source, BuildContext context) async { final ids = _getRemoteIdsForSource(source); try { await _service.shareLink(ids, context); return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to create shared link for assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -107,11 +133,7 @@ class ActionNotifier extends Notifier { return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to favorite assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -122,11 +144,7 @@ class ActionNotifier extends Notifier { return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to unfavorite assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -137,11 +155,7 @@ class ActionNotifier extends Notifier { return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to archive assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -152,26 +166,19 @@ class ActionNotifier extends Notifier { return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to unarchive assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } Future moveToLockFolder(ActionSource source) async { final ids = _getOwnedRemoteIdsForSource(source); + final localIds = _getLocalIdsForSource(source); try { - await _service.moveToLockFolder(ids); + await _service.moveToLockFolder(ids, localIds); return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to move assets to lock folder', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -182,63 +189,69 @@ class ActionNotifier extends Notifier { return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to remove assets from lock folder', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } Future trash(ActionSource source) async { final ids = _getOwnedRemoteIdsForSource(source); + try { await _service.trash(ids); return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to trash assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } - Future delete(ActionSource source) async { + Future restoreTrash(ActionSource source) async { final ids = _getOwnedRemoteIdsForSource(source); try { - await _service.delete(ids); + await _service.restoreTrash(ids); + return ActionResult(count: ids.length, success: true); + } catch (error, stack) { + _logger.severe('Failed to restore trash assets', error, stack); + return ActionResult(count: ids.length, success: false, error: error.toString()); + } + } + + Future trashRemoteAndDeleteLocal(ActionSource source) async { + final ids = _getOwnedRemoteIdsForSource(source); + final localIds = _getLocalIdsForSource(source); + try { + await _service.trashRemoteAndDeleteLocal(ids, localIds); return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to delete assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); + } + } + + Future deleteRemoteAndLocal(ActionSource source) async { + final ids = _getOwnedRemoteIdsForSource(source); + final localIds = _getLocalIdsForSource(source); + try { + await _service.deleteRemoteAndLocal(ids, localIds); + return ActionResult(count: ids.length, success: true); + } catch (error, stack) { + _logger.severe('Failed to delete assets', error, stack); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } Future deleteLocal(ActionSource source) async { final ids = _getLocalIdsForSource(source); try { - await _service.deleteLocal(ids); - return ActionResult(count: ids.length, success: true); + final deletedCount = await _service.deleteLocal(ids); + return ActionResult(count: deletedCount, success: true); } catch (error, stack) { _logger.severe('Failed to delete assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } - Future editLocation( - ActionSource source, - BuildContext context, - ) async { + Future editLocation(ActionSource source, BuildContext context) async { final ids = _getOwnedRemoteIdsForSource(source); try { final isEdited = await _service.editLocation(ids, context); @@ -249,29 +262,49 @@ class ActionNotifier extends Notifier { return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to edit location for assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } - Future removeFromAlbum( - ActionSource source, - String albumId, - ) async { + Future editDateTime(ActionSource source, BuildContext context) async { + final ids = _getOwnedRemoteIdsForSource(source); + try { + final isEdited = await _service.editDateTime(ids, context); + if (!isEdited) { + return null; + } + + return ActionResult(count: ids.length, success: true); + } catch (error, stack) { + _logger.severe('Failed to edit date and time for assets', error, stack); + return ActionResult(count: ids.length, success: false, error: error.toString()); + } + } + + Future removeFromAlbum(ActionSource source, String albumId) async { final ids = _getRemoteIdsForSource(source); try { final removedCount = await _service.removeFromAlbum(ids, albumId); return ActionResult(count: removedCount, success: true); } catch (error, stack) { _logger.severe('Failed to remove assets from album', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); + } + } + + Future updateDescription(ActionSource source, String description) async { + final ids = _getRemoteIdsForSource(source); + if (ids.length != 1) { + _logger.warning('updateDescription called with multiple assets, expected single asset'); + return ActionResult(count: ids.length, success: false, error: 'Expected single asset for description update'); + } + + try { + final isUpdated = await _service.updateDescription(ids.first, description); + return ActionResult(count: 1, success: isUpdated); + } catch (error, stack) { + _logger.severe('Failed to update description for asset', error, stack); + return ActionResult(count: 1, success: false, error: error.toString()); } } @@ -282,11 +315,7 @@ class ActionNotifier extends Notifier { return ActionResult(count: ids.length, success: true); } catch (error, stack) { _logger.severe('Failed to stack assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); } } @@ -297,10 +326,7 @@ class ActionNotifier extends Notifier { return ActionResult(count: assets.length, success: true); } catch (error, stack) { _logger.severe('Failed to unstack assets', error, stack); - return ActionResult( - count: assets.length, - success: false, - ); + return ActionResult(count: assets.length, success: false); } } @@ -312,11 +338,31 @@ class ActionNotifier extends Notifier { return ActionResult(count: count, success: true); } catch (error, stack) { _logger.severe('Failed to share assets', error, stack); - return ActionResult( - count: ids.length, - success: false, - error: error.toString(), - ); + return ActionResult(count: ids.length, success: false, error: error.toString()); + } + } + + Future downloadAll(ActionSource source) async { + final assets = _getAssets(source).whereType().toList(growable: false); + + try { + final didEnqueue = await _service.downloadAll(assets); + final enqueueCount = didEnqueue.where((e) => e).length; + return ActionResult(count: enqueueCount, success: true); + } catch (error, stack) { + _logger.severe('Failed to download assets', error, stack); + return ActionResult(count: assets.length, success: false, error: error.toString()); + } + } + + Future upload(ActionSource source) async { + final assets = _getAssets(source).whereType().toList(); + try { + await _uploadService.manualBackup(assets); + return ActionResult(count: assets.length, success: true); + } catch (error, stack) { + _logger.severe('Failed manually upload assets', error, stack); + return ActionResult(count: assets.length, success: false, error: error.toString()); } } } @@ -329,7 +375,3 @@ extension on Iterable { return whereType().where((a) => a.ownerId == ownerId); } } - -extension on Iterable { - Iterable toIds() => map((e) => e.id); -} diff --git a/mobile/lib/providers/infrastructure/album.provider.dart b/mobile/lib/providers/infrastructure/album.provider.dart index 4ec3453d16..da0f9bc9ce 100644 --- a/mobile/lib/providers/infrastructure/album.provider.dart +++ b/mobile/lib/providers/infrastructure/album.provider.dart @@ -22,8 +22,7 @@ final localAlbumProvider = FutureProvider>( ); final localAlbumThumbnailProvider = FutureProvider.family( - (ref, albumId) => - LocalAlbumService(ref.watch(localAlbumRepository)).getThumbnail(albumId), + (ref, albumId) => LocalAlbumService(ref.watch(localAlbumRepository)).getThumbnail(albumId), ); final remoteAlbumRepository = Provider( @@ -31,15 +30,11 @@ final remoteAlbumRepository = Provider( ); final remoteAlbumServiceProvider = Provider( - (ref) => RemoteAlbumService( - ref.watch(remoteAlbumRepository), - ref.watch(driftAlbumApiRepositoryProvider), - ), + (ref) => RemoteAlbumService(ref.watch(remoteAlbumRepository), ref.watch(driftAlbumApiRepositoryProvider)), dependencies: [remoteAlbumRepository], ); -final remoteAlbumProvider = - NotifierProvider( +final remoteAlbumProvider = NotifierProvider( RemoteAlbumNotifier.new, dependencies: [remoteAlbumServiceProvider], ); diff --git a/mobile/lib/providers/infrastructure/asset_viewer/current_asset.provider.dart b/mobile/lib/providers/infrastructure/asset_viewer/current_asset.provider.dart index 996d5d816f..1956170c1e 100644 --- a/mobile/lib/providers/infrastructure/asset_viewer/current_asset.provider.dart +++ b/mobile/lib/providers/infrastructure/asset_viewer/current_asset.provider.dart @@ -4,10 +4,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; -final currentAssetNotifier = - AutoDisposeNotifierProvider( - CurrentAssetNotifier.new, -); +final currentAssetNotifier = AutoDisposeNotifierProvider(CurrentAssetNotifier.new); class CurrentAssetNotifier extends AutoDisposeNotifier { KeepAliveLink? _keepAliveLink; @@ -20,10 +17,7 @@ class CurrentAssetNotifier extends AutoDisposeNotifier { _keepAliveLink?.close(); _assetSubscription?.cancel(); state = asset; - _assetSubscription = ref - .watch(assetServiceProvider) - .watchAsset(asset) - .listen((updatedAsset) { + _assetSubscription = ref.watch(assetServiceProvider).watchAsset(asset).listen((updatedAsset) { if (updatedAsset != null) { state = updatedAsset; } @@ -37,12 +31,10 @@ class CurrentAssetNotifier extends AutoDisposeNotifier { } } -final currentAssetExifProvider = FutureProvider.autoDispose( - (ref) { - final currentAsset = ref.watch(currentAssetNotifier); - if (currentAsset == null) { - return null; - } - return ref.watch(assetServiceProvider).getExif(currentAsset); - }, -); +final currentAssetExifProvider = FutureProvider.autoDispose((ref) { + final currentAsset = ref.watch(currentAssetNotifier); + if (currentAsset == null) { + return null; + } + return ref.watch(assetServiceProvider).getExif(currentAsset); +}); diff --git a/mobile/lib/providers/infrastructure/current_album.provider.dart b/mobile/lib/providers/infrastructure/current_album.provider.dart index ece188ee15..5d6e0414b0 100644 --- a/mobile/lib/providers/infrastructure/current_album.provider.dart +++ b/mobile/lib/providers/infrastructure/current_album.provider.dart @@ -4,8 +4,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/album/album.model.dart'; import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; -final currentRemoteAlbumProvider = - AutoDisposeNotifierProvider( +final currentRemoteAlbumProvider = AutoDisposeNotifierProvider( CurrentAlbumNotifier.new, ); @@ -21,10 +20,7 @@ class CurrentAlbumNotifier extends AutoDisposeNotifier { _assetSubscription?.cancel(); state = album; - _assetSubscription = ref - .watch(remoteAlbumServiceProvider) - .watchAlbum(album.id) - .listen((updatedAlbum) { + _assetSubscription = ref.watch(remoteAlbumServiceProvider).watchAlbum(album.id).listen((updatedAlbum) { if (updatedAlbum != null) { state = updatedAlbum; } diff --git a/mobile/lib/providers/infrastructure/db.provider.g.dart b/mobile/lib/providers/infrastructure/db.provider.g.dart index 33b330192f..46abfb66a9 100644 --- a/mobile/lib/providers/infrastructure/db.provider.g.dart +++ b/mobile/lib/providers/infrastructure/db.provider.g.dart @@ -13,8 +13,9 @@ String _$isarHash() => r'69d3a06aa7e69a4381478e03f7956eb07d7f7feb'; final isarProvider = Provider.internal( isar, name: r'isarProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$isarHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$isarHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/mobile/lib/providers/infrastructure/exif.provider.dart b/mobile/lib/providers/infrastructure/exif.provider.dart index 59ad632927..c126f6cac0 100644 --- a/mobile/lib/providers/infrastructure/exif.provider.dart +++ b/mobile/lib/providers/infrastructure/exif.provider.dart @@ -6,5 +6,4 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'exif.provider.g.dart'; @Riverpod(keepAlive: true) -IsarExifRepository exifRepository(Ref ref) => - IsarExifRepository(ref.watch(isarProvider)); +IsarExifRepository exifRepository(Ref ref) => IsarExifRepository(ref.watch(isarProvider)); diff --git a/mobile/lib/providers/infrastructure/memory.provider.dart b/mobile/lib/providers/infrastructure/memory.provider.dart index 0e58943f55..e5809a12b4 100644 --- a/mobile/lib/providers/infrastructure/memory.provider.dart +++ b/mobile/lib/providers/infrastructure/memory.provider.dart @@ -14,8 +14,7 @@ final driftMemoryServiceProvider = Provider( (ref) => DriftMemoryService(ref.watch(driftMemoryRepositoryProvider)), ); -final driftMemoryFutureProvider = - FutureProvider.autoDispose>((ref) async { +final driftMemoryFutureProvider = FutureProvider.autoDispose>((ref) async { final user = ref.watch(currentUserProvider); if (user == null) { return []; diff --git a/mobile/lib/providers/infrastructure/partner.provider.dart b/mobile/lib/providers/infrastructure/partner.provider.dart new file mode 100644 index 0000000000..f4ba4cc73a --- /dev/null +++ b/mobile/lib/providers/infrastructure/partner.provider.dart @@ -0,0 +1,87 @@ +import 'package:immich_mobile/domain/models/user.model.dart'; +import 'package:immich_mobile/domain/services/partner.service.dart'; +import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; +import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; + +class PartnerNotifier extends Notifier> { + late DriftPartnerService _driftPartnerService; + + @override + List build() { + _driftPartnerService = ref.read(driftPartnerServiceProvider); + return []; + } + + Future _loadPartners() async { + final currentUser = ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + + state = await _driftPartnerService.getSharedWith(currentUser.id); + } + + Future> getPartners(String userId) async { + final partners = await _driftPartnerService.getSharedWith(userId); + state = partners; + return partners; + } + + Future toggleShowInTimeline(String partnerId, String userId) async { + await _driftPartnerService.toggleShowInTimeline(partnerId, userId); + await _loadPartners(); + } + + Future addPartner(PartnerUserDto partner) async { + final currentUser = ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + + await _driftPartnerService.addPartner(partner.id, currentUser.id); + await _loadPartners(); + ref.invalidate(driftAvailablePartnerProvider); + ref.invalidate(driftSharedByPartnerProvider); + } + + Future removePartner(PartnerUserDto partner) async { + final currentUser = ref.read(currentUserProvider); + if (currentUser == null) { + return; + } + + await _driftPartnerService.removePartner(partner.id, currentUser.id); + await _loadPartners(); + ref.invalidate(driftAvailablePartnerProvider); + ref.invalidate(driftSharedByPartnerProvider); + } +} + +final driftAvailablePartnerProvider = FutureProvider.autoDispose>((ref) { + final currentUser = ref.watch(currentUserProvider); + if (currentUser == null) { + return []; + } + + return ref.watch(driftPartnerServiceProvider).getAvailablePartners(currentUser.id); +}); + +final driftSharedByPartnerProvider = FutureProvider.autoDispose>((ref) { + final currentUser = ref.watch(currentUserProvider); + if (currentUser == null) { + return []; + } + + return ref.watch(driftPartnerServiceProvider).getSharedBy(currentUser.id); +}); + +final driftSharedWithPartnerProvider = FutureProvider.autoDispose>((ref) { + final currentUser = ref.watch(currentUserProvider); + if (currentUser == null) { + return []; + } + + return ref.watch(driftPartnerServiceProvider).getSharedWith(currentUser.id); +}); diff --git a/mobile/lib/providers/infrastructure/people.provider.dart b/mobile/lib/providers/infrastructure/people.provider.dart new file mode 100644 index 0000000000..94a1b2447f --- /dev/null +++ b/mobile/lib/providers/infrastructure/people.provider.dart @@ -0,0 +1,24 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; +import 'package:immich_mobile/domain/services/people.service.dart'; +import 'package:immich_mobile/infrastructure/repositories/people.repository.dart'; +import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; +import 'package:immich_mobile/repositories/person_api.repository.dart'; + +final driftPeopleRepositoryProvider = Provider( + (ref) => DriftPeopleRepository(ref.watch(driftProvider)), +); + +final driftPeopleServiceProvider = Provider( + (ref) => DriftPeopleService(ref.watch(driftPeopleRepositoryProvider), ref.watch(personApiRepositoryProvider)), +); + +final driftPeopleAssetProvider = FutureProvider.family, String>((ref, assetId) async { + final service = ref.watch(driftPeopleServiceProvider); + return service.getAssetPeople(assetId); +}); + +final driftGetAllPeopleProvider = FutureProvider>((ref) async { + final service = ref.watch(driftPeopleServiceProvider); + return service.getAllPeople(); +}); diff --git a/mobile/lib/providers/infrastructure/remote_album.provider.dart b/mobile/lib/providers/infrastructure/remote_album.provider.dart index 14badd58ed..ca7735808c 100644 --- a/mobile/lib/providers/infrastructure/remote_album.provider.dart +++ b/mobile/lib/providers/infrastructure/remote_album.provider.dart @@ -6,6 +6,7 @@ import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/domain/services/remote_album.service.dart'; import 'package:immich_mobile/models/albums/album_search.model.dart'; import 'package:immich_mobile/utils/remote_album.utils.dart'; +import 'package:logging/logging.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'album.provider.dart'; @@ -13,112 +14,65 @@ import 'album.provider.dart'; class RemoteAlbumState { final List albums; final List filteredAlbums; - final bool isLoading; - final String? error; - const RemoteAlbumState({ - required this.albums, - List? filteredAlbums, - this.isLoading = false, - this.error, - }) : filteredAlbums = filteredAlbums ?? albums; + const RemoteAlbumState({required this.albums, List? filteredAlbums}) + : filteredAlbums = filteredAlbums ?? albums; - RemoteAlbumState copyWith({ - List? albums, - List? filteredAlbums, - bool? isLoading, - String? error, - }) { - return RemoteAlbumState( - albums: albums ?? this.albums, - filteredAlbums: filteredAlbums ?? this.filteredAlbums, - isLoading: isLoading ?? this.isLoading, - error: error ?? this.error, - ); + RemoteAlbumState copyWith({List? albums, List? filteredAlbums}) { + return RemoteAlbumState(albums: albums ?? this.albums, filteredAlbums: filteredAlbums ?? this.filteredAlbums); } @override - String toString() => - 'RemoteAlbumState(albums: ${albums.length}, filteredAlbums: ${filteredAlbums.length}, isLoading: $isLoading, error: $error)'; + String toString() => 'RemoteAlbumState(albums: ${albums.length}, filteredAlbums: ${filteredAlbums.length})'; @override bool operator ==(covariant RemoteAlbumState other) { if (identical(this, other)) return true; final listEquals = const DeepCollectionEquality().equals; - return listEquals(other.albums, albums) && - listEquals(other.filteredAlbums, filteredAlbums) && - other.isLoading == isLoading && - other.error == error; + return listEquals(other.albums, albums) && listEquals(other.filteredAlbums, filteredAlbums); } @override - int get hashCode => - albums.hashCode ^ - filteredAlbums.hashCode ^ - isLoading.hashCode ^ - error.hashCode; + int get hashCode => albums.hashCode ^ filteredAlbums.hashCode; } class RemoteAlbumNotifier extends Notifier { late RemoteAlbumService _remoteAlbumService; - + final _logger = Logger('RemoteAlbumNotifier'); @override RemoteAlbumState build() { _remoteAlbumService = ref.read(remoteAlbumServiceProvider); return const RemoteAlbumState(albums: [], filteredAlbums: []); } - Future> getAll() async { - state = state.copyWith(isLoading: true, error: null); - + Future> _getAll() async { try { final albums = await _remoteAlbumService.getAll(); - state = state.copyWith( - albums: albums, - filteredAlbums: albums, - isLoading: false, - ); + state = state.copyWith(albums: albums, filteredAlbums: albums); return albums; - } catch (e) { - state = state.copyWith(isLoading: false, error: e.toString()); + } catch (error, stack) { + _logger.severe('Failed to fetch albums', error, stack); rethrow; } } Future refresh() async { - await getAll(); + await _getAll(); } - void searchAlbums( - String query, - String? userId, [ - QuickFilterMode filterMode = QuickFilterMode.all, - ]) { - final filtered = _remoteAlbumService.searchAlbums( - state.albums, - query, - userId, - filterMode, - ); + void searchAlbums(String query, String? userId, [QuickFilterMode filterMode = QuickFilterMode.all]) { + final filtered = _remoteAlbumService.searchAlbums(state.albums, query, userId, filterMode); - state = state.copyWith( - filteredAlbums: filtered, - ); + state = state.copyWith(filteredAlbums: filtered); } void clearSearch() { - state = state.copyWith( - filteredAlbums: state.albums, - ); + state = state.copyWith(filteredAlbums: state.albums); } - void sortFilteredAlbums( - RemoteAlbumSortMode sortMode, { - bool isReverse = false, - }) { - final sortedAlbums = _remoteAlbumService - .sortAlbums(state.filteredAlbums, sortMode, isReverse: isReverse); + void sortFilteredAlbums(RemoteAlbumSortMode sortMode, {bool isReverse = false}) { + final sortedAlbums = _remoteAlbumService.sortAlbums(state.filteredAlbums, sortMode, isReverse: isReverse); state = state.copyWith(filteredAlbums: sortedAlbums); } @@ -127,24 +81,14 @@ class RemoteAlbumNotifier extends Notifier { String? description, List assetIds = const [], }) async { - state = state.copyWith(isLoading: true, error: null); - try { - final album = await _remoteAlbumService.createAlbum( - title: title, - description: description, - assetIds: assetIds, - ); + final album = await _remoteAlbumService.createAlbum(title: title, description: description, assetIds: assetIds); - state = state.copyWith( - albums: [...state.albums, album], - filteredAlbums: [...state.filteredAlbums, album], - ); + state = state.copyWith(albums: [...state.albums, album], filteredAlbums: [...state.filteredAlbums, album]); - state = state.copyWith(isLoading: false); return album; - } catch (e) { - state = state.copyWith(isLoading: false, error: e.toString()); + } catch (error, stack) { + _logger.severe('Failed to create album', error, stack); rethrow; } } @@ -157,8 +101,6 @@ class RemoteAlbumNotifier extends Notifier { bool? isActivityEnabled, AlbumAssetOrder? order, }) async { - state = state.copyWith(isLoading: true, error: null); - try { final updatedAlbum = await _remoteAlbumService.updateAlbum( albumId, @@ -177,26 +119,19 @@ class RemoteAlbumNotifier extends Notifier { return album.id == albumId ? updatedAlbum : album; }).toList(); - state = state.copyWith( - albums: updatedAlbums, - filteredAlbums: updatedFilteredAlbums, - isLoading: false, - ); + state = state.copyWith(albums: updatedAlbums, filteredAlbums: updatedFilteredAlbums); return updatedAlbum; - } catch (e) { - state = state.copyWith(isLoading: false, error: e.toString()); + } catch (error, stack) { + _logger.severe('Failed to update album', error, stack); rethrow; } } Future toggleAlbumOrder(String albumId) async { - final currentAlbum = - state.albums.firstWhere((album) => album.id == albumId); + final currentAlbum = state.albums.firstWhere((album) => album.id == albumId); - final newOrder = currentAlbum.order == AlbumAssetOrder.asc - ? AlbumAssetOrder.desc - : AlbumAssetOrder.asc; + final newOrder = currentAlbum.order == AlbumAssetOrder.asc ? AlbumAssetOrder.desc : AlbumAssetOrder.asc; return updateAlbum(albumId, order: newOrder); } @@ -204,15 +139,10 @@ class RemoteAlbumNotifier extends Notifier { Future deleteAlbum(String albumId) async { await _remoteAlbumService.deleteAlbum(albumId); - final updatedAlbums = - state.albums.where((album) => album.id != albumId).toList(); - final updatedFilteredAlbums = - state.filteredAlbums.where((album) => album.id != albumId).toList(); + final updatedAlbums = state.albums.where((album) => album.id != albumId).toList(); + final updatedFilteredAlbums = state.filteredAlbums.where((album) => album.id != albumId).toList(); - state = state.copyWith( - albums: updatedAlbums, - filteredAlbums: updatedFilteredAlbums, - ); + state = state.copyWith(albums: updatedAlbums, filteredAlbums: updatedFilteredAlbums); } Future> getAssets(String albumId) { @@ -220,34 +150,22 @@ class RemoteAlbumNotifier extends Notifier { } Future addAssets(String albumId, List assetIds) { - return _remoteAlbumService.addAssets( - albumId: albumId, - assetIds: assetIds, - ); + return _remoteAlbumService.addAssets(albumId: albumId, assetIds: assetIds); } Future addUsers(String albumId, List userIds) { - return _remoteAlbumService.addUsers( - albumId: albumId, - userIds: userIds, - ); + return _remoteAlbumService.addUsers(albumId: albumId, userIds: userIds); } } -final remoteAlbumDateRangeProvider = - FutureProvider.family<(DateTime, DateTime), String>( - (ref, albumId) async { - final service = ref.watch(remoteAlbumServiceProvider); - return service.getDateRange(albumId); - }, -); +final remoteAlbumDateRangeProvider = FutureProvider.family<(DateTime, DateTime), String>((ref, albumId) async { + final service = ref.watch(remoteAlbumServiceProvider); + return service.getDateRange(albumId); +}); -final remoteAlbumSharedUsersProvider = - FutureProvider.autoDispose.family, String>( - (ref, albumId) async { - final link = ref.keepAlive(); - ref.onDispose(() => link.close()); - final service = ref.watch(remoteAlbumServiceProvider); - return service.getSharedUsers(albumId); - }, -); +final remoteAlbumSharedUsersProvider = FutureProvider.autoDispose.family, String>((ref, albumId) async { + final link = ref.keepAlive(); + ref.onDispose(() => link.close()); + final service = ref.watch(remoteAlbumServiceProvider); + return service.getSharedUsers(albumId); +}); diff --git a/mobile/lib/providers/infrastructure/search.provider.dart b/mobile/lib/providers/infrastructure/search.provider.dart index cdcd3ee43b..7d992f9d5f 100644 --- a/mobile/lib/providers/infrastructure/search.provider.dart +++ b/mobile/lib/providers/infrastructure/search.provider.dart @@ -3,10 +3,6 @@ import 'package:immich_mobile/domain/services/search.service.dart'; import 'package:immich_mobile/infrastructure/repositories/search_api.repository.dart'; import 'package:immich_mobile/providers/api.provider.dart'; -final searchApiRepositoryProvider = Provider( - (ref) => SearchApiRepository(ref.watch(apiServiceProvider).searchApi), -); +final searchApiRepositoryProvider = Provider((ref) => SearchApiRepository(ref.watch(apiServiceProvider).searchApi)); -final searchServiceProvider = Provider( - (ref) => SearchService(ref.watch(searchApiRepositoryProvider)), -); +final searchServiceProvider = Provider((ref) => SearchService(ref.watch(searchApiRepositoryProvider))); diff --git a/mobile/lib/providers/infrastructure/setting.provider.dart b/mobile/lib/providers/infrastructure/setting.provider.dart index ad0af8282e..7d8be72cd0 100644 --- a/mobile/lib/providers/infrastructure/setting.provider.dart +++ b/mobile/lib/providers/infrastructure/setting.provider.dart @@ -5,8 +5,7 @@ import 'package:immich_mobile/providers/infrastructure/store.provider.dart'; class SettingsNotifier extends Notifier { @override - SettingsService build() => - SettingsService(storeService: ref.read(storeServiceProvider)); + SettingsService build() => SettingsService(storeService: ref.read(storeServiceProvider)); T get(Setting setting) => state.get(setting); @@ -18,5 +17,4 @@ class SettingsNotifier extends Notifier { Stream watch(Setting setting) => state.watch(setting); } -final settingsProvider = - NotifierProvider(SettingsNotifier.new); +final settingsProvider = NotifierProvider(SettingsNotifier.new); diff --git a/mobile/lib/providers/stack.provider.dart b/mobile/lib/providers/infrastructure/stack.provider.dart similarity index 63% rename from mobile/lib/providers/stack.provider.dart rename to mobile/lib/providers/infrastructure/stack.provider.dart index 71abd1e87a..0528fd0c91 100644 --- a/mobile/lib/providers/stack.provider.dart +++ b/mobile/lib/providers/infrastructure/stack.provider.dart @@ -2,6 +2,4 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/infrastructure/repositories/stack.repository.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; -final driftStackProvider = Provider( - (ref) => DriftStackRepository(ref.watch(driftProvider)), -); +final driftStackProvider = Provider((ref) => DriftStackRepository(ref.watch(driftProvider))); diff --git a/mobile/lib/providers/infrastructure/storage.provider.dart b/mobile/lib/providers/infrastructure/storage.provider.dart index 5bbbe51497..ccca964027 100644 --- a/mobile/lib/providers/infrastructure/storage.provider.dart +++ b/mobile/lib/providers/infrastructure/storage.provider.dart @@ -1,6 +1,4 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart'; -final storageRepositoryProvider = Provider( - (ref) => const StorageRepository(), -); +final storageRepositoryProvider = Provider((ref) => const StorageRepository()); diff --git a/mobile/lib/providers/infrastructure/store.provider.dart b/mobile/lib/providers/infrastructure/store.provider.dart index 6ae7ff987b..0bf42f3e8b 100644 --- a/mobile/lib/providers/infrastructure/store.provider.dart +++ b/mobile/lib/providers/infrastructure/store.provider.dart @@ -7,8 +7,7 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'store.provider.g.dart'; @Riverpod(keepAlive: true) -IsarStoreRepository storeRepository(Ref ref) => - IsarStoreRepository(ref.watch(isarProvider)); +IsarStoreRepository storeRepository(Ref ref) => IsarStoreRepository(ref.watch(isarProvider)); @Riverpod(keepAlive: true) StoreService storeService(Ref _) => StoreService.I; diff --git a/mobile/lib/providers/infrastructure/store.provider.g.dart b/mobile/lib/providers/infrastructure/store.provider.g.dart index 22b783013a..98c978cb60 100644 --- a/mobile/lib/providers/infrastructure/store.provider.g.dart +++ b/mobile/lib/providers/infrastructure/store.provider.g.dart @@ -30,8 +30,9 @@ String _$storeServiceHash() => r'250e10497c42df360e9e1f9a618d0b19c1b5b0a0'; final storeServiceProvider = Provider.internal( storeService, name: r'storeServiceProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$storeServiceHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$storeServiceHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/mobile/lib/providers/infrastructure/sync.provider.dart b/mobile/lib/providers/infrastructure/sync.provider.dart index 2406c37fa8..ddc6eed441 100644 --- a/mobile/lib/providers/infrastructure/sync.provider.dart +++ b/mobile/lib/providers/infrastructure/sync.provider.dart @@ -20,13 +20,9 @@ final syncStreamServiceProvider = Provider( ), ); -final syncApiRepositoryProvider = Provider( - (ref) => SyncApiRepository(ref.watch(apiServiceProvider)), -); +final syncApiRepositoryProvider = Provider((ref) => SyncApiRepository(ref.watch(apiServiceProvider))); -final syncStreamRepositoryProvider = Provider( - (ref) => SyncStreamRepository(ref.watch(driftProvider)), -); +final syncStreamRepositoryProvider = Provider((ref) => SyncStreamRepository(ref.watch(driftProvider))); final localSyncServiceProvider = Provider( (ref) => LocalSyncService( diff --git a/mobile/lib/providers/infrastructure/timeline.provider.dart b/mobile/lib/providers/infrastructure/timeline.provider.dart index bbc04349de..06ec0242b2 100644 --- a/mobile/lib/providers/infrastructure/timeline.provider.dart +++ b/mobile/lib/providers/infrastructure/timeline.provider.dart @@ -11,15 +11,13 @@ final timelineRepositoryProvider = Provider( ); final timelineArgsProvider = Provider.autoDispose( - (ref) => - throw UnimplementedError('Will be overridden through a ProviderScope.'), + (ref) => throw UnimplementedError('Will be overridden through a ProviderScope.'), ); final timelineServiceProvider = Provider( (ref) { final timelineUsers = ref.watch(timelineUsersProvider).valueOrNull ?? []; - final timelineService = - ref.watch(timelineFactoryProvider).main(timelineUsers); + final timelineService = ref.watch(timelineFactoryProvider).main(timelineUsers); ref.onDispose(timelineService.dispose); return timelineService; }, @@ -35,15 +33,11 @@ final timelineFactoryProvider = Provider( ), ); -final timelineUsersProvider = StreamProvider>( - (ref) { - final currentUserId = ref.watch(currentUserProvider.select((u) => u?.id)); - if (currentUserId == null) { - return Stream.value([]); - } +final timelineUsersProvider = StreamProvider>((ref) { + final currentUserId = ref.watch(currentUserProvider.select((u) => u?.id)); + if (currentUserId == null) { + return Stream.value([]); + } - return ref - .watch(timelineRepositoryProvider) - .watchTimelineUserIds(currentUserId); - }, -); + return ref.watch(timelineRepositoryProvider).watchTimelineUserIds(currentUserId); +}); diff --git a/mobile/lib/providers/infrastructure/user.provider.dart b/mobile/lib/providers/infrastructure/user.provider.dart index ca65f8be14..922b9866bb 100644 --- a/mobile/lib/providers/infrastructure/user.provider.dart +++ b/mobile/lib/providers/infrastructure/user.provider.dart @@ -1,25 +1,39 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/user.model.dart'; +import 'package:immich_mobile/domain/services/partner.service.dart'; import 'package:immich_mobile/domain/services/user.service.dart'; +import 'package:immich_mobile/infrastructure/repositories/partner.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/user.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/user_api.repository.dart'; import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/partner.provider.dart'; import 'package:immich_mobile/providers/infrastructure/store.provider.dart'; +import 'package:immich_mobile/repositories/partner_api.repository.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'user.provider.g.dart'; @Riverpod(keepAlive: true) -IsarUserRepository userRepository(Ref ref) => - IsarUserRepository(ref.watch(isarProvider)); +IsarUserRepository userRepository(Ref ref) => IsarUserRepository(ref.watch(isarProvider)); @Riverpod(keepAlive: true) -UserApiRepository userApiRepository(Ref ref) => - UserApiRepository(ref.watch(apiServiceProvider).usersApi); +UserApiRepository userApiRepository(Ref ref) => UserApiRepository(ref.watch(apiServiceProvider).usersApi); @Riverpod(keepAlive: true) UserService userService(Ref ref) => UserService( - isarUserRepository: ref.watch(userRepositoryProvider), - userApiRepository: ref.watch(userApiRepositoryProvider), - storeService: ref.watch(storeServiceProvider), - ); + isarUserRepository: ref.watch(userRepositoryProvider), + userApiRepository: ref.watch(userApiRepositoryProvider), + storeService: ref.watch(storeServiceProvider), +); + +/// Drifts +final driftPartnerRepositoryProvider = Provider( + (ref) => DriftPartnerRepository(ref.watch(driftProvider)), +); + +final driftPartnerServiceProvider = Provider( + (ref) => DriftPartnerService(ref.watch(driftPartnerRepositoryProvider), ref.watch(partnerApiRepositoryProvider)), +); + +final partnerUsersProvider = NotifierProvider>(PartnerNotifier.new); diff --git a/mobile/lib/providers/infrastructure/user.provider.g.dart b/mobile/lib/providers/infrastructure/user.provider.g.dart index 7664b15fd5..f9148bf3a7 100644 --- a/mobile/lib/providers/infrastructure/user.provider.g.dart +++ b/mobile/lib/providers/infrastructure/user.provider.g.dart @@ -47,8 +47,9 @@ String _$userServiceHash() => r'181414dddc7891be6237e13d568c287a804228d1'; final userServiceProvider = Provider.internal( userService, name: r'userServiceProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$userServiceHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$userServiceHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/mobile/lib/providers/local_auth.provider.dart b/mobile/lib/providers/local_auth.provider.dart index 6f7ca5eb71..44fc5ad80c 100644 --- a/mobile/lib/providers/local_auth.provider.dart +++ b/mobile/lib/providers/local_auth.provider.dart @@ -9,12 +9,8 @@ import 'package:immich_mobile/services/local_auth.service.dart'; import 'package:immich_mobile/services/secure_storage.service.dart'; import 'package:logging/logging.dart'; -final localAuthProvider = - StateNotifierProvider((ref) { - return LocalAuthNotifier( - ref.watch(localAuthServiceProvider), - ref.watch(secureStorageServiceProvider), - ); +final localAuthProvider = StateNotifierProvider((ref) { + return LocalAuthNotifier(ref.watch(localAuthServiceProvider), ref.watch(secureStorageServiceProvider)); }); class LocalAuthNotifier extends StateNotifier { @@ -24,23 +20,14 @@ class LocalAuthNotifier extends StateNotifier { final _log = Logger("LocalAuthNotifier"); LocalAuthNotifier(this._localAuthService, this._secureStorageService) - : super( - const BiometricStatus( - availableBiometrics: [], - canAuthenticate: false, - ), - ) { + : super(const BiometricStatus(availableBiometrics: [], canAuthenticate: false)) { _localAuthService.getStatus().then((value) { - state = state.copyWith( - canAuthenticate: value.canAuthenticate, - availableBiometrics: value.availableBiometrics, - ); + state = state.copyWith(canAuthenticate: value.canAuthenticate, availableBiometrics: value.availableBiometrics); }); } Future registerBiometric(BuildContext context, String pinCode) async { - final isAuthenticated = - await authenticate(context, 'Authenticate to enable biometrics'); + final isAuthenticated = await authenticate(context, 'Authenticate to enable biometrics'); if (!isAuthenticated) { return false; @@ -81,10 +68,7 @@ class LocalAuthNotifier extends StateNotifier { if (errorMessage.isNotEmpty) { context.showSnackBar( SnackBar( - content: Text( - errorMessage, - style: context.textTheme.labelLarge, - ), + content: Text(errorMessage, style: context.textTheme.labelLarge), duration: const Duration(seconds: 3), backgroundColor: context.colorScheme.errorContainer, ), diff --git a/mobile/lib/providers/map/map_marker.provider.dart b/mobile/lib/providers/map/map_marker.provider.dart index 23342b77b3..e107dd3602 100644 --- a/mobile/lib/providers/map/map_marker.provider.dart +++ b/mobile/lib/providers/map/map_marker.provider.dart @@ -12,26 +12,17 @@ Future> mapMarkers(Ref ref) async { final mapState = ref.read(mapStateNotifierProvider); DateTime? fileCreatedAfter; bool? isFavorite; - bool? isIncludeArchived; - bool? isWithPartners; + bool isIncludeArchived = mapState.includeArchived; + bool isWithPartners = mapState.withPartners; if (mapState.relativeTime != 0) { - fileCreatedAfter = - DateTime.now().subtract(Duration(days: mapState.relativeTime)); + fileCreatedAfter = DateTime.now().subtract(Duration(days: mapState.relativeTime)); } if (mapState.showFavoriteOnly) { isFavorite = true; } - if (!mapState.includeArchived) { - isIncludeArchived = false; - } - - if (mapState.withPartners) { - isWithPartners = true; - } - final markers = await service.getMapMarkers( isFavorite: isFavorite, withArchived: isIncludeArchived, diff --git a/mobile/lib/providers/map/map_marker.provider.g.dart b/mobile/lib/providers/map/map_marker.provider.g.dart index 76cc44a103..80a21a39b2 100644 --- a/mobile/lib/providers/map/map_marker.provider.g.dart +++ b/mobile/lib/providers/map/map_marker.provider.g.dart @@ -6,15 +6,16 @@ part of 'map_marker.provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$mapMarkersHash() => r'f33ac4baa3251b3f06423aece89673315966f885'; +String _$mapMarkersHash() => r'a0c129fcddbf1b9bce4aafcd2e47a858ab6ef1c9'; /// See also [mapMarkers]. @ProviderFor(mapMarkers) final mapMarkersProvider = AutoDisposeFutureProvider>.internal( mapMarkers, name: r'mapMarkersProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$mapMarkersHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$mapMarkersHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/mobile/lib/providers/map/map_service.provider.g.dart b/mobile/lib/providers/map/map_service.provider.g.dart index 0bb5094c61..e8eb1cd1ee 100644 --- a/mobile/lib/providers/map/map_service.provider.g.dart +++ b/mobile/lib/providers/map/map_service.provider.g.dart @@ -13,8 +13,9 @@ String _$mapServiceHash() => r'ffc8f38b726083452b9df236ed58903879348987'; final mapServiceProvider = AutoDisposeProvider.internal( mapService, name: r'mapServiceProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$mapServiceHash, + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$mapServiceHash, dependencies: null, allTransitiveDependencies: null, ); diff --git a/mobile/lib/providers/map/map_state.provider.dart b/mobile/lib/providers/map/map_state.provider.dart index 189a23cd0a..31f2849df6 100644 --- a/mobile/lib/providers/map/map_state.provider.dart +++ b/mobile/lib/providers/map/map_state.provider.dart @@ -13,44 +13,28 @@ class MapStateNotifier extends _$MapStateNotifier { MapState build() { final appSettingsProvider = ref.read(appSettingsServiceProvider); - final lightStyleUrl = - ref.read(serverInfoProvider).serverConfig.mapLightStyleUrl; - final darkStyleUrl = - ref.read(serverInfoProvider).serverConfig.mapDarkStyleUrl; + final lightStyleUrl = ref.read(serverInfoProvider).serverConfig.mapLightStyleUrl; + final darkStyleUrl = ref.read(serverInfoProvider).serverConfig.mapDarkStyleUrl; return MapState( - themeMode: ThemeMode.values[ - appSettingsProvider.getSetting(AppSettingsEnum.mapThemeMode)], - showFavoriteOnly: appSettingsProvider - .getSetting(AppSettingsEnum.mapShowFavoriteOnly), - includeArchived: appSettingsProvider - .getSetting(AppSettingsEnum.mapIncludeArchived), - withPartners: - appSettingsProvider.getSetting(AppSettingsEnum.mapwithPartners), - relativeTime: - appSettingsProvider.getSetting(AppSettingsEnum.mapRelativeDate), + themeMode: ThemeMode.values[appSettingsProvider.getSetting(AppSettingsEnum.mapThemeMode)], + showFavoriteOnly: appSettingsProvider.getSetting(AppSettingsEnum.mapShowFavoriteOnly), + includeArchived: appSettingsProvider.getSetting(AppSettingsEnum.mapIncludeArchived), + withPartners: appSettingsProvider.getSetting(AppSettingsEnum.mapwithPartners), + relativeTime: appSettingsProvider.getSetting(AppSettingsEnum.mapRelativeDate), lightStyleFetched: AsyncData(lightStyleUrl), darkStyleFetched: AsyncData(darkStyleUrl), ); } void switchTheme(ThemeMode mode) { - ref.read(appSettingsServiceProvider).setSetting( - AppSettingsEnum.mapThemeMode, - mode.index, - ); + ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.mapThemeMode, mode.index); state = state.copyWith(themeMode: mode); } void switchFavoriteOnly(bool isFavoriteOnly) { - ref.read(appSettingsServiceProvider).setSetting( - AppSettingsEnum.mapShowFavoriteOnly, - isFavoriteOnly, - ); - state = state.copyWith( - showFavoriteOnly: isFavoriteOnly, - shouldRefetchMarkers: true, - ); + ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.mapShowFavoriteOnly, isFavoriteOnly); + state = state.copyWith(showFavoriteOnly: isFavoriteOnly, shouldRefetchMarkers: true); } void setRefetchMarkers(bool shouldRefetch) { @@ -58,35 +42,17 @@ class MapStateNotifier extends _$MapStateNotifier { } void switchIncludeArchived(bool isIncludeArchived) { - ref.read(appSettingsServiceProvider).setSetting( - AppSettingsEnum.mapIncludeArchived, - isIncludeArchived, - ); - state = state.copyWith( - includeArchived: isIncludeArchived, - shouldRefetchMarkers: true, - ); + ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.mapIncludeArchived, isIncludeArchived); + state = state.copyWith(includeArchived: isIncludeArchived, shouldRefetchMarkers: true); } void switchWithPartners(bool isWithPartners) { - ref.read(appSettingsServiceProvider).setSetting( - AppSettingsEnum.mapwithPartners, - isWithPartners, - ); - state = state.copyWith( - withPartners: isWithPartners, - shouldRefetchMarkers: true, - ); + ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.mapwithPartners, isWithPartners); + state = state.copyWith(withPartners: isWithPartners, shouldRefetchMarkers: true); } void setRelativeTime(int relativeTime) { - ref.read(appSettingsServiceProvider).setSetting( - AppSettingsEnum.mapRelativeDate, - relativeTime, - ); - state = state.copyWith( - relativeTime: relativeTime, - shouldRefetchMarkers: true, - ); + ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.mapRelativeDate, relativeTime); + state = state.copyWith(relativeTime: relativeTime, shouldRefetchMarkers: true); } } diff --git a/mobile/lib/providers/map/map_state.provider.g.dart b/mobile/lib/providers/map/map_state.provider.g.dart index 85a237099c..94d0ff8698 100644 --- a/mobile/lib/providers/map/map_state.provider.g.dart +++ b/mobile/lib/providers/map/map_state.provider.g.dart @@ -12,14 +12,14 @@ String _$mapStateNotifierHash() => r'22e4e571bd0730dbc34b109255a62b920e9c7d66'; @ProviderFor(MapStateNotifier) final mapStateNotifierProvider = NotifierProvider.internal( - MapStateNotifier.new, - name: r'mapStateNotifierProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$mapStateNotifierHash, - dependencies: null, - allTransitiveDependencies: null, -); + MapStateNotifier.new, + name: r'mapStateNotifierProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$mapStateNotifierHash, + dependencies: null, + allTransitiveDependencies: null, + ); typedef _$MapStateNotifier = Notifier; // ignore_for_file: type=lint diff --git a/mobile/lib/providers/memory.provider.dart b/mobile/lib/providers/memory.provider.dart index aed546002d..7fef3060cc 100644 --- a/mobile/lib/providers/memory.provider.dart +++ b/mobile/lib/providers/memory.provider.dart @@ -2,8 +2,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/models/memories/memory.model.dart'; import 'package:immich_mobile/services/memory.service.dart'; -final memoryFutureProvider = - FutureProvider.autoDispose?>((ref) async { +final memoryFutureProvider = FutureProvider.autoDispose?>((ref) async { final service = ref.watch(memoryServiceProvider); return await service.getMemoryLane(); diff --git a/mobile/lib/providers/network.provider.dart b/mobile/lib/providers/network.provider.dart index 5cb2fae4b1..cd91ff6d56 100644 --- a/mobile/lib/providers/network.provider.dart +++ b/mobile/lib/providers/network.provider.dart @@ -2,9 +2,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/services/network.service.dart'; final networkProvider = StateNotifierProvider((ref) { - return NetworkNotifier( - ref.watch(networkServiceProvider), - ); + return NetworkNotifier(ref.watch(networkServiceProvider)); }); class NetworkNotifier extends StateNotifier { diff --git a/mobile/lib/providers/notification_permission.provider.dart b/mobile/lib/providers/notification_permission.provider.dart index 608f35d63f..da0badd4ec 100644 --- a/mobile/lib/providers/notification_permission.provider.dart +++ b/mobile/lib/providers/notification_permission.provider.dart @@ -5,11 +5,7 @@ import 'package:permission_handler/permission_handler.dart'; class NotificationPermissionNotifier extends StateNotifier { NotificationPermissionNotifier() - : super( - Platform.isAndroid - ? PermissionStatus.granted - : PermissionStatus.restricted, - ) { + : super(Platform.isAndroid ? PermissionStatus.granted : PermissionStatus.restricted) { // Sets the initial state getNotificationPermission().then((p) => state = p); } @@ -40,7 +36,6 @@ class NotificationPermissionNotifier extends StateNotifier { } } -final notificationPermissionProvider = - StateNotifierProvider( +final notificationPermissionProvider = StateNotifierProvider( (ref) => NotificationPermissionNotifier(), ); diff --git a/mobile/lib/providers/oauth.provider.dart b/mobile/lib/providers/oauth.provider.dart index d8d66122f7..14b3353943 100644 --- a/mobile/lib/providers/oauth.provider.dart +++ b/mobile/lib/providers/oauth.provider.dart @@ -2,5 +2,4 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/services/oauth.service.dart'; import 'package:immich_mobile/providers/api.provider.dart'; -final oAuthServiceProvider = - Provider((ref) => OAuthService(ref.watch(apiServiceProvider))); +final oAuthServiceProvider = Provider((ref) => OAuthService(ref.watch(apiServiceProvider))); diff --git a/mobile/lib/providers/partner.provider.dart b/mobile/lib/providers/partner.provider.dart index f210c7fe3f..5a85cea1d4 100644 --- a/mobile/lib/providers/partner.provider.dart +++ b/mobile/lib/providers/partner.provider.dart @@ -12,17 +12,20 @@ class PartnerSharedWithNotifier extends StateNotifier> { PartnerSharedWithNotifier(this._partnerService) : super([]) { Function eq = const ListEquality().equals; - _partnerService.getSharedWith().then((partners) { - if (!eq(state, partners)) { - state = partners; - } - }).then((_) { - streamSub = _partnerService.watchSharedWith().listen((partners) { - if (!eq(state, partners)) { - state = partners; - } - }); - }); + _partnerService + .getSharedWith() + .then((partners) { + if (!eq(state, partners)) { + state = partners; + } + }) + .then((_) { + streamSub = _partnerService.watchSharedWith().listen((partners) { + if (!eq(state, partners)) { + state = partners; + } + }); + }); } Future updatePartner(UserDto partner, {required bool inTimeline}) { @@ -38,11 +41,8 @@ class PartnerSharedWithNotifier extends StateNotifier> { } } -final partnerSharedWithProvider = - StateNotifierProvider>((ref) { - return PartnerSharedWithNotifier( - ref.watch(partnerServiceProvider), - ); +final partnerSharedWithProvider = StateNotifierProvider>((ref) { + return PartnerSharedWithNotifier(ref.watch(partnerServiceProvider)); }); class PartnerSharedByNotifier extends StateNotifier> { @@ -51,17 +51,20 @@ class PartnerSharedByNotifier extends StateNotifier> { PartnerSharedByNotifier(this._partnerService) : super([]) { Function eq = const ListEquality().equals; - _partnerService.getSharedBy().then((partners) { - if (!eq(state, partners)) { - state = partners; - } - }).then((_) { - streamSub = _partnerService.watchSharedBy().listen((partners) { - if (!eq(state, partners)) { - state = partners; - } - }); - }); + _partnerService + .getSharedBy() + .then((partners) { + if (!eq(state, partners)) { + state = partners; + } + }) + .then((_) { + streamSub = _partnerService.watchSharedBy().listen((partners) { + if (!eq(state, partners)) { + state = partners; + } + }); + }); } @override @@ -73,13 +76,11 @@ class PartnerSharedByNotifier extends StateNotifier> { } } -final partnerSharedByProvider = - StateNotifierProvider>((ref) { +final partnerSharedByProvider = StateNotifierProvider>((ref) { return PartnerSharedByNotifier(ref.watch(partnerServiceProvider)); }); -final partnerAvailableProvider = - FutureProvider.autoDispose>((ref) async { +final partnerAvailableProvider = FutureProvider.autoDispose>((ref) async { final otherUsers = await ref.watch(otherUsersProvider.future); final currentPartners = ref.watch(partnerSharedByProvider); final available = Set.of(otherUsers); diff --git a/mobile/lib/providers/routes.provider.dart b/mobile/lib/providers/routes.provider.dart index 74d86f4767..c51f67bc0e 100644 --- a/mobile/lib/providers/routes.provider.dart +++ b/mobile/lib/providers/routes.provider.dart @@ -1,4 +1,7 @@ +import 'package:flutter/widgets.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; final inLockedViewProvider = StateProvider((ref) => false); final currentRouteNameProvider = StateProvider((ref) => null); +final previousRouteNameProvider = StateProvider((ref) => null); +final previousRouteDataProvider = StateProvider((ref) => null); diff --git a/mobile/lib/providers/search/paginated_search.provider.dart b/mobile/lib/providers/search/paginated_search.provider.dart index 65afb39b7c..9a37d83320 100644 --- a/mobile/lib/providers/search/paginated_search.provider.dart +++ b/mobile/lib/providers/search/paginated_search.provider.dart @@ -8,16 +8,14 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'paginated_search.provider.g.dart'; -final paginatedSearchProvider = - StateNotifierProvider( +final paginatedSearchProvider = StateNotifierProvider( (ref) => PaginatedSearchNotifier(ref.watch(searchServiceProvider)), ); class PaginatedSearchNotifier extends StateNotifier { final SearchService _searchService; - PaginatedSearchNotifier(this._searchService) - : super(const SearchResult(assets: [], nextPage: 1)); + PaginatedSearchNotifier(this._searchService) : super(const SearchResult(assets: [], nextPage: 1)); Future search(SearchFilter filter) async { if (state.nextPage == null) { @@ -30,10 +28,7 @@ class PaginatedSearchNotifier extends StateNotifier { return false; } - state = SearchResult( - assets: [...state.assets, ...result.assets], - nextPage: result.nextPage, - ); + state = SearchResult(assets: [...state.assets, ...result.assets], nextPage: result.nextPage); return true; } @@ -44,13 +39,8 @@ class PaginatedSearchNotifier extends StateNotifier { } @riverpod -Future paginatedSearchRenderList( - Ref ref, -) { +Future paginatedSearchRenderList(Ref ref) { final result = ref.watch(paginatedSearchProvider); final timelineService = ref.watch(timelineServiceProvider); - return timelineService.getTimelineFromAssets( - result.assets, - GroupAssetsBy.none, - ); + return timelineService.getTimelineFromAssets(result.assets, GroupAssetsBy.none); } diff --git a/mobile/lib/providers/search/paginated_search.provider.g.dart b/mobile/lib/providers/search/paginated_search.provider.g.dart index 650cf130fc..e984997967 100644 --- a/mobile/lib/providers/search/paginated_search.provider.g.dart +++ b/mobile/lib/providers/search/paginated_search.provider.g.dart @@ -13,14 +13,14 @@ String _$paginatedSearchRenderListHash() => @ProviderFor(paginatedSearchRenderList) final paginatedSearchRenderListProvider = AutoDisposeFutureProvider.internal( - paginatedSearchRenderList, - name: r'paginatedSearchRenderListProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$paginatedSearchRenderListHash, - dependencies: null, - allTransitiveDependencies: null, -); + paginatedSearchRenderList, + name: r'paginatedSearchRenderListProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$paginatedSearchRenderListHash, + dependencies: null, + allTransitiveDependencies: null, + ); @Deprecated('Will be removed in 3.0. Use Ref instead') // ignore: unused_element diff --git a/mobile/lib/providers/search/people.provider.dart b/mobile/lib/providers/search/people.provider.dart index d03d533aaf..3ff8d67983 100644 --- a/mobile/lib/providers/search/people.provider.dart +++ b/mobile/lib/providers/search/people.provider.dart @@ -9,9 +9,7 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'people.provider.g.dart'; @riverpod -Future> getAllPeople( - Ref ref, -) async { +Future> getAllPeople(Ref ref) async { final PersonService personService = ref.read(personServiceProvider); final people = await personService.getAllPeople(); @@ -25,17 +23,12 @@ Future personAssets(Ref ref, String personId) async { final assets = await personService.getPersonAssets(personId); final settings = ref.read(appSettingsServiceProvider); - final groupBy = - GroupAssetsBy.values[settings.getSetting(AppSettingsEnum.groupAssetsBy)]; + final groupBy = GroupAssetsBy.values[settings.getSetting(AppSettingsEnum.groupAssetsBy)]; return await RenderList.fromAssets(assets, groupBy); } @riverpod -Future updatePersonName( - Ref ref, - String personId, - String updatedName, -) async { +Future updatePersonName(Ref ref, String personId, String updatedName) async { final PersonService personService = ref.read(personServiceProvider); final person = await personService.updateName(personId, updatedName); diff --git a/mobile/lib/providers/search/people.provider.g.dart b/mobile/lib/providers/search/people.provider.g.dart index 391edd362c..9595c36eec 100644 --- a/mobile/lib/providers/search/people.provider.g.dart +++ b/mobile/lib/providers/search/people.provider.g.dart @@ -6,22 +6,24 @@ part of 'people.provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$getAllPeopleHash() => r'226947af3b09ce62224916543958dd1d5e2ba651'; +String _$getAllPeopleHash() => r'2c5e6a207683f15ab209650615fdf9cb7f76c736'; /// See also [getAllPeople]. @ProviderFor(getAllPeople) -final getAllPeopleProvider = AutoDisposeFutureProvider>.internal( - getAllPeople, - name: r'getAllPeopleProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$getAllPeopleHash, - dependencies: null, - allTransitiveDependencies: null, -); +final getAllPeopleProvider = + AutoDisposeFutureProvider>.internal( + getAllPeople, + name: r'getAllPeopleProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$getAllPeopleHash, + dependencies: null, + allTransitiveDependencies: null, + ); @Deprecated('Will be removed in 3.0. Use Ref instead') // ignore: unused_element -typedef GetAllPeopleRef = AutoDisposeFutureProviderRef>; +typedef GetAllPeopleRef = AutoDisposeFutureProviderRef>; String _$personAssetsHash() => r'c1d35ee0e024bd6915e21bc724be4b458a14bc24'; /// Copied from Dart SDK @@ -55,21 +57,15 @@ class PersonAssetsFamily extends Family> { const PersonAssetsFamily(); /// See also [personAssets]. - PersonAssetsProvider call( - String personId, - ) { - return PersonAssetsProvider( - personId, - ); + PersonAssetsProvider call(String personId) { + return PersonAssetsProvider(personId); } @override PersonAssetsProvider getProviderOverride( covariant PersonAssetsProvider provider, ) { - return call( - provider.personId, - ); + return call(provider.personId); } static const Iterable? _dependencies = null; @@ -90,24 +86,19 @@ class PersonAssetsFamily extends Family> { /// See also [personAssets]. class PersonAssetsProvider extends AutoDisposeFutureProvider { /// See also [personAssets]. - PersonAssetsProvider( - String personId, - ) : this._internal( - (ref) => personAssets( - ref as PersonAssetsRef, - personId, - ), - from: personAssetsProvider, - name: r'personAssetsProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$personAssetsHash, - dependencies: PersonAssetsFamily._dependencies, - allTransitiveDependencies: - PersonAssetsFamily._allTransitiveDependencies, - personId: personId, - ); + PersonAssetsProvider(String personId) + : this._internal( + (ref) => personAssets(ref as PersonAssetsRef, personId), + from: personAssetsProvider, + name: r'personAssetsProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$personAssetsHash, + dependencies: PersonAssetsFamily._dependencies, + allTransitiveDependencies: + PersonAssetsFamily._allTransitiveDependencies, + personId: personId, + ); PersonAssetsProvider._internal( super._createNotifier, { @@ -166,7 +157,8 @@ mixin PersonAssetsRef on AutoDisposeFutureProviderRef { } class _PersonAssetsProviderElement - extends AutoDisposeFutureProviderElement with PersonAssetsRef { + extends AutoDisposeFutureProviderElement + with PersonAssetsRef { _PersonAssetsProviderElement(super.provider); @override @@ -185,24 +177,15 @@ class UpdatePersonNameFamily extends Family> { const UpdatePersonNameFamily(); /// See also [updatePersonName]. - UpdatePersonNameProvider call( - String personId, - String updatedName, - ) { - return UpdatePersonNameProvider( - personId, - updatedName, - ); + UpdatePersonNameProvider call(String personId, String updatedName) { + return UpdatePersonNameProvider(personId, updatedName); } @override UpdatePersonNameProvider getProviderOverride( covariant UpdatePersonNameProvider provider, ) { - return call( - provider.personId, - provider.updatedName, - ); + return call(provider.personId, provider.updatedName); } static const Iterable? _dependencies = null; @@ -223,27 +206,21 @@ class UpdatePersonNameFamily extends Family> { /// See also [updatePersonName]. class UpdatePersonNameProvider extends AutoDisposeFutureProvider { /// See also [updatePersonName]. - UpdatePersonNameProvider( - String personId, - String updatedName, - ) : this._internal( - (ref) => updatePersonName( - ref as UpdatePersonNameRef, - personId, - updatedName, - ), - from: updatePersonNameProvider, - name: r'updatePersonNameProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$updatePersonNameHash, - dependencies: UpdatePersonNameFamily._dependencies, - allTransitiveDependencies: - UpdatePersonNameFamily._allTransitiveDependencies, - personId: personId, - updatedName: updatedName, - ); + UpdatePersonNameProvider(String personId, String updatedName) + : this._internal( + (ref) => + updatePersonName(ref as UpdatePersonNameRef, personId, updatedName), + from: updatePersonNameProvider, + name: r'updatePersonNameProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$updatePersonNameHash, + dependencies: UpdatePersonNameFamily._dependencies, + allTransitiveDependencies: + UpdatePersonNameFamily._allTransitiveDependencies, + personId: personId, + updatedName: updatedName, + ); UpdatePersonNameProvider._internal( super._createNotifier, { @@ -311,7 +288,8 @@ mixin UpdatePersonNameRef on AutoDisposeFutureProviderRef { } class _UpdatePersonNameProviderElement - extends AutoDisposeFutureProviderElement with UpdatePersonNameRef { + extends AutoDisposeFutureProviderElement + with UpdatePersonNameRef { _UpdatePersonNameProviderElement(super.provider); @override @@ -319,5 +297,6 @@ class _UpdatePersonNameProviderElement @override String get updatedName => (origin as UpdatePersonNameProvider).updatedName; } + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/mobile/lib/providers/search/search_filter.provider.g.dart b/mobile/lib/providers/search/search_filter.provider.g.dart index 03f88b0332..5a322ca285 100644 --- a/mobile/lib/providers/search/search_filter.provider.g.dart +++ b/mobile/lib/providers/search/search_filter.provider.g.dart @@ -95,29 +95,28 @@ class GetSearchSuggestionsProvider String? make, String? model, }) : this._internal( - (ref) => getSearchSuggestions( - ref as GetSearchSuggestionsRef, - type, - locationCountry: locationCountry, - locationState: locationState, - make: make, - model: model, - ), - from: getSearchSuggestionsProvider, - name: r'getSearchSuggestionsProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') - ? null - : _$getSearchSuggestionsHash, - dependencies: GetSearchSuggestionsFamily._dependencies, - allTransitiveDependencies: - GetSearchSuggestionsFamily._allTransitiveDependencies, - type: type, - locationCountry: locationCountry, - locationState: locationState, - make: make, - model: model, - ); + (ref) => getSearchSuggestions( + ref as GetSearchSuggestionsRef, + type, + locationCountry: locationCountry, + locationState: locationState, + make: make, + model: model, + ), + from: getSearchSuggestionsProvider, + name: r'getSearchSuggestionsProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$getSearchSuggestionsHash, + dependencies: GetSearchSuggestionsFamily._dependencies, + allTransitiveDependencies: + GetSearchSuggestionsFamily._allTransitiveDependencies, + type: type, + locationCountry: locationCountry, + locationState: locationState, + make: make, + model: model, + ); GetSearchSuggestionsProvider._internal( super._createNotifier, { @@ -227,5 +226,6 @@ class _GetSearchSuggestionsProviderElement @override String? get model => (origin as GetSearchSuggestionsProvider).model; } + // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/mobile/lib/providers/search/search_page_state.provider.dart b/mobile/lib/providers/search/search_page_state.provider.dart index d0e3720c0f..23d5606922 100644 --- a/mobile/lib/providers/search/search_page_state.provider.dart +++ b/mobile/lib/providers/search/search_page_state.provider.dart @@ -3,8 +3,7 @@ import 'package:immich_mobile/models/search/search_curated_content.model.dart'; import 'package:immich_mobile/services/search.service.dart'; -final getPreviewPlacesProvider = - FutureProvider.autoDispose>((ref) async { +final getPreviewPlacesProvider = FutureProvider.autoDispose>((ref) async { final SearchService searchService = ref.watch(searchServiceProvider); final exploreData = await searchService.getExploreData(); @@ -13,23 +12,14 @@ final getPreviewPlacesProvider = return []; } - final locations = - exploreData.firstWhere((data) => data.fieldName == "exifInfo.city").items; + final locations = exploreData.firstWhere((data) => data.fieldName == "exifInfo.city").items; - final curatedContent = locations - .map( - (l) => SearchCuratedContent( - label: l.value, - id: l.data.id, - ), - ) - .toList(); + final curatedContent = locations.map((l) => SearchCuratedContent(label: l.value, id: l.data.id)).toList(); return curatedContent; }); -final getAllPlacesProvider = - FutureProvider.autoDispose>((ref) async { +final getAllPlacesProvider = FutureProvider.autoDispose>((ref) async { final SearchService searchService = ref.watch(searchServiceProvider); final assetPlaces = await searchService.getAllPlaces(); @@ -39,12 +29,7 @@ final getAllPlacesProvider = } final curatedContent = assetPlaces - .map( - (data) => SearchCuratedContent( - label: data.exifInfo!.city!, - id: data.id, - ), - ) + .map((data) => SearchCuratedContent(label: data.exifInfo!.city!, id: data.id)) .toList(); return curatedContent; diff --git a/mobile/lib/providers/secure_storage.provider.dart b/mobile/lib/providers/secure_storage.provider.dart index 0194e527e9..39813d1027 100644 --- a/mobile/lib/providers/secure_storage.provider.dart +++ b/mobile/lib/providers/secure_storage.provider.dart @@ -1,7 +1,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; -final secureStorageProvider = - StateNotifierProvider((ref) { +final secureStorageProvider = StateNotifierProvider((ref) { return SecureStorageProvider(); }); diff --git a/mobile/lib/providers/server_info.provider.dart b/mobile/lib/providers/server_info.provider.dart index aabf6a5390..25b1002b7a 100644 --- a/mobile/lib/providers/server_info.provider.dart +++ b/mobile/lib/providers/server_info.provider.dart @@ -11,43 +11,24 @@ import 'package:package_info_plus/package_info_plus.dart'; class ServerInfoNotifier extends StateNotifier { ServerInfoNotifier(this._serverInfoService) - : super( - const ServerInfo( - serverVersion: ServerVersion( - major: 0, - minor: 0, - patch: 0, - ), - latestVersion: ServerVersion( - major: 0, - minor: 0, - patch: 0, - ), - serverFeatures: ServerFeatures( - map: true, - trash: true, - oauthEnabled: false, - passwordLogin: true, - ), - serverConfig: ServerConfig( - trashDays: 30, - oauthButtonText: '', - externalDomain: '', - mapLightStyleUrl: - 'https://tiles.immich.cloud/v1/style/light.json', - mapDarkStyleUrl: 'https://tiles.immich.cloud/v1/style/dark.json', - ), - serverDiskInfo: ServerDiskInfo( - diskAvailable: "0", - diskSize: "0", - diskUse: "0", - diskUsagePercentage: 0, - ), - isVersionMismatch: false, - isNewReleaseAvailable: false, - versionMismatchErrorMessage: "", + : super( + const ServerInfo( + serverVersion: ServerVersion(major: 0, minor: 0, patch: 0), + latestVersion: ServerVersion(major: 0, minor: 0, patch: 0), + serverFeatures: ServerFeatures(map: true, trash: true, oauthEnabled: false, passwordLogin: true), + serverConfig: ServerConfig( + trashDays: 30, + oauthButtonText: '', + externalDomain: '', + mapLightStyleUrl: 'https://tiles.immich.cloud/v1/style/light.json', + mapDarkStyleUrl: 'https://tiles.immich.cloud/v1/style/dark.json', ), - ); + serverDiskInfo: ServerDiskInfo(diskAvailable: "0", diskSize: "0", diskUse: "0", diskUsagePercentage: 0), + isVersionMismatch: false, + isNewReleaseAvailable: false, + versionMismatchErrorMessage: "", + ), + ); final ServerInfoService _serverInfoService; final _log = Logger("ServerInfoNotifier"); @@ -63,19 +44,14 @@ class ServerInfoNotifier extends StateNotifier { final serverVersion = await _serverInfoService.getServerVersion(); if (serverVersion == null) { - state = state.copyWith( - isVersionMismatch: true, - versionMismatchErrorMessage: "common_server_error".tr(), - ); + state = state.copyWith(isVersionMismatch: true, versionMismatchErrorMessage: "common_server_error".tr()); return; } await _checkServerVersionMismatch(serverVersion); } catch (e, stackTrace) { _log.severe("Failed to get server version", e, stackTrace); - state = state.copyWith( - isVersionMismatch: true, - ); + state = state.copyWith(isVersionMismatch: true); return; } } @@ -90,8 +66,7 @@ class ServerInfoNotifier extends StateNotifier { if (appVersion["major"]! > serverVersion.major) { state = state.copyWith( isVersionMismatch: true, - versionMismatchErrorMessage: - "profile_drawer_server_out_of_date_major".tr(), + versionMismatchErrorMessage: "profile_drawer_server_out_of_date_major".tr(), ); return; } @@ -99,8 +74,7 @@ class ServerInfoNotifier extends StateNotifier { if (appVersion["major"]! < serverVersion.major) { state = state.copyWith( isVersionMismatch: true, - versionMismatchErrorMessage: - "profile_drawer_client_out_of_date_major".tr(), + versionMismatchErrorMessage: "profile_drawer_client_out_of_date_major".tr(), ); return; } @@ -108,8 +82,7 @@ class ServerInfoNotifier extends StateNotifier { if (appVersion["minor"]! > serverVersion.minor) { state = state.copyWith( isVersionMismatch: true, - versionMismatchErrorMessage: - "profile_drawer_server_out_of_date_minor".tr(), + versionMismatchErrorMessage: "profile_drawer_server_out_of_date_minor".tr(), ); return; } @@ -117,35 +90,26 @@ class ServerInfoNotifier extends StateNotifier { if (appVersion["minor"]! < serverVersion.minor) { state = state.copyWith( isVersionMismatch: true, - versionMismatchErrorMessage: - "profile_drawer_client_out_of_date_minor".tr(), + versionMismatchErrorMessage: "profile_drawer_client_out_of_date_minor".tr(), ); return; } - state = state.copyWith( - isVersionMismatch: false, - versionMismatchErrorMessage: "", - ); + state = state.copyWith(isVersionMismatch: false, versionMismatchErrorMessage: ""); } - handleNewRelease( - ServerVersion serverVersion, - ServerVersion latestVersion, - ) { + handleNewRelease(ServerVersion serverVersion, ServerVersion latestVersion) { // Update local server version _checkServerVersionMismatch(serverVersion); final majorEqual = latestVersion.major == serverVersion.major; final minorEqual = majorEqual && latestVersion.minor == serverVersion.minor; - final newVersionAvailable = latestVersion.major > serverVersion.major || + final newVersionAvailable = + latestVersion.major > serverVersion.major || (majorEqual && latestVersion.minor > serverVersion.minor) || (minorEqual && latestVersion.patch > serverVersion.patch); - state = state.copyWith( - latestVersion: latestVersion, - isNewReleaseAvailable: newVersionAvailable, - ); + state = state.copyWith(latestVersion: latestVersion, isNewReleaseAvailable: newVersionAvailable); } getServerFeatures() async { @@ -171,15 +135,10 @@ class ServerInfoNotifier extends StateNotifier { var minor = detail[1]; var patch = detail[2]; - return { - "major": int.parse(major), - "minor": int.parse(minor), - "patch": int.parse(patch.replaceAll("-DEBUG", "")), - }; + return {"major": int.parse(major), "minor": int.parse(minor), "patch": int.parse(patch.replaceAll("-DEBUG", ""))}; } } -final serverInfoProvider = - StateNotifierProvider((ref) { +final serverInfoProvider = StateNotifierProvider((ref) { return ServerInfoNotifier(ref.read(serverInfoServiceProvider)); }); diff --git a/mobile/lib/providers/shared_link.provider.dart b/mobile/lib/providers/shared_link.provider.dart index 29b628c765..f574554bcb 100644 --- a/mobile/lib/providers/shared_link.provider.dart +++ b/mobile/lib/providers/shared_link.provider.dart @@ -20,10 +20,6 @@ class SharedLinksNotifier extends StateNotifier>> { } } -final sharedLinksStateProvider = - StateNotifierProvider>>( - (ref) { - return SharedLinksNotifier( - ref.watch(sharedLinkServiceProvider), - ); +final sharedLinksStateProvider = StateNotifierProvider>>((ref) { + return SharedLinksNotifier(ref.watch(sharedLinkServiceProvider)); }); diff --git a/mobile/lib/providers/sync_status.provider.dart b/mobile/lib/providers/sync_status.provider.dart new file mode 100644 index 0000000000..8e24bbf4d0 --- /dev/null +++ b/mobile/lib/providers/sync_status.provider.dart @@ -0,0 +1,114 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; + +enum SyncStatus { + idle, + syncing, + success, + error; + + localized() { + return switch (this) { + SyncStatus.idle => "idle".tr(), + SyncStatus.syncing => "running".tr(), + SyncStatus.success => "success".tr(), + SyncStatus.error => "error".tr(), + }; + } +} + +class SyncStatusState { + final SyncStatus remoteSyncStatus; + final SyncStatus localSyncStatus; + final SyncStatus hashJobStatus; + + final String? errorMessage; + + const SyncStatusState({ + this.remoteSyncStatus = SyncStatus.idle, + this.localSyncStatus = SyncStatus.idle, + this.hashJobStatus = SyncStatus.idle, + this.errorMessage, + }); + + SyncStatusState copyWith({ + SyncStatus? remoteSyncStatus, + SyncStatus? localSyncStatus, + SyncStatus? hashJobStatus, + String? errorMessage, + }) { + return SyncStatusState( + remoteSyncStatus: remoteSyncStatus ?? this.remoteSyncStatus, + localSyncStatus: localSyncStatus ?? this.localSyncStatus, + hashJobStatus: hashJobStatus ?? this.hashJobStatus, + errorMessage: errorMessage ?? this.errorMessage, + ); + } + + bool get isRemoteSyncing => remoteSyncStatus == SyncStatus.syncing; + bool get isLocalSyncing => localSyncStatus == SyncStatus.syncing; + bool get isHashing => hashJobStatus == SyncStatus.syncing; + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + return other is SyncStatusState && + other.remoteSyncStatus == remoteSyncStatus && + other.localSyncStatus == localSyncStatus && + other.hashJobStatus == hashJobStatus && + other.errorMessage == errorMessage; + } + + @override + int get hashCode => Object.hash(remoteSyncStatus, localSyncStatus, hashJobStatus, errorMessage); +} + +class SyncStatusNotifier extends Notifier { + @override + SyncStatusState build() { + return const SyncStatusState( + errorMessage: null, + remoteSyncStatus: SyncStatus.idle, + localSyncStatus: SyncStatus.idle, + hashJobStatus: SyncStatus.idle, + ); + } + + /// + /// Remote Sync + /// + + void setRemoteSyncStatus(SyncStatus status, [String? errorMessage]) { + state = state.copyWith(remoteSyncStatus: status, errorMessage: status == SyncStatus.error ? errorMessage : null); + } + + void startRemoteSync() => setRemoteSyncStatus(SyncStatus.syncing); + void completeRemoteSync() => setRemoteSyncStatus(SyncStatus.success); + void errorRemoteSync(String error) => setRemoteSyncStatus(SyncStatus.error, error); + + /// + /// Local Sync + /// + + void setLocalSyncStatus(SyncStatus status, [String? errorMessage]) { + state = state.copyWith(localSyncStatus: status, errorMessage: status == SyncStatus.error ? errorMessage : null); + } + + void startLocalSync() => setLocalSyncStatus(SyncStatus.syncing); + void completeLocalSync() => setLocalSyncStatus(SyncStatus.success); + void errorLocalSync(String error) => setLocalSyncStatus(SyncStatus.error, error); + + /// + /// Hash Job + /// + + void setHashJobStatus(SyncStatus status, [String? errorMessage]) { + state = state.copyWith(hashJobStatus: status, errorMessage: status == SyncStatus.error ? errorMessage : null); + } + + void startHashJob() => setHashJobStatus(SyncStatus.syncing); + void completeHashJob() => setHashJobStatus(SyncStatus.success); + void errorHashJob(String error) => setHashJobStatus(SyncStatus.error, error); +} + +final syncStatusProvider = NotifierProvider(SyncStatusNotifier.new); diff --git a/mobile/lib/providers/tab.provider.dart b/mobile/lib/providers/tab.provider.dart index a4875115ce..d523e72c38 100644 --- a/mobile/lib/providers/tab.provider.dart +++ b/mobile/lib/providers/tab.provider.dart @@ -3,6 +3,4 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; enum TabEnum { home, search, albums, library } /// Provides the currently active tab -final tabProvider = StateProvider( - (ref) => TabEnum.home, -); +final tabProvider = StateProvider((ref) => TabEnum.home); diff --git a/mobile/lib/providers/theme.provider.dart b/mobile/lib/providers/theme.provider.dart index 73623bd026..5f32e07578 100644 --- a/mobile/lib/providers/theme.provider.dart +++ b/mobile/lib/providers/theme.provider.dart @@ -9,9 +9,7 @@ import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; final immichThemeModeProvider = StateProvider((ref) { - final themeMode = ref - .watch(appSettingsServiceProvider) - .getSetting(AppSettingsEnum.themeMode); + final themeMode = ref.watch(appSettingsServiceProvider).getSetting(AppSettingsEnum.themeMode); debugPrint("Current themeMode $themeMode"); @@ -26,36 +24,25 @@ final immichThemeModeProvider = StateProvider((ref) { final immichThemePresetProvider = StateProvider((ref) { final appSettingsProvider = ref.watch(appSettingsServiceProvider); - final primaryColorPreset = - appSettingsProvider.getSetting(AppSettingsEnum.primaryColor); + final primaryColorPreset = appSettingsProvider.getSetting(AppSettingsEnum.primaryColor); debugPrint("Current theme preset $primaryColorPreset"); try { - return ImmichColorPreset.values - .firstWhere((e) => e.name == primaryColorPreset); + return ImmichColorPreset.values.firstWhere((e) => e.name == primaryColorPreset); } catch (e) { - debugPrint( - "Theme preset $primaryColorPreset not found. Applying default preset.", - ); - appSettingsProvider.setSetting( - AppSettingsEnum.primaryColor, - defaultColorPresetName, - ); + debugPrint("Theme preset $primaryColorPreset not found. Applying default preset."); + appSettingsProvider.setSetting(AppSettingsEnum.primaryColor, defaultColorPresetName); return defaultColorPreset; } }); final dynamicThemeSettingProvider = StateProvider((ref) { - return ref - .watch(appSettingsServiceProvider) - .getSetting(AppSettingsEnum.dynamicTheme); + return ref.watch(appSettingsServiceProvider).getSetting(AppSettingsEnum.dynamicTheme); }); final colorfulInterfaceSettingProvider = StateProvider((ref) { - return ref - .watch(appSettingsServiceProvider) - .getSetting(AppSettingsEnum.colorfulInterface); + return ref.watch(appSettingsServiceProvider).getSetting(AppSettingsEnum.colorfulInterface); }); // Provider for current selected theme @@ -64,11 +51,7 @@ final immichThemeProvider = StateProvider((ref) { final useSystemColor = ref.watch(dynamicThemeSettingProvider); final useColorfulInterface = ref.watch(colorfulInterfaceSettingProvider); final ImmichTheme? dynamicTheme = DynamicTheme.theme; - final currentTheme = (useSystemColor && dynamicTheme != null) - ? dynamicTheme - : primaryColorPreset.themeOfPreset; + final currentTheme = (useSystemColor && dynamicTheme != null) ? dynamicTheme : primaryColorPreset.themeOfPreset; - return useColorfulInterface - ? currentTheme - : decolorizeSurfaces(theme: currentTheme); + return useColorfulInterface ? currentTheme : decolorizeSurfaces(theme: currentTheme); }); diff --git a/mobile/lib/providers/timeline.provider.dart b/mobile/lib/providers/timeline.provider.dart index b2c763cdfa..71ea308dbf 100644 --- a/mobile/lib/providers/timeline.provider.dart +++ b/mobile/lib/providers/timeline.provider.dart @@ -5,31 +5,23 @@ import 'package:immich_mobile/providers/locale_provider.dart'; import 'package:immich_mobile/services/timeline.service.dart'; import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; -final singleUserTimelineProvider = StreamProvider.family( - (ref, userId) { - if (userId == null) { - return const Stream.empty(); - } +final singleUserTimelineProvider = StreamProvider.family((ref, userId) { + if (userId == null) { + return const Stream.empty(); + } - ref.watch(localeProvider); - final timelineService = ref.watch(timelineServiceProvider); - return timelineService.watchHomeTimeline(userId); - }, - dependencies: [localeProvider], -); + ref.watch(localeProvider); + final timelineService = ref.watch(timelineServiceProvider); + return timelineService.watchHomeTimeline(userId); +}, dependencies: [localeProvider]); -final multiUsersTimelineProvider = - StreamProvider.family>( - (ref, userIds) { - ref.watch(localeProvider); - final timelineService = ref.watch(timelineServiceProvider); - return timelineService.watchMultiUsersTimeline(userIds); - }, - dependencies: [localeProvider], -); +final multiUsersTimelineProvider = StreamProvider.family>((ref, userIds) { + ref.watch(localeProvider); + final timelineService = ref.watch(timelineServiceProvider); + return timelineService.watchMultiUsersTimeline(userIds); +}, dependencies: [localeProvider]); -final albumTimelineProvider = - StreamProvider.autoDispose.family((ref, id) { +final albumTimelineProvider = StreamProvider.autoDispose.family((ref, id) { final album = ref.watch(albumWatcher(id)).value; final timelineService = ref.watch(timelineServiceProvider); @@ -65,13 +57,9 @@ final assetSelectionTimelineProvider = StreamProvider((ref) { return timelineService.watchAssetSelectionTimeline(); }); -final assetsTimelineProvider = - FutureProvider.family>((ref, assets) { +final assetsTimelineProvider = FutureProvider.family>((ref, assets) { final timelineService = ref.watch(timelineServiceProvider); - return timelineService.getTimelineFromAssets( - assets, - null, - ); + return timelineService.getTimelineFromAssets(assets, null); }); final lockedTimelineProvider = StreamProvider((ref) { diff --git a/mobile/lib/providers/timeline/multiselect.provider.dart b/mobile/lib/providers/timeline/multiselect.provider.dart index b1a926545d..742cbd7dea 100644 --- a/mobile/lib/providers/timeline/multiselect.provider.dart +++ b/mobile/lib/providers/timeline/multiselect.provider.dart @@ -5,8 +5,7 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/services/timeline.service.dart'; import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; -final multiSelectProvider = - NotifierProvider( +final multiSelectProvider = NotifierProvider( MultiSelectNotifier.new, dependencies: [timelineServiceProvider], ); @@ -16,21 +15,17 @@ class MultiSelectState { final Set lockedSelectionAssets; final bool forceEnable; - const MultiSelectState({ - required this.selectedAssets, - required this.lockedSelectionAssets, - this.forceEnable = false, - }); + const MultiSelectState({required this.selectedAssets, required this.lockedSelectionAssets, this.forceEnable = false}); bool get isEnabled => selectedAssets.isNotEmpty; - bool get hasRemote => selectedAssets.any( - (asset) => - asset.storage == AssetState.remote || - asset.storage == AssetState.merged, - ); - bool get hasLocal => selectedAssets.any( - (asset) => asset.storage == AssetState.local, - ); + + /// Cloud only + bool get hasRemote => + selectedAssets.any((asset) => asset.storage == AssetState.remote || asset.storage == AssetState.merged); + + bool get hasLocal => selectedAssets.any((asset) => asset.storage == AssetState.local); + + bool get hasMerged => selectedAssets.any((asset) => asset.storage == AssetState.merged); MultiSelectState copyWith({ Set? selectedAssets, @@ -39,8 +34,7 @@ class MultiSelectState { }) { return MultiSelectState( selectedAssets: selectedAssets ?? this.selectedAssets, - lockedSelectionAssets: - lockedSelectionAssets ?? this.lockedSelectionAssets, + lockedSelectionAssets: lockedSelectionAssets ?? this.lockedSelectionAssets, forceEnable: forceEnable ?? this.forceEnable, ); } @@ -60,10 +54,7 @@ class MultiSelectState { } @override - int get hashCode => - selectedAssets.hashCode ^ - lockedSelectionAssets.hashCode ^ - forceEnable.hashCode; + int get hashCode => selectedAssets.hashCode ^ lockedSelectionAssets.hashCode ^ forceEnable.hashCode; } class MultiSelectNotifier extends Notifier { @@ -74,12 +65,7 @@ class MultiSelectNotifier extends Notifier { @override MultiSelectState build() { - return _defaultState ?? - const MultiSelectState( - selectedAssets: {}, - lockedSelectionAssets: {}, - forceEnable: false, - ); + return _defaultState ?? const MultiSelectState(selectedAssets: {}, lockedSelectionAssets: {}, forceEnable: false); } void selectAsset(BaseAsset asset) { @@ -87,9 +73,7 @@ class MultiSelectNotifier extends Notifier { return; } - state = state.copyWith( - selectedAssets: {...state.selectedAssets, asset}, - ); + state = state.copyWith(selectedAssets: {...state.selectedAssets, asset}); } void deselectAsset(BaseAsset asset) { @@ -97,9 +81,7 @@ class MultiSelectNotifier extends Notifier { return; } - state = state.copyWith( - selectedAssets: state.selectedAssets.where((a) => a != asset).toSet(), - ); + state = state.copyWith(selectedAssets: state.selectedAssets.where((a) => a != asset).toSet()); } void toggleAssetSelection(BaseAsset asset) { @@ -111,11 +93,7 @@ class MultiSelectNotifier extends Notifier { } void reset() { - state = const MultiSelectState( - selectedAssets: {}, - lockedSelectionAssets: {}, - forceEnable: false, - ); + state = const MultiSelectState(selectedAssets: {}, lockedSelectionAssets: {}, forceEnable: false); } /// Bucket bulk operations @@ -125,9 +103,7 @@ class MultiSelectNotifier extends Notifier { selectedAssets.addAll(assets); - state = state.copyWith( - selectedAssets: selectedAssets, - ); + state = state.copyWith(selectedAssets: selectedAssets); } void deselectBucket(int offset, int bucketCount) async { @@ -148,8 +124,7 @@ class MultiSelectNotifier extends Notifier { if (bucketAssets.isEmpty) return; // Check if all assets in this bucket are currently selected - final allSelected = - bucketAssets.every((asset) => state.selectedAssets.contains(asset)); + final allSelected = bucketAssets.every((asset) => state.selectedAssets.contains(asset)); final selectedAssets = state.selectedAssets.toSet(); @@ -165,21 +140,15 @@ class MultiSelectNotifier extends Notifier { } void setLockedSelectionAssets(Set assets) { - state = state.copyWith( - lockedSelectionAssets: assets, - ); + state = state.copyWith(lockedSelectionAssets: assets); } } -final bucketSelectionProvider = Provider.family>( - (ref, bucketAssets) { - final selectedAssets = - ref.watch(multiSelectProvider.select((s) => s.selectedAssets)); +final bucketSelectionProvider = Provider.family>((ref, bucketAssets) { + final selectedAssets = ref.watch(multiSelectProvider.select((s) => s.selectedAssets)); - if (bucketAssets.isEmpty) return false; + if (bucketAssets.isEmpty) return false; - // Check if all assets in the bucket are selected - return bucketAssets.every((asset) => selectedAssets.contains(asset)); - }, - dependencies: [multiSelectProvider, timelineServiceProvider], -); + // Check if all assets in the bucket are selected + return bucketAssets.every((asset) => selectedAssets.contains(asset)); +}, dependencies: [multiSelectProvider, timelineServiceProvider]); diff --git a/mobile/lib/providers/trash.provider.dart b/mobile/lib/providers/trash.provider.dart index c78cccff8a..adf3b1027b 100644 --- a/mobile/lib/providers/trash.provider.dart +++ b/mobile/lib/providers/trash.provider.dart @@ -7,9 +7,7 @@ class TrashNotifier extends StateNotifier { final TrashService _trashService; final _log = Logger('TrashNotifier'); - TrashNotifier( - this._trashService, - ) : super(false); + TrashNotifier(this._trashService) : super(false); Future emptyTrash() async { try { @@ -43,7 +41,5 @@ class TrashNotifier extends StateNotifier { } final trashProvider = StateNotifierProvider((ref) { - return TrashNotifier( - ref.watch(trashServiceProvider), - ); + return TrashNotifier(ref.watch(trashServiceProvider)); }); diff --git a/mobile/lib/providers/upload_profile_image.provider.dart b/mobile/lib/providers/upload_profile_image.provider.dart index fe13944ef6..e9e467346b 100644 --- a/mobile/lib/providers/upload_profile_image.provider.dart +++ b/mobile/lib/providers/upload_profile_image.provider.dart @@ -6,26 +6,15 @@ import 'package:image_picker/image_picker.dart'; import 'package:immich_mobile/domain/services/user.service.dart'; import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; -enum UploadProfileStatus { - idle, - loading, - success, - failure, -} +enum UploadProfileStatus { idle, loading, success, failure } class UploadProfileImageState { // enum final UploadProfileStatus status; final String profileImagePath; - const UploadProfileImageState({ - required this.status, - required this.profileImagePath, - }); + const UploadProfileImageState({required this.status, required this.profileImagePath}); - UploadProfileImageState copyWith({ - UploadProfileStatus? status, - String? profileImagePath, - }) { + UploadProfileImageState copyWith({UploadProfileStatus? status, String? profileImagePath}) { return UploadProfileImageState( status: status ?? this.status, profileImagePath: profileImagePath ?? this.profileImagePath, @@ -50,52 +39,36 @@ class UploadProfileImageState { String toJson() => json.encode(toMap()); - factory UploadProfileImageState.fromJson(String source) => - UploadProfileImageState.fromMap(json.decode(source)); + factory UploadProfileImageState.fromJson(String source) => UploadProfileImageState.fromMap(json.decode(source)); @override - String toString() => - 'UploadProfileImageState(status: $status, profileImagePath: $profileImagePath)'; + String toString() => 'UploadProfileImageState(status: $status, profileImagePath: $profileImagePath)'; @override bool operator ==(Object other) { if (identical(this, other)) return true; - return other is UploadProfileImageState && - other.status == status && - other.profileImagePath == profileImagePath; + return other is UploadProfileImageState && other.status == status && other.profileImagePath == profileImagePath; } @override int get hashCode => status.hashCode ^ profileImagePath.hashCode; } -class UploadProfileImageNotifier - extends StateNotifier { +class UploadProfileImageNotifier extends StateNotifier { UploadProfileImageNotifier(this._userService) - : super( - const UploadProfileImageState( - profileImagePath: '', - status: UploadProfileStatus.idle, - ), - ); + : super(const UploadProfileImageState(profileImagePath: '', status: UploadProfileStatus.idle)); final UserService _userService; Future upload(XFile file) async { state = state.copyWith(status: UploadProfileStatus.loading); - var profileImagePath = await _userService.createProfileImage( - file.name, - await file.readAsBytes(), - ); + var profileImagePath = await _userService.createProfileImage(file.name, await file.readAsBytes()); if (profileImagePath != null) { debugPrint("Successfully upload profile image"); - state = state.copyWith( - status: UploadProfileStatus.success, - profileImagePath: profileImagePath, - ); + state = state.copyWith(status: UploadProfileStatus.success, profileImagePath: profileImagePath); return true; } @@ -104,7 +77,6 @@ class UploadProfileImageNotifier } } -final uploadProfileImageProvider = - StateNotifierProvider( +final uploadProfileImageProvider = StateNotifierProvider( ((ref) => UploadProfileImageNotifier(ref.watch(userServiceProvider))), ); diff --git a/mobile/lib/providers/user.provider.dart b/mobile/lib/providers/user.provider.dart index 1a1c21554c..10dcb2aff5 100644 --- a/mobile/lib/providers/user.provider.dart +++ b/mobile/lib/providers/user.provider.dart @@ -10,8 +10,7 @@ import 'package:immich_mobile/services/timeline.service.dart'; class CurrentUserProvider extends StateNotifier { CurrentUserProvider(this._userService) : super(null) { state = _userService.tryGetMyUser(); - streamSub = - _userService.watchMyUser().listen((user) => state = user ?? state); + streamSub = _userService.watchMyUser().listen((user) => state = user ?? state); } final UserService _userService; @@ -30,8 +29,7 @@ class CurrentUserProvider extends StateNotifier { } } -final currentUserProvider = - StateNotifierProvider((ref) { +final currentUserProvider = StateNotifierProvider((ref) { return CurrentUserProvider(ref.watch(userServiceProvider)); }); @@ -56,7 +54,6 @@ class TimelineUserIdsProvider extends StateNotifier> { } } -final timelineUsersIdsProvider = - StateNotifierProvider>((ref) { +final timelineUsersIdsProvider = StateNotifierProvider>((ref) { return TimelineUserIdsProvider(ref.watch(timelineServiceProvider)); }); diff --git a/mobile/lib/providers/websocket.provider.dart b/mobile/lib/providers/websocket.provider.dart index d9db831776..fdc21592b5 100644 --- a/mobile/lib/providers/websocket.provider.dart +++ b/mobile/lib/providers/websocket.provider.dart @@ -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/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/db.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/services/api.service.dart'; @@ -21,23 +22,14 @@ import 'package:logging/logging.dart'; import 'package:openapi/api.dart'; import 'package:socket_io_client/socket_io_client.dart'; -enum PendingAction { - assetDelete, - assetUploaded, - assetHidden, - assetTrash, -} +enum PendingAction { assetDelete, assetUploaded, assetHidden, assetTrash } class PendingChange { final String id; final PendingAction action; final dynamic value; - const PendingChange( - this.id, - this.action, - this.value, - ); + const PendingChange(this.id, this.action, this.value); @override String toString() => 'PendingChange(id: $id, action: $action, value: $value)'; @@ -58,17 +50,9 @@ class WebsocketState { final bool isConnected; final List pendingChanges; - const WebsocketState({ - this.socket, - required this.isConnected, - required this.pendingChanges, - }); + const WebsocketState({this.socket, required this.isConnected, required this.pendingChanges}); - WebsocketState copyWith({ - Socket? socket, - bool? isConnected, - List? pendingChanges, - }) { + WebsocketState copyWith({Socket? socket, bool? isConnected, List? pendingChanges}) { return WebsocketState( socket: socket ?? this.socket, isConnected: isConnected ?? this.isConnected, @@ -77,16 +61,13 @@ class WebsocketState { } @override - String toString() => - 'WebsocketState(socket: $socket, isConnected: $isConnected)'; + String toString() => 'WebsocketState(socket: $socket, isConnected: $isConnected)'; @override bool operator ==(Object other) { if (identical(this, other)) return true; - return other is WebsocketState && - other.socket == socket && - other.isConnected == isConnected; + return other is WebsocketState && other.socket == socket && other.isConnected == isConnected; } @override @@ -94,19 +75,11 @@ class WebsocketState { } class WebsocketNotifier extends StateNotifier { - WebsocketNotifier(this._ref) - : super( - const WebsocketState( - socket: null, - isConnected: false, - pendingChanges: [], - ), - ); + WebsocketNotifier(this._ref) : super(const WebsocketState(socket: null, isConnected: false, pendingChanges: [])); final _log = Logger('WebsocketNotifier'); final Ref _ref; - final Debouncer _debounce = - Debouncer(interval: const Duration(milliseconds: 500)); + final Debouncer _debounce = Debouncer(interval: const Duration(milliseconds: 500)); final Debouncer _batchDebouncer = Debouncer( interval: const Duration(seconds: 5), @@ -130,8 +103,7 @@ class WebsocketNotifier extends StateNotifier { final endpoint = Uri.parse(Store.get(StoreKey.serverEndpoint)); final headers = ApiService.getRequestHeaders(); if (endpoint.userInfo.isNotEmpty) { - headers["Authorization"] = - "Basic ${base64.encode(utf8.encode(endpoint.userInfo))}"; + headers["Authorization"] = "Basic ${base64.encode(utf8.encode(endpoint.userInfo))}"; } debugPrint("Attempting to connect to websocket"); @@ -151,41 +123,33 @@ class WebsocketNotifier extends StateNotifier { socket.onConnect((_) { debugPrint("Established Websocket Connection"); - state = WebsocketState( - isConnected: true, - socket: socket, - pendingChanges: state.pendingChanges, - ); + state = WebsocketState(isConnected: true, socket: socket, pendingChanges: state.pendingChanges); }); socket.onDisconnect((_) { debugPrint("Disconnect to Websocket Connection"); - state = WebsocketState( - isConnected: false, - socket: null, - pendingChanges: state.pendingChanges, - ); + state = WebsocketState(isConnected: false, socket: null, pendingChanges: state.pendingChanges); }); socket.on('error', (errorMessage) { _log.severe("Websocket Error - $errorMessage"); - state = WebsocketState( - isConnected: false, - socket: null, - pendingChanges: state.pendingChanges, - ); + state = WebsocketState(isConnected: false, socket: null, pendingChanges: state.pendingChanges); }); - socket.on('on_upload_success', _handleOnUploadSuccess); + if (!Store.isBetaTimelineEnabled) { + socket.on('on_upload_success', _handleOnUploadSuccess); + socket.on('on_asset_delete', _handleOnAssetDelete); + socket.on('on_asset_trash', _handleOnAssetTrash); + socket.on('on_asset_restore', _handleServerUpdates); + socket.on('on_asset_update', _handleServerUpdates); + socket.on('on_asset_stack_update', _handleServerUpdates); + socket.on('on_asset_hidden', _handleOnAssetHidden); + } else { + socket.on('AssetUploadReadyV1', _handleSyncAssetUploadReady); + } + socket.on('on_config_update', _handleOnConfigUpdate); - socket.on('on_asset_delete', _handleOnAssetDelete); - socket.on('on_asset_trash', _handleOnAssetTrash); - socket.on('on_asset_restore', _handleServerUpdates); - socket.on('on_asset_update', _handleServerUpdates); - socket.on('on_asset_stack_update', _handleServerUpdates); - socket.on('on_asset_hidden', _handleOnAssetHidden); socket.on('on_new_release', _handleReleaseUpdates); - socket.on('AssetUploadReadyV1', _handleSyncAssetUploadReady); } catch (e) { debugPrint("[WEBSOCKET] Catch Websocket Error - ${e.toString()}"); } @@ -200,19 +164,42 @@ class WebsocketNotifier extends StateNotifier { var socket = state.socket?.disconnect(); if (socket?.disconnected == true) { - state = WebsocketState( - isConnected: false, - socket: null, - pendingChanges: state.pendingChanges, - ); + state = WebsocketState(isConnected: false, socket: null, pendingChanges: state.pendingChanges); } } void stopListenToEvent(String eventName) { - debugPrint("Stop listening to event $eventName"); state.socket?.off(eventName); } + void stopListenToOldEvents() { + state.socket?.off('on_upload_success'); + state.socket?.off('on_asset_delete'); + state.socket?.off('on_asset_trash'); + state.socket?.off('on_asset_restore'); + state.socket?.off('on_asset_update'); + state.socket?.off('on_asset_stack_update'); + state.socket?.off('on_asset_hidden'); + } + + void startListeningToOldEvents() { + state.socket?.on('on_upload_success', _handleOnUploadSuccess); + state.socket?.on('on_asset_delete', _handleOnAssetDelete); + state.socket?.on('on_asset_trash', _handleOnAssetTrash); + state.socket?.on('on_asset_restore', _handleServerUpdates); + state.socket?.on('on_asset_update', _handleServerUpdates); + state.socket?.on('on_asset_stack_update', _handleServerUpdates); + state.socket?.on('on_asset_hidden', _handleOnAssetHidden); + } + + void stopListeningToBetaEvents() { + state.socket?.off('AssetUploadReadyV1'); + } + + void startListeningToBetaEvents() { + state.socket?.on('AssetUploadReadyV1', _handleSyncAssetUploadReady); + } + void listenUploadEvent() { debugPrint("Start listening to event on_upload_success"); state.socket?.on('on_upload_success', _handleOnUploadSuccess); @@ -221,58 +208,36 @@ class WebsocketNotifier extends StateNotifier { void addPendingChange(PendingAction action, dynamic value) { final now = DateTime.now(); state = state.copyWith( - pendingChanges: [ - ...state.pendingChanges, - PendingChange(now.millisecondsSinceEpoch.toString(), action, value), - ], + pendingChanges: [...state.pendingChanges, PendingChange(now.millisecondsSinceEpoch.toString(), action, value)], ); _debounce.run(handlePendingChanges); } Future _handlePendingTrashes() async { - final trashChanges = state.pendingChanges - .where((c) => c.action == PendingAction.assetTrash) - .toList(); + final trashChanges = state.pendingChanges.where((c) => c.action == PendingAction.assetTrash).toList(); if (trashChanges.isNotEmpty) { - List remoteIds = trashChanges - .expand((a) => (a.value as List).map((e) => e.toString())) - .toList(); + List remoteIds = trashChanges.expand((a) => (a.value as List).map((e) => e.toString())).toList(); await _ref.read(syncServiceProvider).handleRemoteAssetRemoval(remoteIds); await _ref.read(assetProvider.notifier).getAllAsset(); - state = state.copyWith( - pendingChanges: state.pendingChanges - .whereNot((c) => trashChanges.contains(c)) - .toList(), - ); + state = state.copyWith(pendingChanges: state.pendingChanges.whereNot((c) => trashChanges.contains(c)).toList()); } } Future _handlePendingDeletes() async { - final deleteChanges = state.pendingChanges - .where((c) => c.action == PendingAction.assetDelete) - .toList(); + final deleteChanges = state.pendingChanges.where((c) => c.action == PendingAction.assetDelete).toList(); if (deleteChanges.isNotEmpty) { - List remoteIds = - deleteChanges.map((a) => a.value.toString()).toList(); + List remoteIds = deleteChanges.map((a) => a.value.toString()).toList(); await _ref.read(syncServiceProvider).handleRemoteAssetRemoval(remoteIds); - state = state.copyWith( - pendingChanges: state.pendingChanges - .whereNot((c) => deleteChanges.contains(c)) - .toList(), - ); + state = state.copyWith(pendingChanges: state.pendingChanges.whereNot((c) => deleteChanges.contains(c)).toList()); } } Future _handlePendingUploaded() async { - final uploadedChanges = state.pendingChanges - .where((c) => c.action == PendingAction.assetUploaded) - .toList(); + final uploadedChanges = state.pendingChanges.where((c) => c.action == PendingAction.assetUploaded).toList(); if (uploadedChanges.isNotEmpty) { - List remoteAssets = uploadedChanges - .map((a) => AssetResponseDto.fromJson(a.value)) - .toList(); + List remoteAssets = uploadedChanges.map((a) => AssetResponseDto.fromJson(a.value)).toList(); for (final dto in remoteAssets) { if (dto != null) { final newAsset = Asset.remote(dto); @@ -280,28 +245,19 @@ class WebsocketNotifier extends StateNotifier { } } state = state.copyWith( - pendingChanges: state.pendingChanges - .whereNot((c) => uploadedChanges.contains(c)) - .toList(), + pendingChanges: state.pendingChanges.whereNot((c) => uploadedChanges.contains(c)).toList(), ); } } Future _handlingPendingHidden() async { - final hiddenChanges = state.pendingChanges - .where((c) => c.action == PendingAction.assetHidden) - .toList(); + final hiddenChanges = state.pendingChanges.where((c) => c.action == PendingAction.assetHidden).toList(); if (hiddenChanges.isNotEmpty) { - List remoteIds = - hiddenChanges.map((a) => a.value.toString()).toList(); + List remoteIds = hiddenChanges.map((a) => a.value.toString()).toList(); final db = _ref.watch(dbProvider); await db.writeTxn(() => db.assets.deleteAllByRemoteId(remoteIds)); - state = state.copyWith( - pendingChanges: state.pendingChanges - .whereNot((c) => hiddenChanges.contains(c)) - .toList(), - ); + state = state.copyWith(pendingChanges: state.pendingChanges.whereNot((c) => hiddenChanges.contains(c)).toList()); } } @@ -322,18 +278,15 @@ class WebsocketNotifier extends StateNotifier { _ref.read(assetProvider.notifier).getAllAsset(); } - void _handleOnUploadSuccess(dynamic data) => - addPendingChange(PendingAction.assetUploaded, data); + void _handleOnUploadSuccess(dynamic data) => addPendingChange(PendingAction.assetUploaded, data); - void _handleOnAssetDelete(dynamic data) => - addPendingChange(PendingAction.assetDelete, data); + void _handleOnAssetDelete(dynamic data) => addPendingChange(PendingAction.assetDelete, data); void _handleOnAssetTrash(dynamic data) { addPendingChange(PendingAction.assetTrash, data); } - void _handleOnAssetHidden(dynamic data) => - addPendingChange(PendingAction.assetHidden, data); + void _handleOnAssetHidden(dynamic data) => addPendingChange(PendingAction.assetHidden, data); _handleReleaseUpdates(dynamic data) { // Json guard @@ -342,27 +295,21 @@ class WebsocketNotifier extends StateNotifier { } final json = data.cast(); - final serverVersionJson = - json.containsKey('serverVersion') ? json['serverVersion'] : null; - final releaseVersionJson = - json.containsKey('releaseVersion') ? json['releaseVersion'] : null; + final serverVersionJson = json.containsKey('serverVersion') ? json['serverVersion'] : null; + final releaseVersionJson = json.containsKey('releaseVersion') ? json['releaseVersion'] : null; if (serverVersionJson == null || releaseVersionJson == null) { return; } - final serverVersionDto = - ServerVersionResponseDto.fromJson(serverVersionJson); - final releaseVersionDto = - ServerVersionResponseDto.fromJson(releaseVersionJson); + final serverVersionDto = ServerVersionResponseDto.fromJson(serverVersionJson); + final releaseVersionDto = ServerVersionResponseDto.fromJson(releaseVersionJson); if (serverVersionDto == null || releaseVersionDto == null) { return; } final serverVersion = ServerVersion.fromDto(serverVersionDto); final releaseVersion = ServerVersion.fromDto(releaseVersionDto); - _ref - .read(serverInfoProvider.notifier) - .handleNewRelease(serverVersion, releaseVersion); + _ref.read(serverInfoProvider.notifier).handleNewRelease(serverVersion, releaseVersion); } void _handleSyncAssetUploadReady(dynamic data) { @@ -376,11 +323,7 @@ class WebsocketNotifier extends StateNotifier { } try { - unawaited( - _ref - .read(backgroundSyncProvider) - .syncWebsocketBatch(_batchedAssetUploadReady.toList()), - ); + unawaited(_ref.read(backgroundSyncProvider).syncWebsocketBatch(_batchedAssetUploadReady.toList())); } catch (error) { _log.severe("Error processing batched AssetUploadReadyV1 events: $error"); } @@ -389,7 +332,6 @@ class WebsocketNotifier extends StateNotifier { } } -final websocketProvider = - StateNotifierProvider((ref) { +final websocketProvider = StateNotifierProvider((ref) { return WebsocketNotifier(ref); }); diff --git a/mobile/lib/repositories/activity_api.repository.dart b/mobile/lib/repositories/activity_api.repository.dart index 1ee92b2e2f..e8f9abc8c8 100644 --- a/mobile/lib/repositories/activity_api.repository.dart +++ b/mobile/lib/repositories/activity_api.repository.dart @@ -15,22 +15,14 @@ class ActivityApiRepository extends ApiRepository { ActivityApiRepository(this._api); Future> getAll(String albumId, {String? assetId}) async { - final response = - await checkNull(_api.getActivities(albumId, assetId: assetId)); + final response = await checkNull(_api.getActivities(albumId, assetId: assetId)); return response.map(_toActivity).toList(); } - Future create( - String albumId, - ActivityType type, { - String? assetId, - String? comment, - }) async { + Future create(String albumId, ActivityType type, {String? assetId, String? comment}) async { final dto = ActivityCreateDto( albumId: albumId, - type: type == ActivityType.comment - ? ReactionType.comment - : ReactionType.like, + type: type == ActivityType.comment ? ReactionType.comment : ReactionType.like, assetId: assetId, comment: comment, ); @@ -43,19 +35,16 @@ class ActivityApiRepository extends ApiRepository { } Future getStats(String albumId, {String? assetId}) async { - final response = - await checkNull(_api.getActivityStatistics(albumId, assetId: assetId)); + final response = await checkNull(_api.getActivityStatistics(albumId, assetId: assetId)); return ActivityStats(comments: response.comments); } static Activity _toActivity(ActivityResponseDto dto) => Activity( - id: dto.id, - createdAt: dto.createdAt, - type: dto.type == ReactionType.comment - ? ActivityType.comment - : ActivityType.like, - user: UserConverter.fromSimpleUserDto(dto.user), - assetId: dto.assetId, - comment: dto.comment, - ); + id: dto.id, + createdAt: dto.createdAt, + type: dto.type == ReactionType.comment ? ActivityType.comment : ActivityType.like, + user: UserConverter.fromSimpleUserDto(dto.user), + assetId: dto.assetId, + comment: dto.comment, + ); } diff --git a/mobile/lib/repositories/album.repository.dart b/mobile/lib/repositories/album.repository.dart index 1d0349bb2a..2d24004944 100644 --- a/mobile/lib/repositories/album.repository.dart +++ b/mobile/lib/repositories/album.repository.dart @@ -4,8 +4,7 @@ import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; -import 'package:immich_mobile/infrastructure/entities/user.entity.dart' - as entity; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart' as entity; import 'package:immich_mobile/models/albums/album_search.model.dart'; import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/repositories/database.repository.dart'; @@ -14,8 +13,7 @@ import 'package:isar/isar.dart'; enum AlbumSort { remoteId, localId } -final albumRepositoryProvider = - Provider((ref) => AlbumRepository(ref.watch(dbProvider))); +final albumRepositoryProvider = Provider((ref) => AlbumRepository(ref.watch(dbProvider))); class AlbumRepository extends DatabaseRepository { const AlbumRepository(super.db); @@ -32,12 +30,7 @@ class AlbumRepository extends DatabaseRepository { Future create(Album album) => txn(() => db.albums.store(album)); - Future getByName( - String name, { - bool? shared, - bool? remote, - bool? owner, - }) { + Future getByName(String name, {bool? shared, bool? remote, bool? owner}) { var query = db.albums.filter().nameEqualTo(name); if (shared != null) { query = query.sharedEqualTo(shared); @@ -60,12 +53,7 @@ class AlbumRepository extends DatabaseRepository { Future delete(int albumId) => txn(() => db.albums.delete(albumId)); - Future> getAll({ - bool? shared, - bool? remote, - int? ownerId, - AlbumSort? sortBy, - }) { + Future> getAll({bool? shared, bool? remote, int? ownerId, AlbumSort? sortBy}) { final baseQuery = db.albums.where(); final QueryBuilder afterWhere; if (remote == null) { @@ -75,8 +63,7 @@ class AlbumRepository extends DatabaseRepository { } else { afterWhere = baseQuery.localIdIsNotNull(); } - QueryBuilder filterQuery = - afterWhere.filter().noOp(); + QueryBuilder filterQuery = afterWhere.filter().noOp(); if (shared != null) { filterQuery = filterQuery.sharedEqualTo(true); } @@ -97,38 +84,27 @@ class AlbumRepository extends DatabaseRepository { return db.albums.filter().remoteIdEqualTo(remoteId).findFirst(); } - Future removeUsers(Album album, List users) => txn( - () => album.sharedUsers.update(unlink: users.map(entity.User.fromDto)), - ); + Future removeUsers(Album album, List users) => + txn(() => album.sharedUsers.update(unlink: users.map(entity.User.fromDto))); - Future addAssets(Album album, List assets) => - txn(() => album.assets.update(link: assets)); + Future addAssets(Album album, List assets) => txn(() => album.assets.update(link: assets)); - Future removeAssets(Album album, List assets) => - txn(() => album.assets.update(unlink: assets)); + Future removeAssets(Album album, List assets) => txn(() => album.assets.update(unlink: assets)); Future recalculateMetadata(Album album) async { album.startDate = await album.assets.filter().fileCreatedAtProperty().min(); album.endDate = await album.assets.filter().fileCreatedAtProperty().max(); - album.lastModifiedAssetTimestamp = - await album.assets.filter().updatedAtProperty().max(); + album.lastModifiedAssetTimestamp = await album.assets.filter().updatedAtProperty().max(); return album; } Future addUsers(Album album, List users) => txn(() => album.sharedUsers.update(link: users.map(entity.User.fromDto))); - Future deleteAllLocal() => - txn(() => db.albums.where().localIdIsNotNull().deleteAll()); + Future deleteAllLocal() => txn(() => db.albums.where().localIdIsNotNull().deleteAll()); - Future> search( - String searchTerm, - QuickFilterMode filterMode, - ) async { - var query = db.albums - .filter() - .nameContains(searchTerm, caseSensitive: false) - .remoteIdIsNotNull(); + Future> search(String searchTerm, QuickFilterMode filterMode) async { + var query = db.albums.filter().nameContains(searchTerm, caseSensitive: false).remoteIdIsNotNull(); final isarUserId = fastHash(Store.get(StoreKey.currentUser).id); switch (filterMode) { diff --git a/mobile/lib/repositories/album_api.repository.dart b/mobile/lib/repositories/album_api.repository.dart index 20365534c2..11fc1537c5 100644 --- a/mobile/lib/repositories/album_api.repository.dart +++ b/mobile/lib/repositories/album_api.repository.dart @@ -1,19 +1,15 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/enums.dart'; -import 'package:immich_mobile/domain/models/album/album.model.dart' - show AlbumAssetOrder, RemoteAlbum; +import 'package:immich_mobile/domain/models/album/album.model.dart' show AlbumAssetOrder, RemoteAlbum; import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; -import 'package:immich_mobile/infrastructure/entities/user.entity.dart' - as entity; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart' as entity; import 'package:immich_mobile/infrastructure/utils/user.converter.dart'; import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/repositories/api.repository.dart'; import 'package:openapi/api.dart'; -final albumApiRepositoryProvider = Provider( - (ref) => AlbumApiRepository(ref.watch(apiServiceProvider).albumsApi), -); +final albumApiRepositoryProvider = Provider((ref) => AlbumApiRepository(ref.watch(apiServiceProvider).albumsApi)); class AlbumApiRepository extends ApiRepository { final AlbumsApi _api; @@ -36,9 +32,7 @@ class AlbumApiRepository extends ApiRepository { Iterable sharedUserIds = const [], String? description, }) async { - final users = sharedUserIds.map( - (id) => AlbumUserCreateDto(userId: id, role: AlbumUserRole.editor), - ); + final users = sharedUserIds.map((id) => AlbumUserCreateDto(userId: id, role: AlbumUserRole.editor)); final responseDto = await checkNull( _api.createAlbum( CreateAlbumDto( @@ -53,19 +47,9 @@ class AlbumApiRepository extends ApiRepository { } // TODO: Change name after removing old method - Future createDriftAlbum( - String name, { - required Iterable assetIds, - String? description, - }) async { + Future createDriftAlbum(String name, {required Iterable assetIds, String? description}) async { final responseDto = await checkNull( - _api.createAlbum( - CreateAlbumDto( - albumName: name, - description: description, - assetIds: assetIds.toList(), - ), - ), + _api.createAlbum(CreateAlbumDto(albumName: name, description: description, assetIds: assetIds.toList())), ); return _toRemoteAlbum(responseDto); @@ -104,16 +88,8 @@ class AlbumApiRepository extends ApiRepository { return _api.deleteAlbum(albumId); } - Future<({List added, List duplicates})> addAssets( - String albumId, - Iterable assetIds, - ) async { - final response = await checkNull( - _api.addAssetsToAlbum( - albumId, - BulkIdsDto(ids: assetIds.toList()), - ), - ); + Future<({List added, List duplicates})> addAssets(String albumId, Iterable assetIds) async { + final response = await checkNull(_api.addAssetsToAlbum(albumId, BulkIdsDto(ids: assetIds.toList()))); final List added = []; final List duplicates = []; @@ -128,16 +104,8 @@ class AlbumApiRepository extends ApiRepository { return (added: added, duplicates: duplicates); } - Future<({List removed, List failed})> removeAssets( - String albumId, - Iterable assetIds, - ) async { - final response = await checkNull( - _api.removeAssetFromAlbum( - albumId, - BulkIdsDto(ids: assetIds.toList()), - ), - ); + Future<({List removed, List failed})> removeAssets(String albumId, Iterable assetIds) async { + final response = await checkNull(_api.removeAssetFromAlbum(albumId, BulkIdsDto(ids: assetIds.toList()))); final List removed = [], failed = []; for (final dto in response) { if (dto.success) { @@ -150,14 +118,8 @@ class AlbumApiRepository extends ApiRepository { } Future addUsers(String albumId, Iterable userIds) async { - final albumUsers = - userIds.map((userId) => AlbumUserAddDto(userId: userId)).toList(); - final response = await checkNull( - _api.addUsersToAlbum( - albumId, - AddUsersDto(albumUsers: albumUsers), - ), - ); + final albumUsers = userIds.map((userId) => AlbumUserAddDto(userId: userId)).toList(); + final response = await checkNull(_api.addUsersToAlbum(albumId, AddUsersDto(albumUsers: albumUsers))); return _toAlbum(response); } @@ -180,11 +142,9 @@ class AlbumApiRepository extends ApiRepository { sortOrder: dto.order == AssetOrder.asc ? SortOrder.asc : SortOrder.desc, ); album.remoteAssetCount = dto.assetCount; - album.owner.value = - entity.User.fromDto(UserConverter.fromSimpleUserDto(dto.owner)); + album.owner.value = entity.User.fromDto(UserConverter.fromSimpleUserDto(dto.owner)); album.remoteThumbnailAssetId = dto.albumThumbnailAssetId; - final users = dto.albumUsers - .map((albumUser) => UserConverter.fromSimpleUserDto(albumUser.user)); + final users = dto.albumUsers.map((albumUser) => UserConverter.fromSimpleUserDto(albumUser.user)); album.sharedUsers.addAll(users.map(entity.User.fromDto)); final assets = dto.assets.map(Asset.remote).toList(); album.assets.addAll(assets); @@ -202,9 +162,7 @@ class AlbumApiRepository extends ApiRepository { updatedAt: dto.updatedAt, thumbnailAssetId: dto.albumThumbnailAssetId, isActivityEnabled: dto.isActivityEnabled, - order: dto.order == AssetOrder.asc - ? AlbumAssetOrder.asc - : AlbumAssetOrder.desc, + order: dto.order == AssetOrder.asc ? AlbumAssetOrder.asc : AlbumAssetOrder.desc, assetCount: dto.assetCount, ownerName: dto.owner.name, ); diff --git a/mobile/lib/repositories/album_media.repository.dart b/mobile/lib/repositories/album_media.repository.dart index 94f1d0581e..89860f4e75 100644 --- a/mobile/lib/repositories/album_media.repository.dart +++ b/mobile/lib/repositories/album_media.repository.dart @@ -7,60 +7,52 @@ import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:immich_mobile/repositories/asset_media.repository.dart'; import 'package:photo_manager/photo_manager.dart' hide AssetType; -final albumMediaRepositoryProvider = - Provider((ref) => const AlbumMediaRepository()); +final albumMediaRepositoryProvider = Provider((ref) => const AlbumMediaRepository()); class AlbumMediaRepository { const AlbumMediaRepository(); - bool get useCustomFilter => - Store.get(StoreKey.photoManagerCustomFilter, true); + bool get useCustomFilter => Store.get(StoreKey.photoManagerCustomFilter, true); FilterOptionGroup? _getAlbumFilter({ DateTimeCond? updateTimeCond, bool? containsPathModified, List? orderBy, - }) => - useCustomFilter - ? FilterOptionGroup( - imageOption: const FilterOption( - needTitle: true, - sizeConstraint: SizeConstraint(ignoreSize: true), - ), - videoOption: const FilterOption( - needTitle: true, - sizeConstraint: SizeConstraint(ignoreSize: true), - durationConstraint: DurationConstraint(allowNullable: true), - ), - containsPathModified: containsPathModified ?? false, - createTimeCond: DateTimeCond.def().copyWith(ignore: true), - updateTimeCond: - updateTimeCond ?? DateTimeCond.def().copyWith(ignore: true), - orders: orderBy ?? [], - ) - : null; + }) => useCustomFilter + ? FilterOptionGroup( + imageOption: const FilterOption(needTitle: true, sizeConstraint: SizeConstraint(ignoreSize: true)), + videoOption: const FilterOption( + needTitle: true, + sizeConstraint: SizeConstraint(ignoreSize: true), + durationConstraint: DurationConstraint(allowNullable: true), + ), + containsPathModified: containsPathModified ?? false, + createTimeCond: DateTimeCond.def().copyWith(ignore: true), + updateTimeCond: updateTimeCond ?? DateTimeCond.def().copyWith(ignore: true), + orders: orderBy ?? [], + ) + : null; Future> getAll() async { final filter = useCustomFilter ? CustomFilter.sql(where: '${CustomColumns.base.width} > 0') : FilterOptionGroup(containsPathModified: true); - final List assetPathEntities = - await PhotoManager.getAssetPathList(hasAll: true, filterOption: filter); + final List assetPathEntities = await PhotoManager.getAssetPathList( + hasAll: true, + filterOption: filter, + ); return assetPathEntities.map(_toAlbum).toList(); } Future> getAssetIds(String albumId) async { - final album = - await AssetPathEntity.fromId(albumId, filterOption: _getAlbumFilter()); - final List assets = - await album.getAssetListRange(start: 0, end: 0x7fffffffffffffff); + final album = await AssetPathEntity.fromId(albumId, filterOption: _getAlbumFilter()); + final List assets = await album.getAssetListRange(start: 0, end: 0x7fffffffffffffff); return assets.map((e) => e.id).toList(); } Future getAssetCount(String albumId) async { - final album = - await AssetPathEntity.fromId(albumId, filterOption: _getAlbumFilter()); + final album = await AssetPathEntity.fromId(albumId, filterOption: _getAlbumFilter()); return album.assetCountAsync; } @@ -77,36 +69,25 @@ class AlbumMediaRepository { filterOption: _getAlbumFilter( updateTimeCond: modifiedFrom == null && modifiedUntil == null ? null - : DateTimeCond( - min: modifiedFrom ?? DateTime.utc(-271820), - max: modifiedUntil ?? DateTime.utc(275760), - ), - orderBy: orderByModificationDate - ? [const OrderOption(type: OrderOptionType.updateDate)] - : [], + : DateTimeCond(min: modifiedFrom ?? DateTime.utc(-271820), max: modifiedUntil ?? DateTime.utc(275760)), + orderBy: orderByModificationDate ? [const OrderOption(type: OrderOptionType.updateDate)] : [], ), ); - final List assets = - await onDevice.getAssetListRange(start: start, end: end); + final List assets = await onDevice.getAssetListRange(start: start, end: end); return assets.map(AssetMediaRepository.toAsset).toList().cast(); } Future get(String id) async { - final assetPathEntity = await AssetPathEntity.fromId( - id, - filterOption: _getAlbumFilter(containsPathModified: true), - ); + final assetPathEntity = await AssetPathEntity.fromId(id, filterOption: _getAlbumFilter(containsPathModified: true)); return _toAlbum(assetPathEntity); } static Album _toAlbum(AssetPathEntity assetPathEntity) { final Album album = Album( name: assetPathEntity.name, - createdAt: - assetPathEntity.lastModified?.toUtc() ?? DateTime.now().toUtc(), - modifiedAt: - assetPathEntity.lastModified?.toUtc() ?? DateTime.now().toUtc(), + createdAt: assetPathEntity.lastModified?.toUtc() ?? DateTime.now().toUtc(), + modifiedAt: assetPathEntity.lastModified?.toUtc() ?? DateTime.now().toUtc(), shared: false, activityEnabled: false, ); diff --git a/mobile/lib/repositories/asset.repository.dart b/mobile/lib/repositories/asset.repository.dart index 1e3defae08..79af8b4921 100644 --- a/mobile/lib/repositories/asset.repository.dart +++ b/mobile/lib/repositories/asset.repository.dart @@ -11,8 +11,7 @@ import 'package:isar/isar.dart'; enum AssetSort { checksum, ownerIdChecksum } -final assetRepositoryProvider = - Provider((ref) => AssetRepository(ref.watch(dbProvider))); +final assetRepositoryProvider = Provider((ref) => AssetRepository(ref.watch(dbProvider))); class AssetRepository extends DatabaseRepository { const AssetRepository(super.db); @@ -29,8 +28,7 @@ class AssetRepository extends DatabaseRepository { if (notOwnedBy.length == 1) { query = query.not().ownerIdEqualTo(isarUserIds.first); } else if (notOwnedBy.isNotEmpty) { - query = - query.not().anyOf(isarUserIds, (q, int id) => q.ownerIdEqualTo(id)); + query = query.not().anyOf(isarUserIds, (q, int id) => q.ownerIdEqualTo(id)); } if (ownerId != null) { query = query.ownerIdEqualTo(fastHash(ownerId)); @@ -44,8 +42,7 @@ class AssetRepository extends DatabaseRepository { }; } - final QueryBuilder sortedQuery = - switch (sortBy) { + final QueryBuilder sortedQuery = switch (sortBy) { null => query.noOp(), AssetSort.checksum => query.sortByChecksum(), AssetSort.ownerIdChecksum => query.sortByOwnerId().thenByChecksum(), @@ -55,16 +52,13 @@ class AssetRepository extends DatabaseRepository { } Future deleteByIds(List ids) => txn(() async { - await db.assets.deleteAll(ids); - await db.exifInfos.deleteAll(ids); - }); + await db.assets.deleteAll(ids); + await db.exifInfos.deleteAll(ids); + }); Future getByRemoteId(String id) => db.assets.getByRemoteId(id); - Future> getAllByRemoteId( - Iterable ids, { - AssetState? state, - }) async { + Future> getAllByRemoteId(Iterable ids, {AssetState? state}) async { if (ids.isEmpty) { return []; } @@ -72,10 +66,7 @@ class AssetRepository extends DatabaseRepository { return _getAllByRemoteIdImpl(ids, state).findAll(); } - QueryBuilder _getAllByRemoteIdImpl( - Iterable ids, - AssetState? state, - ) { + QueryBuilder _getAllByRemoteIdImpl(Iterable ids, AssetState? state) { final query = db.assets.remote(ids).filter(); return switch (state) { null => query.noOp(), @@ -85,39 +76,21 @@ class AssetRepository extends DatabaseRepository { }; } - Future> getAll({ - required String ownerId, - AssetState? state, - AssetSort? sortBy, - int? limit, - }) { + Future> getAll({required String ownerId, AssetState? state, AssetSort? sortBy, int? limit}) { final baseQuery = db.assets.where(); final isarUserIds = fastHash(ownerId); - final QueryBuilder filteredQuery = - switch (state) { + final QueryBuilder filteredQuery = switch (state) { null => baseQuery.ownerIdEqualToAnyChecksum(isarUserIds).noOp(), - AssetState.local => baseQuery - .remoteIdIsNull() - .filter() - .localIdIsNotNull() - .ownerIdEqualTo(isarUserIds), - AssetState.remote => baseQuery - .localIdIsNull() - .filter() - .remoteIdIsNotNull() - .ownerIdEqualTo(isarUserIds), - AssetState.merged => baseQuery - .ownerIdEqualToAnyChecksum(isarUserIds) - .filter() - .remoteIdIsNotNull() - .localIdIsNotNull(), + AssetState.local => baseQuery.remoteIdIsNull().filter().localIdIsNotNull().ownerIdEqualTo(isarUserIds), + AssetState.remote => baseQuery.localIdIsNull().filter().remoteIdIsNotNull().ownerIdEqualTo(isarUserIds), + AssetState.merged => + baseQuery.ownerIdEqualToAnyChecksum(isarUserIds).filter().remoteIdIsNotNull().localIdIsNotNull(), }; final QueryBuilder query = switch (sortBy) { null => filteredQuery.noOp(), AssetSort.checksum => filteredQuery.sortByChecksum(), - AssetSort.ownerIdChecksum => - filteredQuery.sortByOwnerId().thenByChecksum(), + AssetSort.ownerIdChecksum => filteredQuery.sortByOwnerId().thenByChecksum(), }; return limit == null ? query.findAll() : query.limit(limit).findAll(); @@ -135,15 +108,11 @@ class AssetRepository extends DatabaseRepository { int limit = 100, }) { final baseQuery = db.assets.where(); - final QueryBuilder query = - switch (state) { + final QueryBuilder query = switch (state) { null => baseQuery.noOp(), - AssetState.local => - baseQuery.remoteIdIsNull().filter().localIdIsNotNull(), - AssetState.remote => - baseQuery.localIdIsNull().filter().remoteIdIsNotNull(), - AssetState.merged => - baseQuery.localIdIsNotNull().filter().remoteIdIsNotNull(), + AssetState.local => baseQuery.remoteIdIsNull().filter().localIdIsNotNull(), + AssetState.remote => baseQuery.localIdIsNull().filter().remoteIdIsNotNull(), + AssetState.merged => baseQuery.localIdIsNotNull().filter().remoteIdIsNotNull(), }; return _getMatchesImpl(query, fastHash(ownerId), assets, limit); } @@ -153,25 +122,18 @@ class AssetRepository extends DatabaseRepository { return asset; } - Future upsertDuplicatedAssets(Iterable duplicatedAssets) => txn( - () => db.duplicatedAssets - .putAll(duplicatedAssets.map(DuplicatedAsset.new).toList()), - ); + Future upsertDuplicatedAssets(Iterable duplicatedAssets) => + txn(() => db.duplicatedAssets.putAll(duplicatedAssets.map(DuplicatedAsset.new).toList())); - Future> getAllDuplicatedAssetIds() => - db.duplicatedAssets.where().idProperty().findAll(); + Future> getAllDuplicatedAssetIds() => db.duplicatedAssets.where().idProperty().findAll(); Future getByOwnerIdChecksum(int ownerId, String checksum) => db.assets.getByOwnerIdChecksum(ownerId, checksum); - Future> getAllByOwnerIdChecksum( - List ownerIds, - List checksums, - ) => + Future> getAllByOwnerIdChecksum(List ownerIds, List checksums) => db.assets.getAllByOwnerIdChecksum(ownerIds, checksums); - Future> getAllLocal() => - db.assets.where().localIdIsNotNull().findAll(); + Future> getAllLocal() => db.assets.where().localIdIsNotNull().findAll(); Future deleteAllByRemoteId(List ids, {AssetState? state}) => txn(() => _getAllByRemoteIdImpl(ids, state).deleteAll()); @@ -234,26 +196,25 @@ Future> _getMatchesImpl( int ownerId, List assets, int limit, -) => - query - .ownerIdEqualTo(ownerId) - .anyOf( - assets, - (q, Asset a) => q - .fileNameEqualTo(a.fileName) - .and() - .durationInSecondsEqualTo(a.durationInSeconds) - .and() - .fileCreatedAtBetween( - a.fileCreatedAt.subtract(const Duration(hours: 12)), - a.fileCreatedAt.add(const Duration(hours: 12)), - ) - .and() - .not() - .checksumEqualTo(a.checksum), - ) - .sortByFileName() - .thenByFileCreatedAt() - .thenByFileModifiedAt() - .limit(limit) - .findAll(); +) => query + .ownerIdEqualTo(ownerId) + .anyOf( + assets, + (q, Asset a) => q + .fileNameEqualTo(a.fileName) + .and() + .durationInSecondsEqualTo(a.durationInSeconds) + .and() + .fileCreatedAtBetween( + a.fileCreatedAt.subtract(const Duration(hours: 12)), + a.fileCreatedAt.add(const Duration(hours: 12)), + ) + .and() + .not() + .checksumEqualTo(a.checksum), + ) + .sortByFileName() + .thenByFileCreatedAt() + .thenByFileModifiedAt() + .limit(limit) + .findAll(); diff --git a/mobile/lib/repositories/asset_api.repository.dart b/mobile/lib/repositories/asset_api.repository.dart index 4c854973b1..07639fbb3a 100644 --- a/mobile/lib/repositories/asset_api.repository.dart +++ b/mobile/lib/repositories/asset_api.repository.dart @@ -13,6 +13,7 @@ final assetApiRepositoryProvider = Provider( ref.watch(apiServiceProvider).assetsApi, ref.watch(apiServiceProvider).searchApi, ref.watch(apiServiceProvider).stacksApi, + ref.watch(apiServiceProvider).trashApi, ), ); @@ -20,13 +21,12 @@ class AssetApiRepository extends ApiRepository { final AssetsApi _api; final SearchApi _searchApi; final StacksApi _stacksApi; + final TrashApi _trashApi; - AssetApiRepository(this._api, this._searchApi, this._stacksApi); + AssetApiRepository(this._api, this._searchApi, this._stacksApi, this._trashApi); Future update(String id, {String? description}) async { - final response = await checkNull( - _api.updateAsset(id, UpdateAssetDto(description: description)), - ); + final response = await checkNull(_api.updateAsset(id, UpdateAssetDto(description: description))); return Asset.remote(response); } @@ -37,13 +37,7 @@ class AssetApiRepository extends ApiRepository { int currentPage = 1; while (hasNext) { final response = await checkNull( - _searchApi.searchAssets( - MetadataSearchDto( - personIds: personIds, - page: currentPage, - size: 1000, - ), - ), + _searchApi.searchAssets(MetadataSearchDto(personIds: personIds, page: currentPage, size: 1000)), ); result.addAll(response.assets.items.map(Asset.remote)); hasNext = response.assets.nextPage != null; @@ -56,40 +50,28 @@ class AssetApiRepository extends ApiRepository { return _api.deleteAssets(AssetBulkDeleteDto(ids: ids, force: force)); } - Future updateVisibility( - List ids, - AssetVisibilityEnum visibility, - ) async { - return _api.updateAssets( - AssetBulkUpdateDto(ids: ids, visibility: _mapVisibility(visibility)), - ); + Future restoreTrash(List ids) async { + await _trashApi.restoreAssets(BulkIdsDto(ids: ids)); } - Future updateFavorite( - List ids, - bool isFavorite, - ) async { - return _api.updateAssets( - AssetBulkUpdateDto(ids: ids, isFavorite: isFavorite), - ); + Future updateVisibility(List ids, AssetVisibilityEnum visibility) async { + return _api.updateAssets(AssetBulkUpdateDto(ids: ids, visibility: _mapVisibility(visibility))); } - Future updateLocation( - List ids, - LatLng location, - ) async { - return _api.updateAssets( - AssetBulkUpdateDto( - ids: ids, - latitude: location.latitude, - longitude: location.longitude, - ), - ); + Future updateFavorite(List ids, bool isFavorite) async { + return _api.updateAssets(AssetBulkUpdateDto(ids: ids, isFavorite: isFavorite)); + } + + Future updateLocation(List ids, LatLng location) async { + return _api.updateAssets(AssetBulkUpdateDto(ids: ids, latitude: location.latitude, longitude: location.longitude)); + } + + Future updateDateTime(List ids, DateTime dateTime) async { + return _api.updateAssets(AssetBulkUpdateDto(ids: ids, dateTimeOriginal: dateTime.toIso8601String())); } Future stack(List ids) async { - final responseDto = - await checkNull(_stacksApi.createStack(StackCreateDto(assetIds: ids))); + final responseDto = await checkNull(_stacksApi.createStack(StackCreateDto(assetIds: ids))); return responseDto.toStack(); } @@ -103,11 +85,11 @@ class AssetApiRepository extends ApiRepository { } _mapVisibility(AssetVisibilityEnum visibility) => switch (visibility) { - AssetVisibilityEnum.timeline => AssetVisibility.timeline, - AssetVisibilityEnum.hidden => AssetVisibility.hidden, - AssetVisibilityEnum.locked => AssetVisibility.locked, - AssetVisibilityEnum.archive => AssetVisibility.archive, - }; + AssetVisibilityEnum.timeline => AssetVisibility.timeline, + AssetVisibilityEnum.hidden => AssetVisibility.hidden, + AssetVisibilityEnum.locked => AssetVisibility.locked, + AssetVisibilityEnum.archive => AssetVisibility.archive, + }; Future getAssetMIMEType(String assetId) async { final response = await checkNull(_api.getAssetInfo(assetId)); @@ -115,14 +97,14 @@ class AssetApiRepository extends ApiRepository { // we need to get the MIME of the thumbnail once that gets added to the API return response.originalMimeType; } + + Future updateDescription(String assetId, String description) { + return _api.updateAsset(assetId, UpdateAssetDto(description: description)); + } } extension on StackResponseDto { StackResponse toStack() { - return StackResponse( - id: id, - primaryAssetId: primaryAssetId, - assetIds: assets.map((asset) => asset.id).toList(), - ); + return StackResponse(id: id, primaryAssetId: primaryAssetId, assetIds: assets.map((asset) => asset.id).toList()); } } diff --git a/mobile/lib/repositories/asset_media.repository.dart b/mobile/lib/repositories/asset_media.repository.dart index 8708ce9cfd..a0af217f0c 100644 --- a/mobile/lib/repositories/asset_media.repository.dart +++ b/mobile/lib/repositories/asset_media.repository.dart @@ -14,9 +14,7 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/extensions/response_extensions.dart'; import 'package:share_plus/share_plus.dart'; -final assetMediaRepositoryProvider = Provider( - (ref) => AssetMediaRepository(ref.watch(assetApiRepositoryProvider)), -); +final assetMediaRepositoryProvider = Provider((ref) => AssetMediaRepository(ref.watch(assetApiRepositoryProvider))); class AssetMediaRepository { final AssetApiRepository _assetApiRepository; @@ -24,8 +22,7 @@ class AssetMediaRepository { const AssetMediaRepository(this._assetApiRepository); - Future> deleteAll(List ids) => - PhotoManager.editor.deleteWithIds(ids); + Future> deleteAll(List ids) => PhotoManager.editor.deleteWithIds(ids); Future get(String id) async { final entity = await AssetEntity.fromId(id); @@ -52,8 +49,7 @@ class AssetMediaRepository { asset.fileCreatedAt = asset.fileModifiedAt; } if (local.latitude != null) { - asset.exifInfo = - ExifInfo(latitude: local.latitude, longitude: local.longitude); + asset.exifInfo = ExifInfo(latitude: local.latitude, longitude: local.longitude); } asset.local = local; return asset; @@ -79,12 +75,10 @@ class AssetMediaRepository { final localId = (asset is LocalAsset) ? asset.id : asset is RemoteAsset - ? asset.localId - : null; + ? asset.localId + : null; if (localId != null) { - File? f = - await AssetEntity(id: localId, width: 1, height: 1, typeInt: 0) - .originFile; + File? f = await AssetEntity(id: localId, width: 1, height: 1, typeInt: 0).originFile; downloadedXFiles.add(XFile(f!.path)); } else if (asset is RemoteAsset) { final tempDir = await getTemporaryDirectory(); @@ -119,8 +113,6 @@ class AssetMediaRepository { _log.warning("Failed to delete temporary file: ${file.path}", e); } } - return result.status == ShareResultStatus.success - ? downloadedXFiles.length - : 0; + return result.status == ShareResultStatus.success ? downloadedXFiles.length : 0; } } diff --git a/mobile/lib/repositories/auth.repository.dart b/mobile/lib/repositories/auth.repository.dart index eb86e1809f..9d7748254d 100644 --- a/mobile/lib/repositories/auth.repository.dart +++ b/mobile/lib/repositories/auth.repository.dart @@ -34,10 +34,12 @@ class AuthRepository extends DatabaseRepository { _drift.userMetadataEntity.deleteAll(), _drift.partnerEntity.deleteAll(), _drift.stackEntity.deleteAll(), + _drift.assetFaceEntity.deleteAll(), ]); // Drift deletions - parent entities await Future.wait([ _drift.memoryEntity.deleteAll(), + _drift.personEntity.deleteAll(), _drift.remoteAlbumEntity.deleteAll(), _drift.remoteAssetEntity.deleteAll(), _drift.userEntity.deleteAll(), @@ -78,8 +80,7 @@ class AuthRepository extends DatabaseRepository { } final List jsonList = jsonDecode(jsonString); - final endpointList = - jsonList.map((e) => AuxilaryEndpoint.fromJson(e)).toList(); + final endpointList = jsonList.map((e) => AuxilaryEndpoint.fromJson(e)).toList(); return endpointList; } diff --git a/mobile/lib/repositories/auth_api.repository.dart b/mobile/lib/repositories/auth_api.repository.dart index 4b68867506..e488f69578 100644 --- a/mobile/lib/repositories/auth_api.repository.dart +++ b/mobile/lib/repositories/auth_api.repository.dart @@ -5,8 +5,7 @@ import 'package:immich_mobile/repositories/api.repository.dart'; import 'package:immich_mobile/services/api.service.dart'; import 'package:openapi/api.dart'; -final authApiRepositoryProvider = - Provider((ref) => AuthApiRepository(ref.watch(apiServiceProvider))); +final authApiRepositoryProvider = Provider((ref) => AuthApiRepository(ref.watch(apiServiceProvider))); class AuthApiRepository extends ApiRepository { final ApiService _apiService; @@ -14,30 +13,19 @@ class AuthApiRepository extends ApiRepository { AuthApiRepository(this._apiService); Future changePassword(String newPassword) async { - await _apiService.usersApi.updateMyUser( - UserUpdateMeDto( - password: newPassword, - ), - ); + await _apiService.usersApi.updateMyUser(UserUpdateMeDto(password: newPassword)); } Future login(String email, String password) async { final loginResponseDto = await checkNull( - _apiService.authenticationApi.login( - LoginCredentialDto( - email: email, - password: password, - ), - ), + _apiService.authenticationApi.login(LoginCredentialDto(email: email, password: password)), ); return _mapLoginReponse(loginResponseDto); } Future logout() async { - await _apiService.authenticationApi - .logout() - .timeout(const Duration(seconds: 7)); + await _apiService.authenticationApi.logout().timeout(const Duration(seconds: 7)); } _mapLoginReponse(LoginResponseDto dto) { @@ -54,8 +42,7 @@ class AuthApiRepository extends ApiRepository { Future unlockPinCode(String pinCode) async { try { - await _apiService.authenticationApi - .unlockAuthSession(SessionUnlockDto(pinCode: pinCode)); + await _apiService.authenticationApi.unlockAuthSession(SessionUnlockDto(pinCode: pinCode)); return true; } catch (_) { return false; @@ -63,8 +50,7 @@ class AuthApiRepository extends ApiRepository { } Future setupPinCode(String pinCode) { - return _apiService.authenticationApi - .setupPinCode(PinCodeSetupDto(pinCode: pinCode)); + return _apiService.authenticationApi.setupPinCode(PinCodeSetupDto(pinCode: pinCode)); } Future lockPinCode() { diff --git a/mobile/lib/repositories/backup.repository.dart b/mobile/lib/repositories/backup.repository.dart index 05d2047add..6cee6a4427 100644 --- a/mobile/lib/repositories/backup.repository.dart +++ b/mobile/lib/repositories/backup.repository.dart @@ -6,16 +6,14 @@ import 'package:isar/isar.dart'; enum BackupAlbumSort { id } -final backupAlbumRepositoryProvider = - Provider((ref) => BackupAlbumRepository(ref.watch(dbProvider))); +final backupAlbumRepositoryProvider = Provider((ref) => BackupAlbumRepository(ref.watch(dbProvider))); class BackupAlbumRepository extends DatabaseRepository { const BackupAlbumRepository(super.db); Future> getAll({BackupAlbumSort? sort}) { final baseQuery = db.backupAlbums.where(); - final QueryBuilder query = - switch (sort) { + final QueryBuilder query = switch (sort) { null => baseQuery.noOp(), BackupAlbumSort.id => baseQuery.sortById(), }; @@ -28,9 +26,7 @@ class BackupAlbumRepository extends DatabaseRepository { Future> getAllBySelection(BackupSelection backup) => db.backupAlbums.filter().selectionEqualTo(backup).findAll(); - Future deleteAll(List ids) => - txn(() => db.backupAlbums.deleteAll(ids)); + Future deleteAll(List ids) => txn(() => db.backupAlbums.deleteAll(ids)); - Future updateAll(List backupAlbums) => - txn(() => db.backupAlbums.putAll(backupAlbums)); + Future updateAll(List backupAlbums) => txn(() => db.backupAlbums.putAll(backupAlbums)); } diff --git a/mobile/lib/repositories/biometric.repository.dart b/mobile/lib/repositories/biometric.repository.dart index 94540c8249..8bd5a85542 100644 --- a/mobile/lib/repositories/biometric.repository.dart +++ b/mobile/lib/repositories/biometric.repository.dart @@ -3,8 +3,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/models/auth/biometric_status.model.dart'; import 'package:local_auth/local_auth.dart'; -final biometricRepositoryProvider = - Provider((ref) => BiometricRepository(LocalAuthentication())); +final biometricRepositoryProvider = Provider((ref) => BiometricRepository(LocalAuthentication())); class BiometricRepository { final LocalAuthentication _localAuth; @@ -12,21 +11,14 @@ class BiometricRepository { const BiometricRepository(this._localAuth); Future getStatus() async { - final bool canAuthenticateWithBiometrics = - await _localAuth.canCheckBiometrics; - final bool canAuthenticate = - canAuthenticateWithBiometrics || await _localAuth.isDeviceSupported(); + final bool canAuthenticateWithBiometrics = await _localAuth.canCheckBiometrics; + final bool canAuthenticate = canAuthenticateWithBiometrics || await _localAuth.isDeviceSupported(); final availableBiometric = await _localAuth.getAvailableBiometrics(); - return BiometricStatus( - canAuthenticate: canAuthenticate, - availableBiometrics: availableBiometric, - ); + return BiometricStatus(canAuthenticate: canAuthenticate, availableBiometrics: availableBiometric); } Future authenticate(String? message) async { - return _localAuth.authenticate( - localizedReason: message ?? 'please_auth_to_access'.tr(), - ); + return _localAuth.authenticate(localizedReason: message ?? 'please_auth_to_access'.tr()); } } diff --git a/mobile/lib/repositories/database.repository.dart b/mobile/lib/repositories/database.repository.dart index c644dae668..71c15e1c40 100644 --- a/mobile/lib/repositories/database.repository.dart +++ b/mobile/lib/repositories/database.repository.dart @@ -11,12 +11,10 @@ abstract class DatabaseRepository implements IDatabaseRepository { bool get inTxn => Zone.current[_zoneTxn] != null; - Future txn(Future Function() callback) => - inTxn ? callback() : transaction(callback); + Future txn(Future Function() callback) => inTxn ? callback() : transaction(callback); @override - Future transaction(Future Function() callback) => - db.writeTxn(callback); + Future transaction(Future Function() callback) => db.writeTxn(callback); } extension Asd on QueryBuilder { diff --git a/mobile/lib/repositories/download.repository.dart b/mobile/lib/repositories/download.repository.dart index 72f7e065ca..c4b31e9d93 100644 --- a/mobile/lib/repositories/download.repository.dart +++ b/mobile/lib/repositories/download.repository.dart @@ -1,10 +1,28 @@ +import 'dart:convert'; +import 'dart:io'; + import 'package:background_downloader/background_downloader.dart'; +import 'package:collection/collection.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/utils/download.dart'; +import 'package:immich_mobile/constants/constants.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/models/download/livephotos_medatada.model.dart'; +import 'package:immich_mobile/services/api.service.dart'; +import 'package:immich_mobile/utils/image_url_builder.dart'; final downloadRepositoryProvider = Provider((ref) => DownloadRepository()); class DownloadRepository { + static final _downloader = FileDownloader(); + static final _dummyTask = DownloadTask( + taskId: 'dummy', + url: '', + filename: 'dummy', + group: '', + updates: Updates.statusAndProgress, + ); + static final _dummyMetadata = {'part': LivePhotosPart.image.index, 'id': ''}; + void Function(TaskStatusUpdate)? onImageDownloadStatus; void Function(TaskStatusUpdate)? onVideoDownloadStatus; @@ -14,45 +32,102 @@ class DownloadRepository { void Function(TaskProgressUpdate)? onTaskProgress; DownloadRepository() { - FileDownloader().registerCallbacks( - group: downloadGroupImage, + _downloader.registerCallbacks( + group: kDownloadGroupImage, taskStatusCallback: (update) => onImageDownloadStatus?.call(update), taskProgressCallback: (update) => onTaskProgress?.call(update), ); - FileDownloader().registerCallbacks( - group: downloadGroupVideo, + _downloader.registerCallbacks( + group: kDownloadGroupVideo, taskStatusCallback: (update) => onVideoDownloadStatus?.call(update), taskProgressCallback: (update) => onTaskProgress?.call(update), ); - FileDownloader().registerCallbacks( - group: downloadGroupLivePhoto, + _downloader.registerCallbacks( + group: kDownloadGroupLivePhoto, taskStatusCallback: (update) => onLivePhotoDownloadStatus?.call(update), taskProgressCallback: (update) => onTaskProgress?.call(update), ); } Future> downloadAll(List tasks) { - return FileDownloader().enqueueAll(tasks); + return _downloader.enqueueAll(tasks); } Future deleteAllTrackingRecords() { - return FileDownloader().database.deleteAllRecords(); + return _downloader.database.deleteAllRecords(); } Future cancel(String id) { - return FileDownloader().cancelTaskWithId(id); + return _downloader.cancelTaskWithId(id); } Future> getLiveVideoTasks() { - return FileDownloader().database.allRecordsWithStatus( - TaskStatus.complete, - group: downloadGroupLivePhoto, - ); + return _downloader.database.allRecordsWithStatus(TaskStatus.complete, group: kDownloadGroupLivePhoto); } Future deleteRecordsWithIds(List ids) { - return FileDownloader().database.deleteRecordsWithIds(ids); + return _downloader.database.deleteRecordsWithIds(ids); + } + + Future> downloadAllAssets(List assets) async { + if (assets.isEmpty) { + return Future.value(const []); + } + + final length = Platform.isAndroid ? assets.length : assets.length * 2; + final tasks = List.filled(length, _dummyTask); + int taskIndex = 0; + final headers = ApiService.getRequestHeaders(); + for (final asset in assets) { + if (!asset.isRemoteOnly) { + continue; + } + + final id = asset.id; + final livePhotoVideoId = asset.livePhotoVideoId; + final isVideo = asset.isVideo; + final url = getOriginalUrlForRemoteId(id); + + if (Platform.isAndroid || livePhotoVideoId == null || isVideo) { + tasks[taskIndex++] = DownloadTask( + taskId: id, + url: url, + headers: headers, + filename: asset.name, + updates: Updates.statusAndProgress, + group: isVideo ? kDownloadGroupVideo : kDownloadGroupImage, + ); + continue; + } + + _dummyMetadata['part'] = LivePhotosPart.image.index; + _dummyMetadata['id'] = id; + tasks[taskIndex++] = DownloadTask( + taskId: id, + url: url, + headers: headers, + filename: asset.name, + updates: Updates.statusAndProgress, + group: kDownloadGroupLivePhoto, + metaData: json.encode(_dummyMetadata), + ); + + _dummyMetadata['part'] = LivePhotosPart.video.index; + tasks[taskIndex++] = DownloadTask( + taskId: livePhotoVideoId, + url: url, + headers: headers, + filename: asset.name.toUpperCase().replaceAll(RegExp(r"\.(JPG|HEIC)$"), '.MOV'), + updates: Updates.statusAndProgress, + group: kDownloadGroupLivePhoto, + metaData: json.encode(_dummyMetadata), + ); + } + if (taskIndex == 0) { + return Future.value(const []); + } + return _downloader.enqueueAll(tasks.slice(0, taskIndex)); } } diff --git a/mobile/lib/repositories/drift_album_api_repository.dart b/mobile/lib/repositories/drift_album_api_repository.dart index 26b55fbef6..6de025fb47 100644 --- a/mobile/lib/repositories/drift_album_api_repository.dart +++ b/mobile/lib/repositories/drift_album_api_repository.dart @@ -14,34 +14,16 @@ class DriftAlbumApiRepository extends ApiRepository { DriftAlbumApiRepository(this._api); - Future createDriftAlbum( - String name, { - required Iterable assetIds, - String? description, - }) async { + Future createDriftAlbum(String name, {required Iterable assetIds, String? description}) async { final responseDto = await checkNull( - _api.createAlbum( - CreateAlbumDto( - albumName: name, - description: description, - assetIds: assetIds.toList(), - ), - ), + _api.createAlbum(CreateAlbumDto(albumName: name, description: description, assetIds: assetIds.toList())), ); return responseDto.toRemoteAlbum(); } - Future<({List removed, List failed})> removeAssets( - String albumId, - Iterable assetIds, - ) async { - final response = await checkNull( - _api.removeAssetFromAlbum( - albumId, - BulkIdsDto(ids: assetIds.toList()), - ), - ); + Future<({List removed, List failed})> removeAssets(String albumId, Iterable assetIds) async { + final response = await checkNull(_api.removeAssetFromAlbum(albumId, BulkIdsDto(ids: assetIds.toList()))); final List removed = [], failed = []; for (final dto in response) { if (dto.success) { @@ -53,16 +35,8 @@ class DriftAlbumApiRepository extends ApiRepository { return (removed: removed, failed: failed); } - Future<({List added, List failed})> addAssets( - String albumId, - Iterable assetIds, - ) async { - final response = await checkNull( - _api.addAssetsToAlbum( - albumId, - BulkIdsDto(ids: assetIds.toList()), - ), - ); + Future<({List added, List failed})> addAssets(String albumId, Iterable assetIds) async { + final response = await checkNull(_api.addAssetsToAlbum(albumId, BulkIdsDto(ids: assetIds.toList()))); final List added = [], failed = []; for (final dto in response) { if (dto.success) { @@ -85,8 +59,7 @@ class DriftAlbumApiRepository extends ApiRepository { }) async { AssetOrder? apiOrder; if (order != null) { - apiOrder = - order == AlbumAssetOrder.asc ? AssetOrder.asc : AssetOrder.desc; + apiOrder = order == AlbumAssetOrder.asc ? AssetOrder.asc : AssetOrder.desc; } final responseDto = await checkNull( @@ -109,18 +82,9 @@ class DriftAlbumApiRepository extends ApiRepository { return _api.deleteAlbum(albumId); } - Future addUsers( - String albumId, - Iterable userIds, - ) async { - final albumUsers = - userIds.map((userId) => AlbumUserAddDto(userId: userId)).toList(); - final response = await checkNull( - _api.addUsersToAlbum( - albumId, - AddUsersDto(albumUsers: albumUsers), - ), - ); + Future addUsers(String albumId, Iterable userIds) async { + final albumUsers = userIds.map((userId) => AlbumUserAddDto(userId: userId)).toList(); + final response = await checkNull(_api.addUsersToAlbum(albumId, AddUsersDto(albumUsers: albumUsers))); return response.toRemoteAlbum(); } } @@ -136,8 +100,7 @@ extension on AlbumResponseDto { updatedAt: updatedAt, thumbnailAssetId: albumThumbnailAssetId, isActivityEnabled: isActivityEnabled, - order: - order == AssetOrder.asc ? AlbumAssetOrder.asc : AlbumAssetOrder.desc, + order: order == AssetOrder.asc ? AlbumAssetOrder.asc : AlbumAssetOrder.desc, assetCount: assetCount, ownerName: owner.name, ); diff --git a/mobile/lib/repositories/etag.repository.dart b/mobile/lib/repositories/etag.repository.dart index b8382efdfc..768d95b95c 100644 --- a/mobile/lib/repositories/etag.repository.dart +++ b/mobile/lib/repositories/etag.repository.dart @@ -4,8 +4,7 @@ import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/repositories/database.repository.dart'; import 'package:isar/isar.dart'; -final etagRepositoryProvider = - Provider((ref) => ETagRepository(ref.watch(dbProvider))); +final etagRepositoryProvider = Provider((ref) => ETagRepository(ref.watch(dbProvider))); class ETagRepository extends DatabaseRepository { const ETagRepository(super.db); @@ -16,8 +15,7 @@ class ETagRepository extends DatabaseRepository { Future upsertAll(List etags) => txn(() => db.eTags.putAll(etags)); - Future deleteByIds(List ids) => - txn(() => db.eTags.deleteAllById(ids)); + Future deleteByIds(List ids) => txn(() => db.eTags.deleteAllById(ids)); Future getById(String id) => db.eTags.getById(id); diff --git a/mobile/lib/repositories/file_media.repository.dart b/mobile/lib/repositories/file_media.repository.dart index 2bccb05210..6d429e4777 100644 --- a/mobile/lib/repositories/file_media.repository.dart +++ b/mobile/lib/repositories/file_media.repository.dart @@ -6,69 +6,33 @@ import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/repositories/asset_media.repository.dart'; import 'package:photo_manager/photo_manager.dart' hide AssetType; -final fileMediaRepositoryProvider = - Provider((ref) => const FileMediaRepository()); +final fileMediaRepositoryProvider = Provider((ref) => const FileMediaRepository()); class FileMediaRepository { const FileMediaRepository(); - Future saveImage( - Uint8List data, { - required String title, - String? relativePath, - }) async { - final entity = await PhotoManager.editor.saveImage( - data, - filename: title, - title: title, - relativePath: relativePath, - ); + Future saveImage(Uint8List data, {required String title, String? relativePath}) async { + final entity = await PhotoManager.editor.saveImage(data, filename: title, title: title, relativePath: relativePath); return AssetMediaRepository.toAsset(entity); } - Future saveImageWithFile( - String filePath, { - String? title, - String? relativePath, - }) async { - final entity = await PhotoManager.editor.saveImageWithPath( - filePath, - title: title, - relativePath: relativePath, - ); + Future saveImageWithFile(String filePath, {String? title, String? relativePath}) async { + final entity = await PhotoManager.editor.saveImageWithPath(filePath, title: title, relativePath: relativePath); return AssetMediaRepository.toAsset(entity); } - Future saveLivePhoto({ - required File image, - required File video, - required String title, - }) async { - final entity = await PhotoManager.editor.darwin.saveLivePhoto( - imageFile: image, - videoFile: video, - title: title, - ); + Future saveLivePhoto({required File image, required File video, required String title}) async { + final entity = await PhotoManager.editor.darwin.saveLivePhoto(imageFile: image, videoFile: video, title: title); return AssetMediaRepository.toAsset(entity); } - Future saveVideo( - File file, { - required String title, - String? relativePath, - }) async { - final entity = await PhotoManager.editor.saveVideo( - file, - title: title, - relativePath: relativePath, - ); + Future saveVideo(File file, {required String title, String? relativePath}) async { + final entity = await PhotoManager.editor.saveVideo(file, title: title, relativePath: relativePath); return AssetMediaRepository.toAsset(entity); } Future clearFileCache() => PhotoManager.clearFileCache(); - Future enableBackgroundAccess() => - PhotoManager.setIgnorePermissionCheck(true); + Future enableBackgroundAccess() => PhotoManager.setIgnorePermissionCheck(true); - Future requestExtendedPermissions() => - PhotoManager.requestPermissionExtend(); + Future requestExtendedPermissions() => PhotoManager.requestPermissionExtend(); } diff --git a/mobile/lib/repositories/folder_api.repository.dart b/mobile/lib/repositories/folder_api.repository.dart index 9fcb57ae21..dfda19e45e 100644 --- a/mobile/lib/repositories/folder_api.repository.dart +++ b/mobile/lib/repositories/folder_api.repository.dart @@ -5,11 +5,7 @@ import 'package:immich_mobile/repositories/api.repository.dart'; import 'package:logging/logging.dart'; import 'package:openapi/api.dart'; -final folderApiRepositoryProvider = Provider( - (ref) => FolderApiRepository( - ref.watch(apiServiceProvider).viewApi, - ), -); +final folderApiRepositoryProvider = Provider((ref) => FolderApiRepository(ref.watch(apiServiceProvider).viewApi)); class FolderApiRepository extends ApiRepository { final ViewApi _api; diff --git a/mobile/lib/repositories/gcast.repository.dart b/mobile/lib/repositories/gcast.repository.dart index 11c149ab37..db3e0f45d0 100644 --- a/mobile/lib/repositories/gcast.repository.dart +++ b/mobile/lib/repositories/gcast.repository.dart @@ -33,19 +33,13 @@ class GCastRepository { }); // open the default receiver - sendMessage(CastSession.kNamespaceReceiver, { - 'type': 'LAUNCH', - 'appId': 'CC1AD845', - }); + sendMessage(CastSession.kNamespaceReceiver, {'type': 'LAUNCH', 'appId': 'CC1AD845'}); } Future disconnect() async { final sessionID = getSessionId(); - sendMessage(CastSession.kNamespaceReceiver, { - 'type': "STOP", - "sessionId": sessionID, - }); + sendMessage(CastSession.kNamespaceReceiver, {'type': "STOP", "sessionId": sessionID}); // wait 500ms to ensure the stop command is processed await Future.delayed(const Duration(milliseconds: 500)); @@ -69,7 +63,6 @@ class GCastRepository { } Future> listDestinations() async { - return await CastDiscoveryService() - .search(timeout: const Duration(seconds: 3)); + return await CastDiscoveryService().search(timeout: const Duration(seconds: 3)); } } diff --git a/mobile/lib/repositories/local_files_manager.repository.dart b/mobile/lib/repositories/local_files_manager.repository.dart index 35c59acc2f..519d79b49b 100644 --- a/mobile/lib/repositories/local_files_manager.repository.dart +++ b/mobile/lib/repositories/local_files_manager.repository.dart @@ -2,8 +2,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/services/local_files_manager.service.dart'; final localFilesManagerRepositoryProvider = Provider( - (ref) => - LocalFilesManagerRepository(ref.watch(localFileManagerServiceProvider)), + (ref) => LocalFilesManagerRepository(ref.watch(localFileManagerServiceProvider)), ); class LocalFilesManagerRepository { diff --git a/mobile/lib/repositories/partner.repository.dart b/mobile/lib/repositories/partner.repository.dart index eb41391ba2..7f5ce62e0c 100644 --- a/mobile/lib/repositories/partner.repository.dart +++ b/mobile/lib/repositories/partner.repository.dart @@ -1,49 +1,34 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; -import 'package:immich_mobile/infrastructure/entities/user.entity.dart' - as entity; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart' as entity; import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/repositories/database.repository.dart'; import 'package:isar/isar.dart'; -final partnerRepositoryProvider = Provider( - (ref) => PartnerRepository(ref.watch(dbProvider)), -); +final partnerRepositoryProvider = Provider((ref) => PartnerRepository(ref.watch(dbProvider))); class PartnerRepository extends DatabaseRepository { const PartnerRepository(super.db); Future> getSharedBy() async { - return (await db.users - .filter() - .isPartnerSharedByEqualTo(true) - .sortById() - .findAll()) - .map((u) => u.toDto()) - .toList(); + return (await db.users.filter().isPartnerSharedByEqualTo(true).sortById().findAll()).map((u) => u.toDto()).toList(); } Future> getSharedWith() async { - return (await db.users - .filter() - .isPartnerSharedWithEqualTo(true) - .sortById() - .findAll()) + return (await db.users.filter().isPartnerSharedWithEqualTo(true).sortById().findAll()) .map((u) => u.toDto()) .toList(); } Stream> watchSharedBy() { - return (db.users.filter().isPartnerSharedByEqualTo(true).sortById().watch()) - .map((users) => users.map((u) => u.toDto()).toList()); + return (db.users.filter().isPartnerSharedByEqualTo(true).sortById().watch()).map( + (users) => users.map((u) => u.toDto()).toList(), + ); } Stream> watchSharedWith() { - return (db.users - .filter() - .isPartnerSharedWithEqualTo(true) - .sortById() - .watch()) - .map((users) => users.map((u) => u.toDto()).toList()); + return (db.users.filter().isPartnerSharedWithEqualTo(true).sortById().watch()).map( + (users) => users.map((u) => u.toDto()).toList(), + ); } } diff --git a/mobile/lib/repositories/partner_api.repository.dart b/mobile/lib/repositories/partner_api.repository.dart index 836a708e3a..82554d62e8 100644 --- a/mobile/lib/repositories/partner_api.repository.dart +++ b/mobile/lib/repositories/partner_api.repository.dart @@ -5,16 +5,9 @@ import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/repositories/api.repository.dart'; import 'package:openapi/api.dart'; -enum Direction { - sharedWithMe, - sharedByMe, -} +enum Direction { sharedWithMe, sharedByMe } -final partnerApiRepositoryProvider = Provider( - (ref) => PartnerApiRepository( - ref.watch(apiServiceProvider).partnersApi, - ), -); +final partnerApiRepositoryProvider = Provider((ref) => PartnerApiRepository(ref.watch(apiServiceProvider).partnersApi)); class PartnerApiRepository extends ApiRepository { final PartnersApi _api; @@ -23,11 +16,7 @@ class PartnerApiRepository extends ApiRepository { Future> getAll(Direction direction) async { final response = await checkNull( - _api.getPartners( - direction == Direction.sharedByMe - ? PartnerDirection.by - : PartnerDirection.with_, - ), + _api.getPartners(direction == Direction.sharedByMe ? PartnerDirection.by : PartnerDirection.with_), ); return response.map(UserConverter.fromPartnerDto).toList(); } @@ -40,12 +29,7 @@ class PartnerApiRepository extends ApiRepository { Future delete(String id) => _api.removePartner(id); Future update(String id, {required bool inTimeline}) async { - final dto = await checkNull( - _api.updatePartner( - id, - UpdatePartnerDto(inTimeline: inTimeline), - ), - ); + final dto = await checkNull(_api.updatePartner(id, UpdatePartnerDto(inTimeline: inTimeline))); return UserConverter.fromPartnerDto(dto); } } diff --git a/mobile/lib/repositories/person_api.repository.dart b/mobile/lib/repositories/person_api.repository.dart index a2a6e2489b..04e4bd2a2c 100644 --- a/mobile/lib/repositories/person_api.repository.dart +++ b/mobile/lib/repositories/person_api.repository.dart @@ -4,32 +4,28 @@ import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/repositories/api.repository.dart'; import 'package:openapi/api.dart'; -final personApiRepositoryProvider = Provider( - (ref) => PersonApiRepository(ref.watch(apiServiceProvider).peopleApi), -); +final personApiRepositoryProvider = Provider((ref) => PersonApiRepository(ref.watch(apiServiceProvider).peopleApi)); class PersonApiRepository extends ApiRepository { final PeopleApi _api; PersonApiRepository(this._api); - Future> getAll() async { + Future> getAll() async { final dto = await checkNull(_api.getAllPeople()); return dto.people.map(_toPerson).toList(); } - Future update(String id, {String? name}) async { - final dto = await checkNull( - _api.updatePerson(id, PersonUpdateDto(name: name)), - ); + Future update(String id, {String? name, DateTime? birthday}) async { + final dto = await checkNull(_api.updatePerson(id, PersonUpdateDto(name: name, birthDate: birthday))); return _toPerson(dto); } - static Person _toPerson(PersonResponseDto dto) => Person( - birthDate: dto.birthDate, - id: dto.id, - isHidden: dto.isHidden, - name: dto.name, - thumbnailPath: dto.thumbnailPath, - ); + static PersonDto _toPerson(PersonResponseDto dto) => PersonDto( + birthDate: dto.birthDate, + id: dto.id, + isHidden: dto.isHidden, + name: dto.name, + thumbnailPath: dto.thumbnailPath, + ); } diff --git a/mobile/lib/repositories/secure_storage.repository.dart b/mobile/lib/repositories/secure_storage.repository.dart index 5660258a2d..9ae643a587 100644 --- a/mobile/lib/repositories/secure_storage.repository.dart +++ b/mobile/lib/repositories/secure_storage.repository.dart @@ -1,8 +1,7 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -final secureStorageRepositoryProvider = - Provider((ref) => const SecureStorageRepository(FlutterSecureStorage())); +final secureStorageRepositoryProvider = Provider((ref) => const SecureStorageRepository(FlutterSecureStorage())); class SecureStorageRepository { final FlutterSecureStorage _secureStorage; diff --git a/mobile/lib/repositories/sessions_api.repository.dart b/mobile/lib/repositories/sessions_api.repository.dart index 8ef43d54f2..f25e724f19 100644 --- a/mobile/lib/repositories/sessions_api.repository.dart +++ b/mobile/lib/repositories/sessions_api.repository.dart @@ -5,9 +5,7 @@ import 'package:immich_mobile/repositories/api.repository.dart'; import 'package:openapi/api.dart'; final sessionsAPIRepositoryProvider = Provider( - (ref) => SessionsAPIRepository( - ref.watch(apiServiceProvider).sessionsApi, - ), + (ref) => SessionsAPIRepository(ref.watch(apiServiceProvider).sessionsApi), ); class SessionsAPIRepository extends ApiRepository { @@ -15,19 +13,9 @@ class SessionsAPIRepository extends ApiRepository { SessionsAPIRepository(this._api); - Future createSession( - String deviceType, - String deviceOS, { - int? duration, - }) async { + Future createSession(String deviceType, String deviceOS, {int? duration}) async { final dto = await checkNull( - _api.createSession( - SessionCreateDto( - deviceType: deviceType, - deviceOS: deviceOS, - duration: duration, - ), - ), + _api.createSession(SessionCreateDto(deviceType: deviceType, deviceOS: deviceOS, duration: duration)), ); return SessionCreateResponse( diff --git a/mobile/lib/repositories/share_handler.repository.dart b/mobile/lib/repositories/share_handler.repository.dart index e739ebe6ae..4650b04a79 100644 --- a/mobile/lib/repositories/share_handler.repository.dart +++ b/mobile/lib/repositories/share_handler.repository.dart @@ -4,9 +4,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/models/upload/share_intent_attachment.model.dart'; import 'package:share_handler/share_handler.dart'; -final shareHandlerRepositoryProvider = Provider( - (ref) => ShareHandlerRepository(), -); +final shareHandlerRepositoryProvider = Provider((ref) => ShareHandlerRepository()); class ShareHandlerRepository { ShareHandlerRepository(); @@ -28,9 +26,7 @@ class ShareHandlerRepository { }); } - List _buildPayload( - List attachments, - ) { + List _buildPayload(List attachments) { final payload = []; for (final attachment in attachments) { diff --git a/mobile/lib/repositories/timeline.repository.dart b/mobile/lib/repositories/timeline.repository.dart index ea4cb63753..c8c173b6f6 100644 --- a/mobile/lib/repositories/timeline.repository.dart +++ b/mobile/lib/repositories/timeline.repository.dart @@ -9,30 +9,17 @@ import 'package:immich_mobile/utils/hash.dart'; import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart'; import 'package:isar/isar.dart'; -final timelineRepositoryProvider = - Provider((ref) => TimelineRepository(ref.watch(dbProvider))); +final timelineRepositoryProvider = Provider((ref) => TimelineRepository(ref.watch(dbProvider))); class TimelineRepository extends DatabaseRepository { const TimelineRepository(super.db); Future> getTimelineUserIds(String id) { - return db.users - .filter() - .inTimelineEqualTo(true) - .or() - .idEqualTo(id) - .idProperty() - .findAll(); + return db.users.filter().inTimelineEqualTo(true).or().idEqualTo(id).idProperty().findAll(); } Stream> watchTimelineUsers(String id) { - return db.users - .filter() - .inTimelineEqualTo(true) - .or() - .idEqualTo(id) - .idProperty() - .watch(); + return db.users.filter().inTimelineEqualTo(true).or().idEqualTo(id).idProperty().watch(); } Stream watchArchiveTimeline(String userId) { @@ -61,15 +48,8 @@ class TimelineRepository extends DatabaseRepository { return _watchRenderList(query, GroupAssetsBy.none); } - Stream watchAlbumTimeline( - Album album, - GroupAssetsBy groupAssetByOption, - ) { - final query = album.assets - .filter() - .isTrashedEqualTo(false) - .not() - .visibilityEqualTo(AssetVisibilityEnum.locked); + Stream watchAlbumTimeline(Album album, GroupAssetsBy groupAssetByOption) { + final query = album.assets.filter().isTrashedEqualTo(false).not().visibilityEqualTo(AssetVisibilityEnum.locked); final withSortedOption = switch (album.sortOrder) { SortOrder.asc => query.sortByFileCreatedAt(), @@ -80,11 +60,7 @@ class TimelineRepository extends DatabaseRepository { } Stream watchTrashTimeline(String userId) { - final query = db.assets - .filter() - .ownerIdEqualTo(fastHash(userId)) - .isTrashedEqualTo(true) - .sortByFileCreatedAtDesc(); + final query = db.assets.filter().ownerIdEqualTo(fastHash(userId)).isTrashedEqualTo(true).sortByFileCreatedAtDesc(); return _watchRenderList(query, GroupAssetsBy.none); } @@ -102,10 +78,7 @@ class TimelineRepository extends DatabaseRepository { return _watchRenderList(query, GroupAssetsBy.none); } - Stream watchHomeTimeline( - String userId, - GroupAssetsBy groupAssetByOption, - ) { + Stream watchHomeTimeline(String userId, GroupAssetsBy groupAssetByOption) { final query = db.assets .where() .ownerIdEqualToAnyChecksum(fastHash(userId)) @@ -118,10 +91,7 @@ class TimelineRepository extends DatabaseRepository { return _watchRenderList(query, groupAssetByOption); } - Stream watchMultiUsersTimeline( - List userIds, - GroupAssetsBy groupAssetByOption, - ) { + Stream watchMultiUsersTimeline(List userIds, GroupAssetsBy groupAssetByOption) { final isarUserIds = userIds.map(fastHash).toList(); final query = db.assets .where() @@ -134,10 +104,7 @@ class TimelineRepository extends DatabaseRepository { return _watchRenderList(query, groupAssetByOption); } - Future getTimelineFromAssets( - List assets, - GroupAssetsBy getGroupByOption, - ) { + Future getTimelineFromAssets(List assets, GroupAssetsBy getGroupByOption) { return RenderList.fromAssets(assets, getGroupByOption); } @@ -155,10 +122,7 @@ class TimelineRepository extends DatabaseRepository { return _watchRenderList(query, GroupAssetsBy.none); } - Stream watchLockedTimeline( - String userId, - GroupAssetsBy getGroupByOption, - ) { + Stream watchLockedTimeline(String userId, GroupAssetsBy getGroupByOption) { final query = db.assets .where() .ownerIdEqualToAnyChecksum(fastHash(userId)) diff --git a/mobile/lib/repositories/upload.repository.dart b/mobile/lib/repositories/upload.repository.dart index 4f840fa3c6..220dbf81c3 100644 --- a/mobile/lib/repositories/upload.repository.dart +++ b/mobile/lib/repositories/upload.repository.dart @@ -1,35 +1,73 @@ import 'package:background_downloader/background_downloader.dart'; +import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/utils/upload.dart'; +import 'package:immich_mobile/constants/constants.dart'; final uploadRepositoryProvider = Provider((ref) => UploadRepository()); class UploadRepository { void Function(TaskStatusUpdate)? onUploadStatus; - void Function(TaskProgressUpdate)? onTaskProgress; UploadRepository() { FileDownloader().registerCallbacks( - group: uploadGroup, + group: kBackupGroup, + taskStatusCallback: (update) => onUploadStatus?.call(update), + taskProgressCallback: (update) => onTaskProgress?.call(update), + ); + FileDownloader().registerCallbacks( + group: kBackupLivePhotoGroup, + taskStatusCallback: (update) => onUploadStatus?.call(update), + taskProgressCallback: (update) => onTaskProgress?.call(update), + ); + FileDownloader().registerCallbacks( + group: kManualUploadGroup, taskStatusCallback: (update) => onUploadStatus?.call(update), taskProgressCallback: (update) => onTaskProgress?.call(update), ); } - Future upload(UploadTask task) { - return FileDownloader().enqueue(task); + void enqueueBackgroundAll(List tasks) { + FileDownloader().enqueueAll(tasks); } - Future deleteAllTrackingRecords() { - return FileDownloader().database.deleteAllRecords(); + Future deleteDatabaseRecords(String group) { + return FileDownloader().database.deleteAllRecords(group: group); } - Future cancel(String id) { - return FileDownloader().cancelTaskWithId(id); + Future cancelAll(String group) { + return FileDownloader().cancelAll(group: group); } - Future deleteRecordsWithIds(List ids) { - return FileDownloader().database.deleteRecordsWithIds(ids); + Future reset(String group) { + return FileDownloader().reset(group: group); + } + + /// Get a list of tasks that are ENQUEUED or RUNNING + Future> getActiveTasks(String group) { + return FileDownloader().allTasks(group: group); + } + + Future start() { + return FileDownloader().start(); + } + + Future getUploadInfo() async { + final [enqueuedTasks, runningTasks, canceledTasks, waitingTasks, pausedTasks] = await Future.wait([ + FileDownloader().database.allRecordsWithStatus(TaskStatus.enqueued, group: kBackupGroup), + FileDownloader().database.allRecordsWithStatus(TaskStatus.running, group: kBackupGroup), + FileDownloader().database.allRecordsWithStatus(TaskStatus.canceled, group: kBackupGroup), + FileDownloader().database.allRecordsWithStatus(TaskStatus.waitingToRetry, group: kBackupGroup), + FileDownloader().database.allRecordsWithStatus(TaskStatus.paused, group: kBackupGroup), + ]); + + debugPrint(""" + Upload Info: + Enqueued: ${enqueuedTasks.length} + Running: ${runningTasks.length} + Canceled: ${canceledTasks.length} + Waiting: ${waitingTasks.length} + Paused: ${pausedTasks.length} + """); } } diff --git a/mobile/lib/repositories/widget.repository.dart b/mobile/lib/repositories/widget.repository.dart index be314a281e..f21d31e1ec 100644 --- a/mobile/lib/repositories/widget.repository.dart +++ b/mobile/lib/repositories/widget.repository.dart @@ -10,8 +10,8 @@ class WidgetRepository { await HomeWidget.saveWidgetData(key, value); } - Future refresh(String name) async { - await HomeWidget.updateWidget(name: name, iOSName: name); + Future refresh(String iosName, String androidName) async { + await HomeWidget.updateWidget(iOSName: iosName, qualifiedAndroidName: androidName); } Future setAppGroupId(String appGroupId) async { diff --git a/mobile/lib/routing/app_navigation_observer.dart b/mobile/lib/routing/app_navigation_observer.dart index 047e897c8e..26ec017b9a 100644 --- a/mobile/lib/routing/app_navigation_observer.dart +++ b/mobile/lib/routing/app_navigation_observer.dart @@ -8,50 +8,53 @@ class AppNavigationObserver extends AutoRouterObserver { /// Riverpod Instance final WidgetRef ref; - AppNavigationObserver({ - required this.ref, - }); + AppNavigationObserver({required this.ref}); @override - Future didChangeTabRoute( - TabPageRoute route, - TabPageRoute previousRoute, - ) async { - Future( - () => ref.read(inLockedViewProvider.notifier).state = false, - ); + Future didChangeTabRoute(TabPageRoute route, TabPageRoute previousRoute) async { + Future(() => ref.read(inLockedViewProvider.notifier).state = false); } @override void didPush(Route route, Route? previousRoute) { _handleLockedViewState(route, previousRoute); - - Future( - () => ref.read(currentRouteNameProvider.notifier).state = - route.settings.name, - ); + _handleDriftLockedFolderState(route, previousRoute); + Future(() { + ref.read(currentRouteNameProvider.notifier).state = route.settings.name; + ref.read(previousRouteNameProvider.notifier).state = previousRoute?.settings.name; + ref.read(previousRouteDataProvider.notifier).state = previousRoute?.settings; + }); } _handleLockedViewState(Route route, Route? previousRoute) { final isInLockedView = ref.read(inLockedViewProvider); final isFromLockedViewToDetailView = - route.settings.name == GalleryViewerRoute.name && - previousRoute?.settings.name == LockedRoute.name; + route.settings.name == GalleryViewerRoute.name && previousRoute?.settings.name == LockedRoute.name; - final isFromDetailViewToInfoPanelView = route.settings.name == null && - previousRoute?.settings.name == GalleryViewerRoute.name && - isInLockedView; + final isFromDetailViewToInfoPanelView = + route.settings.name == null && previousRoute?.settings.name == GalleryViewerRoute.name && isInLockedView; - if (route.settings.name == LockedRoute.name || + if (route.settings.name == LockedRoute.name || isFromLockedViewToDetailView || isFromDetailViewToInfoPanelView) { + Future(() => ref.read(inLockedViewProvider.notifier).state = true); + } else { + Future(() => ref.read(inLockedViewProvider.notifier).state = false); + } + } + + _handleDriftLockedFolderState(Route route, Route? previousRoute) { + final isInLockedView = ref.read(inLockedViewProvider); + final isFromLockedViewToDetailView = + route.settings.name == AssetViewerRoute.name && previousRoute?.settings.name == DriftLockedFolderRoute.name; + + final isFromDetailViewToInfoPanelView = + route.settings.name == null && previousRoute?.settings.name == AssetViewerRoute.name && isInLockedView; + + if (route.settings.name == DriftLockedFolderRoute.name || isFromLockedViewToDetailView || isFromDetailViewToInfoPanelView) { - Future( - () => ref.read(inLockedViewProvider.notifier).state = true, - ); + Future(() => ref.read(inLockedViewProvider.notifier).state = true); } else { - Future( - () => ref.read(inLockedViewProvider.notifier).state = false, - ); + Future(() => ref.read(inLockedViewProvider.notifier).state = false); } } } diff --git a/mobile/lib/routing/custom_transition_builders.dart b/mobile/lib/routing/custom_transition_builders.dart index 610edd8185..d8412eb7cf 100644 --- a/mobile/lib/routing/custom_transition_builders.dart +++ b/mobile/lib/routing/custom_transition_builders.dart @@ -3,8 +3,7 @@ import 'package:flutter/material.dart'; class CustomTransitionsBuilders { const CustomTransitionsBuilders._(); - static const ZoomPageTransitionsBuilder zoomPageTransitionsBuilder = - ZoomPageTransitionsBuilder(); + static const ZoomPageTransitionsBuilder zoomPageTransitionsBuilder = ZoomPageTransitionsBuilder(); static const RouteTransitionsBuilder zoomedPage = _zoomedPage; @@ -19,8 +18,7 @@ class CustomTransitionsBuilders { PageRouteBuilder( allowSnapshotting: true, fullscreenDialog: false, - pageBuilder: (context, animation, secondaryAnimation) => - const SizedBox.shrink(), + pageBuilder: (context, animation, secondaryAnimation) => const SizedBox.shrink(), ), context, animation, diff --git a/mobile/lib/routing/duplicate_guard.dart b/mobile/lib/routing/duplicate_guard.dart index efc649bc41..6f83d5297e 100644 --- a/mobile/lib/routing/duplicate_guard.dart +++ b/mobile/lib/routing/duplicate_guard.dart @@ -8,9 +8,7 @@ class DuplicateGuard extends AutoRouteGuard { void onNavigation(NavigationResolver resolver, StackRouter router) async { // Duplicate navigation if (resolver.route.name == router.current.name) { - debugPrint( - 'DuplicateGuard: Preventing duplicate route navigation for ${resolver.route.name}', - ); + debugPrint('DuplicateGuard: Preventing duplicate route navigation for ${resolver.route.name}'); resolver.next(false); } else { resolver.next(true); diff --git a/mobile/lib/routing/gallery_guard.dart b/mobile/lib/routing/gallery_guard.dart index fcac142271..eace8257b6 100644 --- a/mobile/lib/routing/gallery_guard.dart +++ b/mobile/lib/routing/gallery_guard.dart @@ -7,8 +7,7 @@ class GalleryGuard extends AutoRouteGuard { @override void onNavigation(NavigationResolver resolver, StackRouter router) async { final newRouteName = resolver.route.name; - final currentTopRouteName = - router.stack.isNotEmpty ? router.stack.last.name : null; + final currentTopRouteName = router.stack.isNotEmpty ? router.stack.last.name : null; if (currentTopRouteName == newRouteName) { // Replace instead of pushing duplicate diff --git a/mobile/lib/routing/locked_guard.dart b/mobile/lib/routing/locked_guard.dart index d731c7942c..851407ff16 100644 --- a/mobile/lib/routing/locked_guard.dart +++ b/mobile/lib/routing/locked_guard.dart @@ -17,11 +17,7 @@ class LockedGuard extends AutoRouteGuard { final LocalAuthService _localAuth; final _log = Logger("AuthGuard"); - LockedGuard( - this._apiService, - this._secureStorageService, - this._localAuth, - ); + LockedGuard(this._apiService, this._secureStorageService, this._localAuth); @override void onNavigation(NavigationResolver resolver, StackRouter router) async { @@ -58,9 +54,7 @@ class LockedGuard extends AutoRouteGuard { return; } - await _apiService.authenticationApi.unlockAuthSession( - SessionUnlockDto(pinCode: securePinCode), - ); + await _apiService.authenticationApi.unlockAuthSession(SessionUnlockDto(pinCode: securePinCode)); resolver.next(true); } on PlatformException catch (error) { diff --git a/mobile/lib/routing/router.dart b/mobile/lib/routing/router.dart index 94e3437d66..4fe1673893 100644 --- a/mobile/lib/routing/router.dart +++ b/mobile/lib/routing/router.dart @@ -6,6 +6,7 @@ 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/log.model.dart'; import 'package:immich_mobile/domain/models/memory.model.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/domain/services/timeline.service.dart'; import 'package:immich_mobile/entities/album.entity.dart'; @@ -22,9 +23,13 @@ import 'package:immich_mobile/pages/album/album_shared_user_selection.page.dart' import 'package:immich_mobile/pages/album/album_viewer.page.dart'; import 'package:immich_mobile/pages/albums/albums.page.dart'; import 'package:immich_mobile/pages/backup/album_preview.page.dart'; +import 'package:immich_mobile/pages/backup/drift_backup_album_selection.page.dart'; +import 'package:immich_mobile/pages/backup/drift_backup.page.dart'; import 'package:immich_mobile/pages/backup/backup_album_selection.page.dart'; import 'package:immich_mobile/pages/backup/backup_controller.page.dart'; import 'package:immich_mobile/pages/backup/backup_options.page.dart'; +import 'package:immich_mobile/pages/backup/drift_backup_options.page.dart'; +import 'package:immich_mobile/pages/backup/drift_upload_detail.page.dart'; import 'package:immich_mobile/pages/backup/failed_backup_status.page.dart'; import 'package:immich_mobile/pages/common/activities.page.dart'; import 'package:immich_mobile/pages/common/app_log.page.dart'; @@ -48,6 +53,7 @@ import 'package:immich_mobile/pages/library/library.page.dart'; import 'package:immich_mobile/pages/library/local_albums.page.dart'; import 'package:immich_mobile/pages/library/locked/locked.page.dart'; import 'package:immich_mobile/pages/library/locked/pin_auth.page.dart'; +import 'package:immich_mobile/pages/library/partner/drift_partner.page.dart'; import 'package:immich_mobile/pages/library/partner/partner.page.dart'; import 'package:immich_mobile/pages/library/partner/partner_detail.page.dart'; import 'package:immich_mobile/pages/library/people/people_collection.page.dart'; @@ -69,6 +75,7 @@ import 'package:immich_mobile/pages/search/map/map_location_picker.page.dart'; import 'package:immich_mobile/pages/search/person_result.page.dart'; import 'package:immich_mobile/pages/search/recently_taken.page.dart'; import 'package:immich_mobile/pages/search/search.page.dart'; +import 'package:immich_mobile/pages/settings/beta_sync_settings.page.dart'; import 'package:immich_mobile/pages/share_intent/share_intent.page.dart'; import 'package:immich_mobile/presentation/pages/dev/feat_in_development.page.dart'; import 'package:immich_mobile/presentation/pages/dev/main_timeline.page.dart'; @@ -83,6 +90,8 @@ import 'package:immich_mobile/presentation/pages/drift_local_album.page.dart'; import 'package:immich_mobile/presentation/pages/drift_locked_folder.page.dart'; import 'package:immich_mobile/presentation/pages/drift_memory.page.dart'; import 'package:immich_mobile/presentation/pages/drift_partner_detail.page.dart'; +import 'package:immich_mobile/presentation/pages/drift_people_collection.page.dart'; +import 'package:immich_mobile/presentation/pages/drift_person.page.dart'; import 'package:immich_mobile/presentation/pages/drift_place.page.dart'; import 'package:immich_mobile/presentation/pages/drift_place_detail.page.dart'; import 'package:immich_mobile/presentation/pages/drift_recently_taken.page.dart'; @@ -134,8 +143,7 @@ class AppRouter extends RootStackRouter { ) { _authGuard = AuthGuard(apiService); _duplicateGuard = const DuplicateGuard(); - _lockedGuard = - LockedGuard(apiService, secureStorageService, localAuthService); + _lockedGuard = LockedGuard(apiService, secureStorageService, localAuthService); _backupPermissionGuard = BackupPermissionGuard(galleryPermissionNotifier); _galleryGuard = const GalleryGuard(); } @@ -146,38 +154,18 @@ class AppRouter extends RootStackRouter { @override late final List routes = [ AutoRoute(page: SplashScreenRoute.page, initial: true), - AutoRoute( - page: PermissionOnboardingRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: PermissionOnboardingRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: LoginRoute.page, guards: [_duplicateGuard]), AutoRoute(page: ChangePasswordRoute.page), - AutoRoute( - page: SearchRoute.page, - guards: [_authGuard, _duplicateGuard], - maintainState: false, - ), + AutoRoute(page: SearchRoute.page, guards: [_authGuard, _duplicateGuard], maintainState: false), CustomRoute( page: TabControllerRoute.page, guards: [_authGuard, _duplicateGuard], children: [ - AutoRoute( - page: PhotosRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: SearchRoute.page, - guards: [_authGuard, _duplicateGuard], - maintainState: false, - ), - AutoRoute( - page: LibraryRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: AlbumsRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: PhotosRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: SearchRoute.page, guards: [_authGuard, _duplicateGuard], maintainState: false), + AutoRoute(page: LibraryRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: AlbumsRoute.page, guards: [_authGuard, _duplicateGuard]), ], transitionsBuilder: TransitionsBuilders.fadeIn, ), @@ -185,23 +173,10 @@ class AppRouter extends RootStackRouter { page: TabShellRoute.page, guards: [_authGuard, _duplicateGuard], children: [ - AutoRoute( - page: MainTimelineRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftSearchRoute.page, - guards: [_authGuard, _duplicateGuard], - maintainState: false, - ), - AutoRoute( - page: DriftLibraryRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftAlbumsRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: MainTimelineRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftSearchRoute.page, guards: [_authGuard, _duplicateGuard], maintainState: false), + AutoRoute(page: DriftLibraryRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftAlbumsRoute.page, guards: [_authGuard, _duplicateGuard]), ], transitionsBuilder: TransitionsBuilders.fadeIn, ), @@ -210,18 +185,9 @@ class AppRouter extends RootStackRouter { guards: [_authGuard, _galleryGuard], transitionsBuilder: CustomTransitionsBuilders.zoomedPage, ), - AutoRoute( - page: BackupControllerRoute.page, - guards: [_authGuard, _duplicateGuard, _backupPermissionGuard], - ), - AutoRoute( - page: AllPlacesRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: CreateAlbumRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: BackupControllerRoute.page, guards: [_authGuard, _duplicateGuard, _backupPermissionGuard]), + AutoRoute(page: AllPlacesRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: CreateAlbumRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: EditImageRoute.page), AutoRoute(page: CropImageRoute.page), AutoRoute(page: FilterImageRoute.page), @@ -231,14 +197,8 @@ class AppRouter extends RootStackRouter { transitionsBuilder: TransitionsBuilders.slideLeft, ), AutoRoute(page: AllVideosRoute.page, guards: [_authGuard, _duplicateGuard]), - AutoRoute( - page: AllMotionPhotosRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: RecentlyTakenRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: AllMotionPhotosRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: RecentlyTakenRoute.page, guards: [_authGuard, _duplicateGuard]), CustomRoute( page: AlbumAssetSelectionRoute.page, guards: [_authGuard, _duplicateGuard], @@ -249,23 +209,14 @@ class AppRouter extends RootStackRouter { guards: [_authGuard, _duplicateGuard], transitionsBuilder: TransitionsBuilders.slideBottom, ), - AutoRoute( - page: AlbumViewerRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: AlbumViewerRoute.page, guards: [_authGuard, _duplicateGuard]), CustomRoute( page: AlbumAdditionalSharedUserSelectionRoute.page, guards: [_authGuard, _duplicateGuard], transitionsBuilder: TransitionsBuilders.slideBottom, ), - AutoRoute( - page: BackupAlbumSelectionRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: AlbumPreviewRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: BackupAlbumSelectionRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: AlbumPreviewRoute.page, guards: [_authGuard, _duplicateGuard]), CustomRoute( page: FailedBackupStatusRoute.page, guards: [_authGuard, _duplicateGuard], @@ -285,26 +236,13 @@ class AppRouter extends RootStackRouter { guards: [_authGuard, _duplicateGuard], transitionsBuilder: TransitionsBuilders.slideLeft, ), - CustomRoute( - page: FolderRoute.page, - guards: [_authGuard], - transitionsBuilder: TransitionsBuilders.fadeIn, - ), - AutoRoute( - page: PartnerDetailRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: PersonResultRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + CustomRoute(page: FolderRoute.page, guards: [_authGuard], transitionsBuilder: TransitionsBuilders.fadeIn), + AutoRoute(page: PartnerDetailRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: PersonResultRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: AllPeopleRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: MemoryRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute(page: MapRoute.page, guards: [_authGuard, _duplicateGuard]), - AutoRoute( - page: AlbumOptionsRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: AlbumOptionsRoute.page, guards: [_authGuard, _duplicateGuard]), CustomRoute( page: TrashRoute.page, guards: [_authGuard, _duplicateGuard], @@ -315,28 +253,16 @@ class AppRouter extends RootStackRouter { guards: [_authGuard, _duplicateGuard], transitionsBuilder: TransitionsBuilders.slideLeft, ), - AutoRoute( - page: SharedLinkEditRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: SharedLinkEditRoute.page, guards: [_authGuard, _duplicateGuard]), CustomRoute( page: ActivitiesRoute.page, guards: [_authGuard, _duplicateGuard], transitionsBuilder: TransitionsBuilders.slideLeft, durationInMilliseconds: 200, ), - CustomRoute( - page: MapLocationPickerRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: BackupOptionsRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: HeaderSettingsRoute.page, - guards: [_duplicateGuard], - ), + CustomRoute(page: MapLocationPickerRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: BackupOptionsRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: HeaderSettingsRoute.page, guards: [_duplicateGuard]), CustomRoute( page: PeopleCollectionRoute.page, guards: [_authGuard, _duplicateGuard], @@ -357,46 +283,18 @@ class AppRouter extends RootStackRouter { guards: [_authGuard, _duplicateGuard], transitionsBuilder: TransitionsBuilders.slideLeft, ), - AutoRoute( - page: NativeVideoViewerRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: ShareIntentRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: LockedRoute.page, - guards: [_authGuard, _lockedGuard, _duplicateGuard], - ), - AutoRoute( - page: PinAuthRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: FeatInDevRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: LocalMediaSummaryRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: RemoteMediaSummaryRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: LocalTimelineRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: MainTimelineRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: RemoteAlbumRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: NativeVideoViewerRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: ShareIntentRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: LockedRoute.page, guards: [_authGuard, _lockedGuard, _duplicateGuard]), + AutoRoute(page: PinAuthRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: FeatInDevRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: LocalMediaSummaryRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: RemoteMediaSummaryRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftBackupRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftBackupAlbumSelectionRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: LocalTimelineRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: MainTimelineRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: RemoteAlbumRoute.page, guards: [_authGuard, _duplicateGuard]), AutoRoute( page: AssetViewerRoute.page, guards: [_authGuard, _duplicateGuard], @@ -409,71 +307,28 @@ class AppRouter extends RootStackRouter { ), ), ), - AutoRoute( - page: DriftMemoryRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftFavoriteRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftTrashRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftArchiveRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftLockedFolderRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftVideoRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftLibraryRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftAssetSelectionTimelineRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftPartnerDetailRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftRecentlyTakenRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftLocalAlbumsRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftCreateAlbumRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftPlaceRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftPlaceDetailRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - AutoRoute( - page: DriftUserSelectionRoute.page, - guards: [_authGuard, _duplicateGuard], - ), - - AutoRoute( - page: ChangeExperienceRoute.page, - guards: [_authGuard, _duplicateGuard], - ), + AutoRoute(page: DriftMemoryRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftFavoriteRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftTrashRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftArchiveRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftLockedFolderRoute.page, guards: [_authGuard, _lockedGuard, _duplicateGuard]), + AutoRoute(page: DriftVideoRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftLibraryRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftAssetSelectionTimelineRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftPartnerDetailRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftRecentlyTakenRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftLocalAlbumsRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftCreateAlbumRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftPlaceRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftPlaceDetailRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftUserSelectionRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: ChangeExperienceRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftPartnerRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftUploadDetailRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: BetaSyncSettingsRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftPeopleCollectionRoute.page, guards: [_authGuard, _duplicateGuard]), + AutoRoute(page: DriftPersonRoute.page, guards: [_authGuard]), + AutoRoute(page: DriftBackupOptionsRoute.page, guards: [_authGuard, _duplicateGuard]), // required to handle all deeplinks in deep_link.service.dart // auto_route_library#1722 RedirectRoute(path: '*', redirectTo: '/'), diff --git a/mobile/lib/routing/router.gr.dart b/mobile/lib/routing/router.gr.dart index 716d5ef89a..e8f0dd8b1f 100644 --- a/mobile/lib/routing/router.gr.dart +++ b/mobile/lib/routing/router.gr.dart @@ -14,7 +14,7 @@ part of 'router.dart'; /// [ActivitiesPage] class ActivitiesRoute extends PageRouteInfo { const ActivitiesRoute({List? children}) - : super(ActivitiesRoute.name, initialChildren: children); + : super(ActivitiesRoute.name, initialChildren: children); static const String name = 'ActivitiesRoute'; @@ -35,13 +35,13 @@ class AlbumAdditionalSharedUserSelectionRoute required Album album, List? children, }) : super( - AlbumAdditionalSharedUserSelectionRoute.name, - args: AlbumAdditionalSharedUserSelectionRouteArgs( - key: key, - album: album, - ), - initialChildren: children, - ); + AlbumAdditionalSharedUserSelectionRoute.name, + args: AlbumAdditionalSharedUserSelectionRouteArgs( + key: key, + album: album, + ), + initialChildren: children, + ); static const String name = 'AlbumAdditionalSharedUserSelectionRoute'; @@ -83,14 +83,14 @@ class AlbumAssetSelectionRoute bool canDeselect = false, List? children, }) : super( - AlbumAssetSelectionRoute.name, - args: AlbumAssetSelectionRouteArgs( - key: key, - existingAssets: existingAssets, - canDeselect: canDeselect, - ), - initialChildren: children, - ); + AlbumAssetSelectionRoute.name, + args: AlbumAssetSelectionRouteArgs( + key: key, + existingAssets: existingAssets, + canDeselect: canDeselect, + ), + initialChildren: children, + ); static const String name = 'AlbumAssetSelectionRoute'; @@ -130,7 +130,7 @@ class AlbumAssetSelectionRouteArgs { /// [AlbumOptionsPage] class AlbumOptionsRoute extends PageRouteInfo { const AlbumOptionsRoute({List? children}) - : super(AlbumOptionsRoute.name, initialChildren: children); + : super(AlbumOptionsRoute.name, initialChildren: children); static const String name = 'AlbumOptionsRoute'; @@ -150,10 +150,10 @@ class AlbumPreviewRoute extends PageRouteInfo { required Album album, List? children, }) : super( - AlbumPreviewRoute.name, - args: AlbumPreviewRouteArgs(key: key, album: album), - initialChildren: children, - ); + AlbumPreviewRoute.name, + args: AlbumPreviewRouteArgs(key: key, album: album), + initialChildren: children, + ); static const String name = 'AlbumPreviewRoute'; @@ -188,10 +188,10 @@ class AlbumSharedUserSelectionRoute required Set assets, List? children, }) : super( - AlbumSharedUserSelectionRoute.name, - args: AlbumSharedUserSelectionRouteArgs(key: key, assets: assets), - initialChildren: children, - ); + AlbumSharedUserSelectionRoute.name, + args: AlbumSharedUserSelectionRouteArgs(key: key, assets: assets), + initialChildren: children, + ); static const String name = 'AlbumSharedUserSelectionRoute'; @@ -225,10 +225,10 @@ class AlbumViewerRoute extends PageRouteInfo { required int albumId, List? children, }) : super( - AlbumViewerRoute.name, - args: AlbumViewerRouteArgs(key: key, albumId: albumId), - initialChildren: children, - ); + AlbumViewerRoute.name, + args: AlbumViewerRouteArgs(key: key, albumId: albumId), + initialChildren: children, + ); static const String name = 'AlbumViewerRoute'; @@ -258,7 +258,7 @@ class AlbumViewerRouteArgs { /// [AlbumsPage] class AlbumsRoute extends PageRouteInfo { const AlbumsRoute({List? children}) - : super(AlbumsRoute.name, initialChildren: children); + : super(AlbumsRoute.name, initialChildren: children); static const String name = 'AlbumsRoute'; @@ -274,7 +274,7 @@ class AlbumsRoute extends PageRouteInfo { /// [AllMotionPhotosPage] class AllMotionPhotosRoute extends PageRouteInfo { const AllMotionPhotosRoute({List? children}) - : super(AllMotionPhotosRoute.name, initialChildren: children); + : super(AllMotionPhotosRoute.name, initialChildren: children); static const String name = 'AllMotionPhotosRoute'; @@ -290,7 +290,7 @@ class AllMotionPhotosRoute extends PageRouteInfo { /// [AllPeoplePage] class AllPeopleRoute extends PageRouteInfo { const AllPeopleRoute({List? children}) - : super(AllPeopleRoute.name, initialChildren: children); + : super(AllPeopleRoute.name, initialChildren: children); static const String name = 'AllPeopleRoute'; @@ -306,7 +306,7 @@ class AllPeopleRoute extends PageRouteInfo { /// [AllPlacesPage] class AllPlacesRoute extends PageRouteInfo { const AllPlacesRoute({List? children}) - : super(AllPlacesRoute.name, initialChildren: children); + : super(AllPlacesRoute.name, initialChildren: children); static const String name = 'AllPlacesRoute'; @@ -322,7 +322,7 @@ class AllPlacesRoute extends PageRouteInfo { /// [AllVideosPage] class AllVideosRoute extends PageRouteInfo { const AllVideosRoute({List? children}) - : super(AllVideosRoute.name, initialChildren: children); + : super(AllVideosRoute.name, initialChildren: children); static const String name = 'AllVideosRoute'; @@ -342,10 +342,10 @@ class AppLogDetailRoute extends PageRouteInfo { required LogMessage logMessage, List? children, }) : super( - AppLogDetailRoute.name, - args: AppLogDetailRouteArgs(key: key, logMessage: logMessage), - initialChildren: children, - ); + AppLogDetailRoute.name, + args: AppLogDetailRouteArgs(key: key, logMessage: logMessage), + initialChildren: children, + ); static const String name = 'AppLogDetailRoute'; @@ -375,7 +375,7 @@ class AppLogDetailRouteArgs { /// [AppLogPage] class AppLogRoute extends PageRouteInfo { const AppLogRoute({List? children}) - : super(AppLogRoute.name, initialChildren: children); + : super(AppLogRoute.name, initialChildren: children); static const String name = 'AppLogRoute'; @@ -391,7 +391,7 @@ class AppLogRoute extends PageRouteInfo { /// [ArchivePage] class ArchiveRoute extends PageRouteInfo { const ArchiveRoute({List? children}) - : super(ArchiveRoute.name, initialChildren: children); + : super(ArchiveRoute.name, initialChildren: children); static const String name = 'ArchiveRoute'; @@ -410,16 +410,18 @@ class AssetViewerRoute extends PageRouteInfo { Key? key, required int initialIndex, required TimelineService timelineService, + int? heroOffset, List? children, }) : super( - AssetViewerRoute.name, - args: AssetViewerRouteArgs( - key: key, - initialIndex: initialIndex, - timelineService: timelineService, - ), - initialChildren: children, - ); + AssetViewerRoute.name, + args: AssetViewerRouteArgs( + key: key, + initialIndex: initialIndex, + timelineService: timelineService, + heroOffset: heroOffset, + ), + initialChildren: children, + ); static const String name = 'AssetViewerRoute'; @@ -431,6 +433,7 @@ class AssetViewerRoute extends PageRouteInfo { key: args.key, initialIndex: args.initialIndex, timelineService: args.timelineService, + heroOffset: args.heroOffset, ); }, ); @@ -441,6 +444,7 @@ class AssetViewerRouteArgs { this.key, required this.initialIndex, required this.timelineService, + this.heroOffset, }); final Key? key; @@ -449,9 +453,11 @@ class AssetViewerRouteArgs { final TimelineService timelineService; + final int? heroOffset; + @override String toString() { - return 'AssetViewerRouteArgs{key: $key, initialIndex: $initialIndex, timelineService: $timelineService}'; + return 'AssetViewerRouteArgs{key: $key, initialIndex: $initialIndex, timelineService: $timelineService, heroOffset: $heroOffset}'; } } @@ -459,7 +465,7 @@ class AssetViewerRouteArgs { /// [BackupAlbumSelectionPage] class BackupAlbumSelectionRoute extends PageRouteInfo { const BackupAlbumSelectionRoute({List? children}) - : super(BackupAlbumSelectionRoute.name, initialChildren: children); + : super(BackupAlbumSelectionRoute.name, initialChildren: children); static const String name = 'BackupAlbumSelectionRoute'; @@ -475,7 +481,7 @@ class BackupAlbumSelectionRoute extends PageRouteInfo { /// [BackupControllerPage] class BackupControllerRoute extends PageRouteInfo { const BackupControllerRoute({List? children}) - : super(BackupControllerRoute.name, initialChildren: children); + : super(BackupControllerRoute.name, initialChildren: children); static const String name = 'BackupControllerRoute'; @@ -491,7 +497,7 @@ class BackupControllerRoute extends PageRouteInfo { /// [BackupOptionsPage] class BackupOptionsRoute extends PageRouteInfo { const BackupOptionsRoute({List? children}) - : super(BackupOptionsRoute.name, initialChildren: children); + : super(BackupOptionsRoute.name, initialChildren: children); static const String name = 'BackupOptionsRoute'; @@ -503,6 +509,22 @@ class BackupOptionsRoute extends PageRouteInfo { ); } +/// generated route for +/// [BetaSyncSettingsPage] +class BetaSyncSettingsRoute extends PageRouteInfo { + const BetaSyncSettingsRoute({List? children}) + : super(BetaSyncSettingsRoute.name, initialChildren: children); + + static const String name = 'BetaSyncSettingsRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + return const BetaSyncSettingsPage(); + }, + ); +} + /// generated route for /// [ChangeExperiencePage] class ChangeExperienceRoute extends PageRouteInfo { @@ -511,13 +533,13 @@ class ChangeExperienceRoute extends PageRouteInfo { required bool switchingToBeta, List? children, }) : super( - ChangeExperienceRoute.name, - args: ChangeExperienceRouteArgs( - key: key, - switchingToBeta: switchingToBeta, - ), - initialChildren: children, - ); + ChangeExperienceRoute.name, + args: ChangeExperienceRouteArgs( + key: key, + switchingToBeta: switchingToBeta, + ), + initialChildren: children, + ); static const String name = 'ChangeExperienceRoute'; @@ -550,7 +572,7 @@ class ChangeExperienceRouteArgs { /// [ChangePasswordPage] class ChangePasswordRoute extends PageRouteInfo { const ChangePasswordRoute({List? children}) - : super(ChangePasswordRoute.name, initialChildren: children); + : super(ChangePasswordRoute.name, initialChildren: children); static const String name = 'ChangePasswordRoute'; @@ -570,10 +592,10 @@ class CreateAlbumRoute extends PageRouteInfo { List? assets, List? children, }) : super( - CreateAlbumRoute.name, - args: CreateAlbumRouteArgs(key: key, assets: assets), - initialChildren: children, - ); + CreateAlbumRoute.name, + args: CreateAlbumRouteArgs(key: key, assets: assets), + initialChildren: children, + ); static const String name = 'CreateAlbumRoute'; @@ -610,10 +632,10 @@ class CropImageRoute extends PageRouteInfo { required Asset asset, List? children, }) : super( - CropImageRoute.name, - args: CropImageRouteArgs(key: key, image: image, asset: asset), - initialChildren: children, - ); + CropImageRoute.name, + args: CropImageRouteArgs(key: key, image: image, asset: asset), + initialChildren: children, + ); static const String name = 'CropImageRoute'; @@ -649,7 +671,7 @@ class CropImageRouteArgs { /// [DriftAlbumsPage] class DriftAlbumsRoute extends PageRouteInfo { const DriftAlbumsRoute({List? children}) - : super(DriftAlbumsRoute.name, initialChildren: children); + : super(DriftAlbumsRoute.name, initialChildren: children); static const String name = 'DriftAlbumsRoute'; @@ -665,7 +687,7 @@ class DriftAlbumsRoute extends PageRouteInfo { /// [DriftArchivePage] class DriftArchiveRoute extends PageRouteInfo { const DriftArchiveRoute({List? children}) - : super(DriftArchiveRoute.name, initialChildren: children); + : super(DriftArchiveRoute.name, initialChildren: children); static const String name = 'DriftArchiveRoute'; @@ -686,13 +708,13 @@ class DriftAssetSelectionTimelineRoute Set lockedSelectionAssets = const {}, List? children, }) : super( - DriftAssetSelectionTimelineRoute.name, - args: DriftAssetSelectionTimelineRouteArgs( - key: key, - lockedSelectionAssets: lockedSelectionAssets, - ), - initialChildren: children, - ); + DriftAssetSelectionTimelineRoute.name, + args: DriftAssetSelectionTimelineRouteArgs( + key: key, + lockedSelectionAssets: lockedSelectionAssets, + ), + initialChildren: children, + ); static const String name = 'DriftAssetSelectionTimelineRoute'; @@ -726,11 +748,59 @@ class DriftAssetSelectionTimelineRouteArgs { } } +/// generated route for +/// [DriftBackupAlbumSelectionPage] +class DriftBackupAlbumSelectionRoute extends PageRouteInfo { + const DriftBackupAlbumSelectionRoute({List? children}) + : super(DriftBackupAlbumSelectionRoute.name, initialChildren: children); + + static const String name = 'DriftBackupAlbumSelectionRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + return const DriftBackupAlbumSelectionPage(); + }, + ); +} + +/// generated route for +/// [DriftBackupOptionsPage] +class DriftBackupOptionsRoute extends PageRouteInfo { + const DriftBackupOptionsRoute({List? children}) + : super(DriftBackupOptionsRoute.name, initialChildren: children); + + static const String name = 'DriftBackupOptionsRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + return const DriftBackupOptionsPage(); + }, + ); +} + +/// generated route for +/// [DriftBackupPage] +class DriftBackupRoute extends PageRouteInfo { + const DriftBackupRoute({List? children}) + : super(DriftBackupRoute.name, initialChildren: children); + + static const String name = 'DriftBackupRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + return const DriftBackupPage(); + }, + ); +} + /// generated route for /// [DriftCreateAlbumPage] class DriftCreateAlbumRoute extends PageRouteInfo { const DriftCreateAlbumRoute({List? children}) - : super(DriftCreateAlbumRoute.name, initialChildren: children); + : super(DriftCreateAlbumRoute.name, initialChildren: children); static const String name = 'DriftCreateAlbumRoute'; @@ -746,7 +816,7 @@ class DriftCreateAlbumRoute extends PageRouteInfo { /// [DriftFavoritePage] class DriftFavoriteRoute extends PageRouteInfo { const DriftFavoriteRoute({List? children}) - : super(DriftFavoriteRoute.name, initialChildren: children); + : super(DriftFavoriteRoute.name, initialChildren: children); static const String name = 'DriftFavoriteRoute'; @@ -762,7 +832,7 @@ class DriftFavoriteRoute extends PageRouteInfo { /// [DriftLibraryPage] class DriftLibraryRoute extends PageRouteInfo { const DriftLibraryRoute({List? children}) - : super(DriftLibraryRoute.name, initialChildren: children); + : super(DriftLibraryRoute.name, initialChildren: children); static const String name = 'DriftLibraryRoute'; @@ -778,7 +848,7 @@ class DriftLibraryRoute extends PageRouteInfo { /// [DriftLocalAlbumsPage] class DriftLocalAlbumsRoute extends PageRouteInfo { const DriftLocalAlbumsRoute({List? children}) - : super(DriftLocalAlbumsRoute.name, initialChildren: children); + : super(DriftLocalAlbumsRoute.name, initialChildren: children); static const String name = 'DriftLocalAlbumsRoute'; @@ -794,7 +864,7 @@ class DriftLocalAlbumsRoute extends PageRouteInfo { /// [DriftLockedFolderPage] class DriftLockedFolderRoute extends PageRouteInfo { const DriftLockedFolderRoute({List? children}) - : super(DriftLockedFolderRoute.name, initialChildren: children); + : super(DriftLockedFolderRoute.name, initialChildren: children); static const String name = 'DriftLockedFolderRoute'; @@ -815,14 +885,14 @@ class DriftMemoryRoute extends PageRouteInfo { Key? key, List? children, }) : super( - DriftMemoryRoute.name, - args: DriftMemoryRouteArgs( - memories: memories, - memoryIndex: memoryIndex, - key: key, - ), - initialChildren: children, - ); + DriftMemoryRoute.name, + args: DriftMemoryRouteArgs( + memories: memories, + memoryIndex: memoryIndex, + key: key, + ), + initialChildren: children, + ); static const String name = 'DriftMemoryRoute'; @@ -864,13 +934,13 @@ class DriftPartnerDetailRoute extends PageRouteInfo { DriftPartnerDetailRoute({ Key? key, - required UserDto partner, + required PartnerUserDto partner, List? children, }) : super( - DriftPartnerDetailRoute.name, - args: DriftPartnerDetailRouteArgs(key: key, partner: partner), - initialChildren: children, - ); + DriftPartnerDetailRoute.name, + args: DriftPartnerDetailRouteArgs(key: key, partner: partner), + initialChildren: children, + ); static const String name = 'DriftPartnerDetailRoute'; @@ -888,7 +958,7 @@ class DriftPartnerDetailRouteArgs { final Key? key; - final UserDto partner; + final PartnerUserDto partner; @override String toString() { @@ -896,6 +966,75 @@ class DriftPartnerDetailRouteArgs { } } +/// generated route for +/// [DriftPartnerPage] +class DriftPartnerRoute extends PageRouteInfo { + const DriftPartnerRoute({List? children}) + : super(DriftPartnerRoute.name, initialChildren: children); + + static const String name = 'DriftPartnerRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + return const DriftPartnerPage(); + }, + ); +} + +/// generated route for +/// [DriftPeopleCollectionPage] +class DriftPeopleCollectionRoute extends PageRouteInfo { + const DriftPeopleCollectionRoute({List? children}) + : super(DriftPeopleCollectionRoute.name, initialChildren: children); + + static const String name = 'DriftPeopleCollectionRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + return const DriftPeopleCollectionPage(); + }, + ); +} + +/// generated route for +/// [DriftPersonPage] +class DriftPersonRoute extends PageRouteInfo { + DriftPersonRoute({ + Key? key, + required DriftPerson person, + List? children, + }) : super( + DriftPersonRoute.name, + args: DriftPersonRouteArgs(key: key, person: person), + initialChildren: children, + ); + + static const String name = 'DriftPersonRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + final args = data.argsAs(); + return DriftPersonPage(key: args.key, person: args.person); + }, + ); +} + +class DriftPersonRouteArgs { + const DriftPersonRouteArgs({this.key, required this.person}); + + final Key? key; + + final DriftPerson person; + + @override + String toString() { + return 'DriftPersonRouteArgs{key: $key, person: $person}'; + } +} + /// generated route for /// [DriftPlaceDetailPage] class DriftPlaceDetailRoute extends PageRouteInfo { @@ -904,10 +1043,10 @@ class DriftPlaceDetailRoute extends PageRouteInfo { required String place, List? children, }) : super( - DriftPlaceDetailRoute.name, - args: DriftPlaceDetailRouteArgs(key: key, place: place), - initialChildren: children, - ); + DriftPlaceDetailRoute.name, + args: DriftPlaceDetailRouteArgs(key: key, place: place), + initialChildren: children, + ); static const String name = 'DriftPlaceDetailRoute'; @@ -941,10 +1080,10 @@ class DriftPlaceRoute extends PageRouteInfo { LatLng? currentLocation, List? children, }) : super( - DriftPlaceRoute.name, - args: DriftPlaceRouteArgs(key: key, currentLocation: currentLocation), - initialChildren: children, - ); + DriftPlaceRoute.name, + args: DriftPlaceRouteArgs(key: key, currentLocation: currentLocation), + initialChildren: children, + ); static const String name = 'DriftPlaceRoute'; @@ -979,7 +1118,7 @@ class DriftPlaceRouteArgs { /// [DriftRecentlyTakenPage] class DriftRecentlyTakenRoute extends PageRouteInfo { const DriftRecentlyTakenRoute({List? children}) - : super(DriftRecentlyTakenRoute.name, initialChildren: children); + : super(DriftRecentlyTakenRoute.name, initialChildren: children); static const String name = 'DriftRecentlyTakenRoute'; @@ -999,10 +1138,10 @@ class DriftSearchRoute extends PageRouteInfo { SearchFilter? preFilter, List? children, }) : super( - DriftSearchRoute.name, - args: DriftSearchRouteArgs(key: key, preFilter: preFilter), - initialChildren: children, - ); + DriftSearchRoute.name, + args: DriftSearchRouteArgs(key: key, preFilter: preFilter), + initialChildren: children, + ); static const String name = 'DriftSearchRoute'; @@ -1034,7 +1173,7 @@ class DriftSearchRouteArgs { /// [DriftTrashPage] class DriftTrashRoute extends PageRouteInfo { const DriftTrashRoute({List? children}) - : super(DriftTrashRoute.name, initialChildren: children); + : super(DriftTrashRoute.name, initialChildren: children); static const String name = 'DriftTrashRoute'; @@ -1046,6 +1185,22 @@ class DriftTrashRoute extends PageRouteInfo { ); } +/// generated route for +/// [DriftUploadDetailPage] +class DriftUploadDetailRoute extends PageRouteInfo { + const DriftUploadDetailRoute({List? children}) + : super(DriftUploadDetailRoute.name, initialChildren: children); + + static const String name = 'DriftUploadDetailRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + return const DriftUploadDetailPage(); + }, + ); +} + /// generated route for /// [DriftUserSelectionPage] class DriftUserSelectionRoute @@ -1055,10 +1210,10 @@ class DriftUserSelectionRoute required RemoteAlbum album, List? children, }) : super( - DriftUserSelectionRoute.name, - args: DriftUserSelectionRouteArgs(key: key, album: album), - initialChildren: children, - ); + DriftUserSelectionRoute.name, + args: DriftUserSelectionRouteArgs(key: key, album: album), + initialChildren: children, + ); static const String name = 'DriftUserSelectionRoute'; @@ -1088,7 +1243,7 @@ class DriftUserSelectionRouteArgs { /// [DriftVideoPage] class DriftVideoRoute extends PageRouteInfo { const DriftVideoRoute({List? children}) - : super(DriftVideoRoute.name, initialChildren: children); + : super(DriftVideoRoute.name, initialChildren: children); static const String name = 'DriftVideoRoute'; @@ -1110,15 +1265,15 @@ class EditImageRoute extends PageRouteInfo { required bool isEdited, List? children, }) : super( - EditImageRoute.name, - args: EditImageRouteArgs( - key: key, - asset: asset, - image: image, - isEdited: isEdited, - ), - initialChildren: children, - ); + EditImageRoute.name, + args: EditImageRouteArgs( + key: key, + asset: asset, + image: image, + isEdited: isEdited, + ), + initialChildren: children, + ); static const String name = 'EditImageRoute'; @@ -1162,7 +1317,7 @@ class EditImageRouteArgs { /// [FailedBackupStatusPage] class FailedBackupStatusRoute extends PageRouteInfo { const FailedBackupStatusRoute({List? children}) - : super(FailedBackupStatusRoute.name, initialChildren: children); + : super(FailedBackupStatusRoute.name, initialChildren: children); static const String name = 'FailedBackupStatusRoute'; @@ -1178,7 +1333,7 @@ class FailedBackupStatusRoute extends PageRouteInfo { /// [FavoritesPage] class FavoritesRoute extends PageRouteInfo { const FavoritesRoute({List? children}) - : super(FavoritesRoute.name, initialChildren: children); + : super(FavoritesRoute.name, initialChildren: children); static const String name = 'FavoritesRoute'; @@ -1194,7 +1349,7 @@ class FavoritesRoute extends PageRouteInfo { /// [FeatInDevPage] class FeatInDevRoute extends PageRouteInfo { const FeatInDevRoute({List? children}) - : super(FeatInDevRoute.name, initialChildren: children); + : super(FeatInDevRoute.name, initialChildren: children); static const String name = 'FeatInDevRoute'; @@ -1215,10 +1370,10 @@ class FilterImageRoute extends PageRouteInfo { required Asset asset, List? children, }) : super( - FilterImageRoute.name, - args: FilterImageRouteArgs(key: key, image: image, asset: asset), - initialChildren: children, - ); + FilterImageRoute.name, + args: FilterImageRouteArgs(key: key, image: image, asset: asset), + initialChildren: children, + ); static const String name = 'FilterImageRoute'; @@ -1262,10 +1417,10 @@ class FolderRoute extends PageRouteInfo { RecursiveFolder? folder, List? children, }) : super( - FolderRoute.name, - args: FolderRouteArgs(key: key, folder: folder), - initialChildren: children, - ); + FolderRoute.name, + args: FolderRouteArgs(key: key, folder: folder), + initialChildren: children, + ); static const String name = 'FolderRoute'; @@ -1304,16 +1459,16 @@ class GalleryViewerRoute extends PageRouteInfo { bool showStack = false, List? children, }) : super( - GalleryViewerRoute.name, - args: GalleryViewerRouteArgs( - key: key, - renderList: renderList, - initialIndex: initialIndex, - heroOffset: heroOffset, - showStack: showStack, - ), - initialChildren: children, - ); + GalleryViewerRoute.name, + args: GalleryViewerRouteArgs( + key: key, + renderList: renderList, + initialIndex: initialIndex, + heroOffset: heroOffset, + showStack: showStack, + ), + initialChildren: children, + ); static const String name = 'GalleryViewerRoute'; @@ -1361,7 +1516,7 @@ class GalleryViewerRouteArgs { /// [HeaderSettingsPage] class HeaderSettingsRoute extends PageRouteInfo { const HeaderSettingsRoute({List? children}) - : super(HeaderSettingsRoute.name, initialChildren: children); + : super(HeaderSettingsRoute.name, initialChildren: children); static const String name = 'HeaderSettingsRoute'; @@ -1377,7 +1532,7 @@ class HeaderSettingsRoute extends PageRouteInfo { /// [LibraryPage] class LibraryRoute extends PageRouteInfo { const LibraryRoute({List? children}) - : super(LibraryRoute.name, initialChildren: children); + : super(LibraryRoute.name, initialChildren: children); static const String name = 'LibraryRoute'; @@ -1393,7 +1548,7 @@ class LibraryRoute extends PageRouteInfo { /// [LocalAlbumsPage] class LocalAlbumsRoute extends PageRouteInfo { const LocalAlbumsRoute({List? children}) - : super(LocalAlbumsRoute.name, initialChildren: children); + : super(LocalAlbumsRoute.name, initialChildren: children); static const String name = 'LocalAlbumsRoute'; @@ -1409,7 +1564,7 @@ class LocalAlbumsRoute extends PageRouteInfo { /// [LocalMediaSummaryPage] class LocalMediaSummaryRoute extends PageRouteInfo { const LocalMediaSummaryRoute({List? children}) - : super(LocalMediaSummaryRoute.name, initialChildren: children); + : super(LocalMediaSummaryRoute.name, initialChildren: children); static const String name = 'LocalMediaSummaryRoute'; @@ -1429,10 +1584,10 @@ class LocalTimelineRoute extends PageRouteInfo { required LocalAlbum album, List? children, }) : super( - LocalTimelineRoute.name, - args: LocalTimelineRouteArgs(key: key, album: album), - initialChildren: children, - ); + LocalTimelineRoute.name, + args: LocalTimelineRouteArgs(key: key, album: album), + initialChildren: children, + ); static const String name = 'LocalTimelineRoute'; @@ -1462,7 +1617,7 @@ class LocalTimelineRouteArgs { /// [LockedPage] class LockedRoute extends PageRouteInfo { const LockedRoute({List? children}) - : super(LockedRoute.name, initialChildren: children); + : super(LockedRoute.name, initialChildren: children); static const String name = 'LockedRoute'; @@ -1478,7 +1633,7 @@ class LockedRoute extends PageRouteInfo { /// [LoginPage] class LoginRoute extends PageRouteInfo { const LoginRoute({List? children}) - : super(LoginRoute.name, initialChildren: children); + : super(LoginRoute.name, initialChildren: children); static const String name = 'LoginRoute'; @@ -1494,7 +1649,7 @@ class LoginRoute extends PageRouteInfo { /// [MainTimelinePage] class MainTimelineRoute extends PageRouteInfo { const MainTimelineRoute({List? children}) - : super(MainTimelineRoute.name, initialChildren: children); + : super(MainTimelineRoute.name, initialChildren: children); static const String name = 'MainTimelineRoute'; @@ -1514,13 +1669,13 @@ class MapLocationPickerRoute extends PageRouteInfo { LatLng initialLatLng = const LatLng(0, 0), List? children, }) : super( - MapLocationPickerRoute.name, - args: MapLocationPickerRouteArgs( - key: key, - initialLatLng: initialLatLng, - ), - initialChildren: children, - ); + MapLocationPickerRoute.name, + args: MapLocationPickerRouteArgs( + key: key, + initialLatLng: initialLatLng, + ), + initialChildren: children, + ); static const String name = 'MapLocationPickerRoute'; @@ -1558,11 +1713,11 @@ class MapLocationPickerRouteArgs { /// [MapPage] class MapRoute extends PageRouteInfo { MapRoute({Key? key, LatLng? initialLocation, List? children}) - : super( - MapRoute.name, - args: MapRouteArgs(key: key, initialLocation: initialLocation), - initialChildren: children, - ); + : super( + MapRoute.name, + args: MapRouteArgs(key: key, initialLocation: initialLocation), + initialChildren: children, + ); static const String name = 'MapRoute'; @@ -1599,14 +1754,14 @@ class MemoryRoute extends PageRouteInfo { Key? key, List? children, }) : super( - MemoryRoute.name, - args: MemoryRouteArgs( - memories: memories, - memoryIndex: memoryIndex, - key: key, - ), - initialChildren: children, - ); + MemoryRoute.name, + args: MemoryRouteArgs( + memories: memories, + memoryIndex: memoryIndex, + key: key, + ), + initialChildren: children, + ); static const String name = 'MemoryRoute'; @@ -1653,16 +1808,16 @@ class NativeVideoViewerRoute extends PageRouteInfo { int playbackDelayFactor = 1, List? children, }) : super( - NativeVideoViewerRoute.name, - args: NativeVideoViewerRouteArgs( - key: key, - asset: asset, - image: image, - showControls: showControls, - playbackDelayFactor: playbackDelayFactor, - ), - initialChildren: children, - ); + NativeVideoViewerRoute.name, + args: NativeVideoViewerRouteArgs( + key: key, + asset: asset, + image: image, + showControls: showControls, + playbackDelayFactor: playbackDelayFactor, + ), + initialChildren: children, + ); static const String name = 'NativeVideoViewerRoute'; @@ -1714,10 +1869,10 @@ class PartnerDetailRoute extends PageRouteInfo { required UserDto partner, List? children, }) : super( - PartnerDetailRoute.name, - args: PartnerDetailRouteArgs(key: key, partner: partner), - initialChildren: children, - ); + PartnerDetailRoute.name, + args: PartnerDetailRouteArgs(key: key, partner: partner), + initialChildren: children, + ); static const String name = 'PartnerDetailRoute'; @@ -1747,7 +1902,7 @@ class PartnerDetailRouteArgs { /// [PartnerPage] class PartnerRoute extends PageRouteInfo { const PartnerRoute({List? children}) - : super(PartnerRoute.name, initialChildren: children); + : super(PartnerRoute.name, initialChildren: children); static const String name = 'PartnerRoute'; @@ -1763,7 +1918,7 @@ class PartnerRoute extends PageRouteInfo { /// [PeopleCollectionPage] class PeopleCollectionRoute extends PageRouteInfo { const PeopleCollectionRoute({List? children}) - : super(PeopleCollectionRoute.name, initialChildren: children); + : super(PeopleCollectionRoute.name, initialChildren: children); static const String name = 'PeopleCollectionRoute'; @@ -1779,7 +1934,7 @@ class PeopleCollectionRoute extends PageRouteInfo { /// [PermissionOnboardingPage] class PermissionOnboardingRoute extends PageRouteInfo { const PermissionOnboardingRoute({List? children}) - : super(PermissionOnboardingRoute.name, initialChildren: children); + : super(PermissionOnboardingRoute.name, initialChildren: children); static const String name = 'PermissionOnboardingRoute'; @@ -1800,14 +1955,14 @@ class PersonResultRoute extends PageRouteInfo { required String personName, List? children, }) : super( - PersonResultRoute.name, - args: PersonResultRouteArgs( - key: key, - personId: personId, - personName: personName, - ), - initialChildren: children, - ); + PersonResultRoute.name, + args: PersonResultRouteArgs( + key: key, + personId: personId, + personName: personName, + ), + initialChildren: children, + ); static const String name = 'PersonResultRoute'; @@ -1847,7 +2002,7 @@ class PersonResultRouteArgs { /// [PhotosPage] class PhotosRoute extends PageRouteInfo { const PhotosRoute({List? children}) - : super(PhotosRoute.name, initialChildren: children); + : super(PhotosRoute.name, initialChildren: children); static const String name = 'PhotosRoute'; @@ -1867,10 +2022,10 @@ class PinAuthRoute extends PageRouteInfo { bool createPinCode = false, List? children, }) : super( - PinAuthRoute.name, - args: PinAuthRouteArgs(key: key, createPinCode: createPinCode), - initialChildren: children, - ); + PinAuthRoute.name, + args: PinAuthRouteArgs(key: key, createPinCode: createPinCode), + initialChildren: children, + ); static const String name = 'PinAuthRoute'; @@ -1906,13 +2061,13 @@ class PlacesCollectionRoute extends PageRouteInfo { LatLng? currentLocation, List? children, }) : super( - PlacesCollectionRoute.name, - args: PlacesCollectionRouteArgs( - key: key, - currentLocation: currentLocation, - ), - initialChildren: children, - ); + PlacesCollectionRoute.name, + args: PlacesCollectionRouteArgs( + key: key, + currentLocation: currentLocation, + ), + initialChildren: children, + ); static const String name = 'PlacesCollectionRoute'; @@ -1947,7 +2102,7 @@ class PlacesCollectionRouteArgs { /// [RecentlyTakenPage] class RecentlyTakenRoute extends PageRouteInfo { const RecentlyTakenRoute({List? children}) - : super(RecentlyTakenRoute.name, initialChildren: children); + : super(RecentlyTakenRoute.name, initialChildren: children); static const String name = 'RecentlyTakenRoute'; @@ -1967,10 +2122,10 @@ class RemoteAlbumRoute extends PageRouteInfo { required RemoteAlbum album, List? children, }) : super( - RemoteAlbumRoute.name, - args: RemoteAlbumRouteArgs(key: key, album: album), - initialChildren: children, - ); + RemoteAlbumRoute.name, + args: RemoteAlbumRouteArgs(key: key, album: album), + initialChildren: children, + ); static const String name = 'RemoteAlbumRoute'; @@ -2000,7 +2155,7 @@ class RemoteAlbumRouteArgs { /// [RemoteMediaSummaryPage] class RemoteMediaSummaryRoute extends PageRouteInfo { const RemoteMediaSummaryRoute({List? children}) - : super(RemoteMediaSummaryRoute.name, initialChildren: children); + : super(RemoteMediaSummaryRoute.name, initialChildren: children); static const String name = 'RemoteMediaSummaryRoute'; @@ -2020,10 +2175,10 @@ class SearchRoute extends PageRouteInfo { SearchFilter? prefilter, List? children, }) : super( - SearchRoute.name, - args: SearchRouteArgs(key: key, prefilter: prefilter), - initialChildren: children, - ); + SearchRoute.name, + args: SearchRouteArgs(key: key, prefilter: prefilter), + initialChildren: children, + ); static const String name = 'SearchRoute'; @@ -2055,7 +2210,7 @@ class SearchRouteArgs { /// [SettingsPage] class SettingsRoute extends PageRouteInfo { const SettingsRoute({List? children}) - : super(SettingsRoute.name, initialChildren: children); + : super(SettingsRoute.name, initialChildren: children); static const String name = 'SettingsRoute'; @@ -2075,10 +2230,10 @@ class SettingsSubRoute extends PageRouteInfo { Key? key, List? children, }) : super( - SettingsSubRoute.name, - args: SettingsSubRouteArgs(section: section, key: key), - initialChildren: children, - ); + SettingsSubRoute.name, + args: SettingsSubRouteArgs(section: section, key: key), + initialChildren: children, + ); static const String name = 'SettingsSubRoute'; @@ -2112,10 +2267,10 @@ class ShareIntentRoute extends PageRouteInfo { required List attachments, List? children, }) : super( - ShareIntentRoute.name, - args: ShareIntentRouteArgs(key: key, attachments: attachments), - initialChildren: children, - ); + ShareIntentRoute.name, + args: ShareIntentRouteArgs(key: key, attachments: attachments), + initialChildren: children, + ); static const String name = 'ShareIntentRoute'; @@ -2151,15 +2306,15 @@ class SharedLinkEditRoute extends PageRouteInfo { String? albumId, List? children, }) : super( - SharedLinkEditRoute.name, - args: SharedLinkEditRouteArgs( - key: key, - existingLink: existingLink, - assetsList: assetsList, - albumId: albumId, - ), - initialChildren: children, - ); + SharedLinkEditRoute.name, + args: SharedLinkEditRouteArgs( + key: key, + existingLink: existingLink, + assetsList: assetsList, + albumId: albumId, + ), + initialChildren: children, + ); static const String name = 'SharedLinkEditRoute'; @@ -2205,7 +2360,7 @@ class SharedLinkEditRouteArgs { /// [SharedLinkPage] class SharedLinkRoute extends PageRouteInfo { const SharedLinkRoute({List? children}) - : super(SharedLinkRoute.name, initialChildren: children); + : super(SharedLinkRoute.name, initialChildren: children); static const String name = 'SharedLinkRoute'; @@ -2221,7 +2376,7 @@ class SharedLinkRoute extends PageRouteInfo { /// [SplashScreenPage] class SplashScreenRoute extends PageRouteInfo { const SplashScreenRoute({List? children}) - : super(SplashScreenRoute.name, initialChildren: children); + : super(SplashScreenRoute.name, initialChildren: children); static const String name = 'SplashScreenRoute'; @@ -2237,7 +2392,7 @@ class SplashScreenRoute extends PageRouteInfo { /// [TabControllerPage] class TabControllerRoute extends PageRouteInfo { const TabControllerRoute({List? children}) - : super(TabControllerRoute.name, initialChildren: children); + : super(TabControllerRoute.name, initialChildren: children); static const String name = 'TabControllerRoute'; @@ -2253,7 +2408,7 @@ class TabControllerRoute extends PageRouteInfo { /// [TabShellPage] class TabShellRoute extends PageRouteInfo { const TabShellRoute({List? children}) - : super(TabShellRoute.name, initialChildren: children); + : super(TabShellRoute.name, initialChildren: children); static const String name = 'TabShellRoute'; @@ -2269,7 +2424,7 @@ class TabShellRoute extends PageRouteInfo { /// [TrashPage] class TrashRoute extends PageRouteInfo { const TrashRoute({List? children}) - : super(TrashRoute.name, initialChildren: children); + : super(TrashRoute.name, initialChildren: children); static const String name = 'TrashRoute'; diff --git a/mobile/lib/services/action.service.dart b/mobile/lib/services/action.service.dart index adefd5da16..9a12745acd 100644 --- a/mobile/lib/services/action.service.dart +++ b/mobile/lib/services/action.service.dart @@ -1,3 +1,5 @@ +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/repositories/download.repository.dart'; import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:immich_mobile/constants/enums.dart'; @@ -11,6 +13,7 @@ import 'package:immich_mobile/repositories/asset_api.repository.dart'; import 'package:immich_mobile/repositories/asset_media.repository.dart'; import 'package:immich_mobile/repositories/drift_album_api_repository.dart'; import 'package:immich_mobile/routing/router.dart'; +import 'package:immich_mobile/widgets/common/date_time_picker.dart'; import 'package:immich_mobile/widgets/common/location_picker.dart'; import 'package:maplibre_gl/maplibre_gl.dart' as maplibre; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -23,6 +26,7 @@ final actionServiceProvider = Provider( ref.watch(driftAlbumApiRepositoryProvider), ref.watch(remoteAlbumRepository), ref.watch(assetMediaRepositoryProvider), + ref.watch(downloadRepositoryProvider), ), ); @@ -33,6 +37,7 @@ class ActionService { final DriftAlbumApiRepository _albumApiRepository; final DriftRemoteAlbumRepository _remoteAlbumRepository; final AssetMediaRepository _assetMediaRepository; + final DownloadRepository _downloadRepository; const ActionService( this._assetApiRepository, @@ -41,14 +46,11 @@ class ActionService { this._albumApiRepository, this._remoteAlbumRepository, this._assetMediaRepository, + this._downloadRepository, ); Future shareLink(List remoteIds, BuildContext context) async { - context.pushRoute( - SharedLinkEditRoute( - assetsList: remoteIds, - ), - ); + context.pushRoute(SharedLinkEditRoute(assetsList: remoteIds)); } Future favorite(List remoteIds) async { @@ -62,47 +64,32 @@ class ActionService { } Future archive(List remoteIds) async { - await _assetApiRepository.updateVisibility( - remoteIds, - AssetVisibilityEnum.archive, - ); - await _remoteAssetRepository.updateVisibility( - remoteIds, - AssetVisibility.archive, - ); + await _assetApiRepository.updateVisibility(remoteIds, AssetVisibilityEnum.archive); + await _remoteAssetRepository.updateVisibility(remoteIds, AssetVisibility.archive); } Future unArchive(List remoteIds) async { - await _assetApiRepository.updateVisibility( - remoteIds, - AssetVisibilityEnum.timeline, - ); - await _remoteAssetRepository.updateVisibility( - remoteIds, - AssetVisibility.timeline, - ); + await _assetApiRepository.updateVisibility(remoteIds, AssetVisibilityEnum.timeline); + await _remoteAssetRepository.updateVisibility(remoteIds, AssetVisibility.timeline); } - Future moveToLockFolder(List remoteIds) async { - await _assetApiRepository.updateVisibility( - remoteIds, - AssetVisibilityEnum.locked, - ); - await _remoteAssetRepository.updateVisibility( - remoteIds, - AssetVisibility.locked, - ); + Future moveToLockFolder(List remoteIds, List localIds) async { + await _assetApiRepository.updateVisibility(remoteIds, AssetVisibilityEnum.locked); + await _remoteAssetRepository.updateVisibility(remoteIds, AssetVisibility.locked); + + // Ask user if they want to delete local copies + if (localIds.isNotEmpty) { + final deletedIds = await _assetMediaRepository.deleteAll(localIds); + + if (deletedIds.isNotEmpty) { + await _localAssetRepository.delete(deletedIds); + } + } } Future removeFromLockFolder(List remoteIds) async { - await _assetApiRepository.updateVisibility( - remoteIds, - AssetVisibilityEnum.timeline, - ); - await _remoteAssetRepository.updateVisibility( - remoteIds, - AssetVisibility.timeline, - ); + await _assetApiRepository.updateVisibility(remoteIds, AssetVisibilityEnum.timeline); + await _remoteAssetRepository.updateVisibility(remoteIds, AssetVisibility.timeline); } Future trash(List remoteIds) async { @@ -110,20 +97,48 @@ class ActionService { await _remoteAssetRepository.trash(remoteIds); } - Future delete(List remoteIds) async { + Future restoreTrash(List ids) async { + await _assetApiRepository.restoreTrash(ids); + await _remoteAssetRepository.restoreTrash(ids); + } + + Future trashRemoteAndDeleteLocal(List remoteIds, List localIds) async { + await _assetApiRepository.delete(remoteIds, false); + await _remoteAssetRepository.trash(remoteIds); + + if (localIds.isNotEmpty) { + final deletedIds = await _assetMediaRepository.deleteAll(localIds); + + if (deletedIds.isNotEmpty) { + await _localAssetRepository.delete(deletedIds); + } + } + } + + Future deleteRemoteAndLocal(List remoteIds, List localIds) async { await _assetApiRepository.delete(remoteIds, true); await _remoteAssetRepository.delete(remoteIds); + + if (localIds.isNotEmpty) { + final deletedIds = await _assetMediaRepository.deleteAll(localIds); + + if (deletedIds.isNotEmpty) { + await _localAssetRepository.delete(deletedIds); + } + } } - Future deleteLocal(List localIds) async { - await _assetMediaRepository.deleteAll(localIds); - await _localAssetRepository.delete(localIds); + Future deleteLocal(List localIds) async { + final deletedIds = await _assetMediaRepository.deleteAll(localIds); + if (deletedIds.isNotEmpty) { + await _localAssetRepository.delete(deletedIds); + return deletedIds.length; + } + + return 0; } - Future editLocation( - List remoteIds, - BuildContext context, - ) async { + Future editLocation(List remoteIds, BuildContext context) async { maplibre.LatLng? initialLatLng; if (remoteIds.length == 1) { final exif = await _remoteAssetRepository.getExif(remoteIds[0]); @@ -133,24 +148,53 @@ class ActionService { } } - final location = await showLocationPicker( - context: context, - initialLatLng: initialLatLng, - ); + final location = await showLocationPicker(context: context, initialLatLng: initialLatLng); if (location == null) { return false; } - await _assetApiRepository.updateLocation( - remoteIds, - location, - ); - await _remoteAssetRepository.updateLocation( - remoteIds, - location, + await _assetApiRepository.updateLocation(remoteIds, location); + await _remoteAssetRepository.updateLocation(remoteIds, location); + + return true; + } + + Future editDateTime(List remoteIds, BuildContext context) async { + DateTime? initialDate; + String? timeZone; + Duration? offset; + + if (remoteIds.length == 1) { + final assetId = remoteIds.first; + final asset = await _remoteAssetRepository.get(assetId); + if (asset == null) { + return false; + } + + final exifData = await _remoteAssetRepository.getExif(assetId); + initialDate = asset.createdAt.toLocal(); + offset = initialDate.timeZoneOffset; + timeZone = exifData?.timeZone; + } + + final dateTime = await showDateTimePicker( + context: context, + initialDateTime: initialDate, + initialTZ: timeZone, + initialTZOffset: offset, ); + if (dateTime == null) { + return false; + } + + // convert dateTime to DateTime object + final parsedDateTime = DateTime.parse(dateTime); + + await _assetApiRepository.updateDateTime(remoteIds, parsedDateTime); + await _remoteAssetRepository.updateDateTime(remoteIds, parsedDateTime); + return true; } @@ -159,13 +203,20 @@ class ActionService { final result = await _albumApiRepository.removeAssets(albumId, remoteIds); if (result.removed.isNotEmpty) { - removedCount = - await _remoteAlbumRepository.removeAssets(albumId, result.removed); + removedCount = await _remoteAlbumRepository.removeAssets(albumId, result.removed); } return removedCount; } + Future updateDescription(String assetId, String description) async { + // update remote first, then local to ensure consistency + await _assetApiRepository.updateDescription(assetId, description); + await _remoteAssetRepository.updateDescription(assetId, description); + + return true; + } + Future stack(String userId, List remoteIds) async { final stack = await _assetApiRepository.stack(remoteIds); await _remoteAssetRepository.stack(userId, stack); @@ -179,4 +230,8 @@ class ActionService { Future shareAssets(List assets) { return _assetMediaRepository.shareAssets(assets); } + + Future> downloadAll(List assets) { + return _downloadRepository.downloadAllAssets(assets); + } } diff --git a/mobile/lib/services/activity.service.dart b/mobile/lib/services/activity.service.dart index 611d985afe..7a0a092e7d 100644 --- a/mobile/lib/services/activity.service.dart +++ b/mobile/lib/services/activity.service.dart @@ -11,10 +11,7 @@ class ActivityService with ErrorLoggerMixin { ActivityService(this._activityApiRepository); - Future> getAllActivities( - String albumId, { - String? assetId, - }) async { + Future> getAllActivities(String albumId, {String? assetId}) async { return logError( () => _activityApiRepository.getAll(albumId, assetId: assetId), defaultValue: [], @@ -41,19 +38,9 @@ class ActivityService with ErrorLoggerMixin { ); } - AsyncFuture addActivity( - String albumId, - ActivityType type, { - String? assetId, - String? comment, - }) async { + AsyncFuture addActivity(String albumId, ActivityType type, {String? assetId, String? comment}) async { return guardError( - () => _activityApiRepository.create( - albumId, - type, - assetId: assetId, - comment: comment, - ), + () => _activityApiRepository.create(albumId, type, assetId: assetId, comment: comment), errorMessage: "Failed to create $type for album $albumId", ); } diff --git a/mobile/lib/services/album.service.dart b/mobile/lib/services/album.service.dart index 6733ec41b2..454f652035 100644 --- a/mobile/lib/services/album.service.dart +++ b/mobile/lib/services/album.service.dart @@ -11,8 +11,7 @@ import 'package:immich_mobile/domain/services/user.service.dart'; import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/backup_album.entity.dart'; -import 'package:immich_mobile/infrastructure/entities/user.entity.dart' - as entity; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart' as entity; import 'package:immich_mobile/models/albums/album_add_asset_response.model.dart'; import 'package:immich_mobile/models/albums/album_search.model.dart'; import 'package:immich_mobile/providers/infrastructure/user.provider.dart'; @@ -76,13 +75,9 @@ class AlbumService { bool changes = false; try { final (selectedIds, excludedIds, onDevice) = await ( - _backupAlbumRepository - .getIdsBySelection(BackupSelection.select) - .then((value) => value.toSet()), - _backupAlbumRepository - .getIdsBySelection(BackupSelection.exclude) - .then((value) => value.toSet()), - _albumMediaRepository.getAll() + _backupAlbumRepository.getIdsBySelection(BackupSelection.select).then((value) => value.toSet()), + _backupAlbumRepository.getIdsBySelection(BackupSelection.exclude).then((value) => value.toSet()), + _albumMediaRepository.getAll(), ).wait; _log.info("Found ${onDevice.length} device albums"); if (selectedIds.isEmpty) { @@ -107,9 +102,7 @@ class AlbumService { } // remove all excluded albums onDevice.removeWhere((e) => excludedIds.contains(e.localId)); - _log.info( - "Ignoring ${excludedIds.length} excluded albums resulting in ${onDevice.length} device albums", - ); + _log.info("Ignoring ${excludedIds.length} excluded albums resulting in ${onDevice.length} device albums"); } final allAlbum = onDevice.firstWhereOrNull((album) => album.isAll); @@ -126,8 +119,7 @@ class AlbumService { onDevice.removeWhere((album) => !selectedIds.contains(album.localId)); _log.info("'Recents' is not selected, keeping only selected albums"); } - changes = - await _syncService.syncLocalAlbumAssetsToDb(onDevice, excludedAssets); + changes = await _syncService.syncLocalAlbumAssetsToDb(onDevice, excludedAssets); _log.info("Syncing completed. Changes: $changes"); } finally { _localCompleter.complete(changes); @@ -136,20 +128,11 @@ class AlbumService { return changes; } - Future> _loadExcludedAssetIds( - List albums, - Set excludedAlbumIds, - ) async { + Future> _loadExcludedAssetIds(List albums, Set excludedAlbumIds) async { final Set result = HashSet(); - for (final batchAlbums in albums - .where((album) => excludedAlbumIds.contains(album.localId)) - .slices(5)) { + for (final batchAlbums in albums.where((album) => excludedAlbumIds.contains(album.localId)).slices(5)) { await batchAlbums - .map( - (album) => _albumMediaRepository - .getAssetIds(album.localId!) - .then((assetIds) => result.addAll(assetIds)), - ) + .map((album) => _albumMediaRepository.getAssetIds(album.localId!).then((assetIds) => result.addAll(assetIds))) .wait; } return result; @@ -177,13 +160,10 @@ class AlbumService { _albumApiRepository.getAll(shared: true), // Passing null (or nothing) for `shared` returns only albums that // explicitly belong to us - _albumApiRepository.getAll(shared: null) + _albumApiRepository.getAll(shared: null), ).wait; - final albums = HashSet( - equals: (a, b) => a.remoteId == b.remoteId, - hashCode: (a) => a.remoteId.hashCode, - ); + final albums = HashSet(equals: (a, b) => a.remoteId == b.remoteId, hashCode: (a) => a.remoteId.hashCode); albums.addAll(sharedAlbum); albums.addAll(ownedAlbum); @@ -215,7 +195,7 @@ class AlbumService { */ Future _getNextAlbumName() async { const baseName = "Untitled"; - for (int round = 0;; round++) { + for (int round = 0; ; round++) { final proposedName = "$baseName${round == 0 ? "" : " ($round)"}"; if (null == await _albumRepository.getByName(proposedName, owner: true)) { @@ -224,25 +204,13 @@ class AlbumService { } } - Future createAlbumWithGeneratedName( - Iterable assets, - ) async { - return createAlbum( - await _getNextAlbumName(), - assets, - [], - ); + Future createAlbumWithGeneratedName(Iterable assets) async { + return createAlbum(await _getNextAlbumName(), assets, []); } - Future addAssets( - Album album, - Iterable assets, - ) async { + Future addAssets(Album album, Iterable assets) async { try { - final result = await _albumApiRepository.addAssets( - album.remoteId!, - assets.map((asset) => asset.remoteId!), - ); + final result = await _albumApiRepository.addAssets(album.remoteId!, assets.map((asset) => asset.remoteId!)); final List addedAssets = result.added .map((id) => assets.firstWhere((asset) => asset.remoteId == id)) @@ -250,21 +218,14 @@ class AlbumService { await _updateAssets(album.id, add: addedAssets); - return AlbumAddAssetsResponse( - alreadyInAlbum: result.duplicates, - successfullyAdded: addedAssets.length, - ); + return AlbumAddAssetsResponse(alreadyInAlbum: result.duplicates, successfullyAdded: addedAssets.length); } catch (e) { debugPrint("Error addAssets ${e.toString()}"); } return null; } - Future _updateAssets( - int albumId, { - List add = const [], - List remove = const [], - }) => + Future _updateAssets(int albumId, {List add = const [], List remove = const []}) => _albumRepository.transaction(() async { final album = await _albumRepository.get(albumId); if (album == null) return; @@ -276,10 +237,7 @@ class AlbumService { Future setActivityStatus(Album album, bool enabled) async { try { - final updatedAlbum = await _albumApiRepository.update( - album.remoteId!, - activityEnabled: enabled, - ); + final updatedAlbum = await _albumApiRepository.update(album.remoteId!, activityEnabled: enabled); album.activityEnabled = updatedAlbum.activityEnabled; await _albumRepository.update(album); return true; @@ -296,19 +254,15 @@ class AlbumService { await _albumApiRepository.delete(album.remoteId!); } if (album.shared) { - final foreignAssets = - await _assetRepository.getByAlbum(album, notOwnedBy: [userId]); + final foreignAssets = await _assetRepository.getByAlbum(album, notOwnedBy: [userId]); await _albumRepository.delete(album.id); final List albums = await _albumRepository.getAll(shared: true); final List existing = []; for (Album album in albums) { - existing.addAll( - await _assetRepository.getByAlbum(album, notOwnedBy: [userId]), - ); + existing.addAll(await _assetRepository.getByAlbum(album, notOwnedBy: [userId])); } - final List idsToRemove = - _syncService.sharedAssetsToRemove(foreignAssets, existing); + final List idsToRemove = _syncService.sharedAssetsToRemove(foreignAssets, existing); if (idsToRemove.isNotEmpty) { await _assetRepository.deleteByIds(idsToRemove); } @@ -332,17 +286,10 @@ class AlbumService { } } - Future removeAsset( - Album album, - Iterable assets, - ) async { + Future removeAsset(Album album, Iterable assets) async { try { - final result = await _albumApiRepository.removeAssets( - album.remoteId!, - assets.map((asset) => asset.remoteId!), - ); - final toRemove = result.removed - .map((id) => assets.firstWhere((asset) => asset.remoteId == id)); + final result = await _albumApiRepository.removeAssets(album.remoteId!, assets.map((asset) => asset.remoteId!)); + final toRemove = result.removed.map((id) => assets.firstWhere((asset) => asset.remoteId == id)); await _updateAssets(album.id, remove: toRemove.toList()); return true; } catch (e) { @@ -351,15 +298,9 @@ class AlbumService { return false; } - Future removeUser( - Album album, - UserDto user, - ) async { + Future removeUser(Album album, UserDto user) async { try { - await _albumApiRepository.removeUser( - album.remoteId!, - userId: user.id, - ); + await _albumApiRepository.removeUser(album.remoteId!, userId: user.id); album.sharedUsers.remove(entity.User.fromDto(user)); await _albumRepository.removeUsers(album, [user]); @@ -374,21 +315,14 @@ class AlbumService { } } - Future addUsers( - Album album, - List userIds, - ) async { + Future addUsers(Album album, List userIds) async { try { - final updatedAlbum = - await _albumApiRepository.addUsers(album.remoteId!, userIds); + final updatedAlbum = await _albumApiRepository.addUsers(album.remoteId!, userIds); album.sharedUsers.addAll(updatedAlbum.remoteUsers); album.shared = true; - await _albumRepository.addUsers( - album, - album.sharedUsers.map((u) => u.toDto()).toList(), - ); + await _albumRepository.addUsers(album, album.sharedUsers.map((u) => u.toDto()).toList()); await _albumRepository.update(album); return true; @@ -398,15 +332,9 @@ class AlbumService { return false; } - Future changeTitleAlbum( - Album album, - String newAlbumTitle, - ) async { + Future changeTitleAlbum(Album album, String newAlbumTitle) async { try { - final updatedAlbum = await _albumApiRepository.update( - album.remoteId!, - name: newAlbumTitle, - ); + final updatedAlbum = await _albumApiRepository.update(album.remoteId!, name: newAlbumTitle); album.name = updatedAlbum.name; await _albumRepository.update(album); @@ -417,15 +345,9 @@ class AlbumService { } } - Future changeDescriptionAlbum( - Album album, - String newAlbumDescription, - ) async { + Future changeDescriptionAlbum(Album album, String newAlbumDescription) async { try { - final updatedAlbum = await _albumApiRepository.update( - album.remoteId!, - description: newAlbumDescription, - ); + final updatedAlbum = await _albumApiRepository.update(album.remoteId!, description: newAlbumDescription); album.description = updatedAlbum.description; await _albumRepository.update(album); @@ -436,26 +358,13 @@ class AlbumService { } } - Future getAlbumByName( - String name, { - bool? remote, - bool? shared, - bool? owner, - }) => - _albumRepository.getByName( - name, - remote: remote, - shared: shared, - owner: owner, - ); + Future getAlbumByName(String name, {bool? remote, bool? shared, bool? owner}) => + _albumRepository.getByName(name, remote: remote, shared: shared, owner: owner); /// /// Add the uploaded asset to the selected albums /// - Future syncUploadAlbums( - List albumNames, - List assetIds, - ) async { + Future syncUploadAlbums(List albumNames, List assetIds) async { for (final albumName in albumNames) { Album? album = await getAlbumByName(albumName, remote: true, owner: true); album ??= await createAlbum(albumName, []); @@ -494,17 +403,13 @@ class AlbumService { return _albumRepository.watchAlbum(id); } - Future> search( - String searchTerm, - QuickFilterMode filterMode, - ) async { + Future> search(String searchTerm, QuickFilterMode filterMode) async { return _albumRepository.search(searchTerm, filterMode); } Future updateSortOrder(Album album, SortOrder order) async { try { - final updateAlbum = - await _albumApiRepository.update(album.remoteId!, sortOrder: order); + final updateAlbum = await _albumApiRepository.update(album.remoteId!, sortOrder: order); album.sortOrder = updateAlbum.sortOrder; return _albumRepository.update(album); diff --git a/mobile/lib/services/api.service.dart b/mobile/lib/services/api.service.dart index fe007a2aab..fca9080c86 100644 --- a/mobile/lib/services/api.service.dart +++ b/mobile/lib/services/api.service.dart @@ -127,11 +127,7 @@ class ApiService implements Authentication { } on SocketException catch (_) { return false; } catch (error, stackTrace) { - _log.severe( - "Error while checking server availability", - error, - stackTrace, - ); + _log.severe("Error while checking server availability", error, stackTrace); return false; } return true; @@ -145,10 +141,7 @@ class ApiService implements Authentication { headers.addAll(getRequestHeaders()); final res = await client - .get( - Uri.parse("$baseUrl/.well-known/immich"), - headers: headers, - ) + .get(Uri.parse("$baseUrl/.well-known/immich"), headers: headers) .timeout(const Duration(seconds: 5)); if (res.statusCode == 200) { @@ -178,13 +171,11 @@ class ApiService implements Authentication { if (Platform.isIOS) { final iosInfo = await deviceInfoPlugin.iosInfo; - authenticationApi.apiClient - .addDefaultHeader('deviceModel', iosInfo.utsname.machine); + authenticationApi.apiClient.addDefaultHeader('deviceModel', iosInfo.utsname.machine); authenticationApi.apiClient.addDefaultHeader('deviceType', 'iOS'); } else if (Platform.isAndroid) { final androidInfo = await deviceInfoPlugin.androidInfo; - authenticationApi.apiClient - .addDefaultHeader('deviceModel', androidInfo.model); + authenticationApi.apiClient.addDefaultHeader('deviceModel', androidInfo.model); authenticationApi.apiClient.addDefaultHeader('deviceType', 'Android'); } else { authenticationApi.apiClient.addDefaultHeader('deviceModel', 'Unknown'); @@ -213,10 +204,7 @@ class ApiService implements Authentication { } @override - Future applyToParams( - List queryParams, - Map headerParams, - ) { + Future applyToParams(List queryParams, Map headerParams) { return Future(() { var headers = ApiService.getRequestHeaders(); headerParams.addAll(headers); diff --git a/mobile/lib/services/app_settings.service.dart b/mobile/lib/services/app_settings.service.dart index 982dab1cae..8a4b0c6719 100644 --- a/mobile/lib/services/app_settings.service.dart +++ b/mobile/lib/services/app_settings.service.dart @@ -5,26 +5,10 @@ import 'package:immich_mobile/entities/store.entity.dart'; enum AppSettingsEnum { loadPreview(StoreKey.loadPreview, "loadPreview", true), loadOriginal(StoreKey.loadOriginal, "loadOriginal", false), - themeMode( - StoreKey.themeMode, - "themeMode", - "system", - ), // "light","dark","system" - primaryColor( - StoreKey.primaryColor, - "primaryColor", - defaultColorPresetName, - ), - dynamicTheme( - StoreKey.dynamicTheme, - "dynamicTheme", - false, - ), - colorfulInterface( - StoreKey.colorfulInterface, - "colorfulInterface", - true, - ), + themeMode(StoreKey.themeMode, "themeMode", "system"), // "light","dark","system" + primaryColor(StoreKey.primaryColor, "primaryColor", defaultColorPresetName), + dynamicTheme(StoreKey.dynamicTheme, "dynamicTheme", false), + colorfulInterface(StoreKey.colorfulInterface, "colorfulInterface", true), tilesPerRow(StoreKey.tilesPerRow, "tilesPerRow", 4), dynamicLayout(StoreKey.dynamicLayout, "dynamicLayout", false), groupAssetsBy(StoreKey.groupAssetsBy, "groupBy", 0), @@ -33,43 +17,23 @@ enum AppSettingsEnum { "uploadErrorNotificationGracePeriod", 2, ), - backgroundBackupTotalProgress( - StoreKey.backgroundBackupTotalProgress, - "backgroundBackupTotalProgress", - true, - ), + backgroundBackupTotalProgress(StoreKey.backgroundBackupTotalProgress, "backgroundBackupTotalProgress", true), backgroundBackupSingleProgress( StoreKey.backgroundBackupSingleProgress, "backgroundBackupSingleProgress", false, ), storageIndicator(StoreKey.storageIndicator, "storageIndicator", true), - thumbnailCacheSize( - StoreKey.thumbnailCacheSize, - "thumbnailCacheSize", - 10000, - ), + thumbnailCacheSize(StoreKey.thumbnailCacheSize, "thumbnailCacheSize", 10000), imageCacheSize(StoreKey.imageCacheSize, "imageCacheSize", 350), - albumThumbnailCacheSize( - StoreKey.albumThumbnailCacheSize, - "albumThumbnailCacheSize", - 200, - ), - selectedAlbumSortOrder( - StoreKey.selectedAlbumSortOrder, - "selectedAlbumSortOrder", - 0, - ), + albumThumbnailCacheSize(StoreKey.albumThumbnailCacheSize, "albumThumbnailCacheSize", 200), + selectedAlbumSortOrder(StoreKey.selectedAlbumSortOrder, "selectedAlbumSortOrder", 0), advancedTroubleshooting(StoreKey.advancedTroubleshooting, null, false), manageLocalMediaAndroid(StoreKey.manageLocalMediaAndroid, null, false), logLevel(StoreKey.logLevel, null, 5), // Level.INFO = 5 preferRemoteImage(StoreKey.preferRemoteImage, null, false), loopVideo(StoreKey.loopVideo, "loopVideo", true), - loadOriginalVideo( - StoreKey.loadOriginalVideo, - "loadOriginalVideo", - false, - ), + loadOriginalVideo(StoreKey.loadOriginalVideo, "loadOriginalVideo", false), mapThemeMode(StoreKey.mapThemeMode, null, 0), mapShowFavoriteOnly(StoreKey.mapShowFavoriteOnly, null, false), mapIncludeArchived(StoreKey.mapIncludeArchived, null, false), @@ -77,21 +41,15 @@ enum AppSettingsEnum { mapRelativeDate(StoreKey.mapRelativeDate, null, 0), allowSelfSignedSSLCert(StoreKey.selfSignedCert, null, false), ignoreIcloudAssets(StoreKey.ignoreIcloudAssets, null, false), - selectedAlbumSortReverse( - StoreKey.selectedAlbumSortReverse, - null, - false, - ), + selectedAlbumSortReverse(StoreKey.selectedAlbumSortReverse, null, false), enableHapticFeedback(StoreKey.enableHapticFeedback, null, true), syncAlbums(StoreKey.syncAlbums, null, false), autoEndpointSwitching(StoreKey.autoEndpointSwitching, null, false), - photoManagerCustomFilter( - StoreKey.photoManagerCustomFilter, - null, - true, - ), + photoManagerCustomFilter(StoreKey.photoManagerCustomFilter, null, true), betaTimeline(StoreKey.betaTimeline, null, false), - ; + enableBackup(StoreKey.enableBackup, null, false), + useCellularForUploadVideos(StoreKey.useWifiForUploadVideos, null, false), + useCellularForUploadPhotos(StoreKey.useWifiForUploadPhotos, null, false); const AppSettingsEnum(this.storeKey, this.hiveKey, this.defaultValue); diff --git a/mobile/lib/services/asset.service.dart b/mobile/lib/services/asset.service.dart index cb7f59e3a9..ee61929c81 100644 --- a/mobile/lib/services/asset.service.dart +++ b/mobile/lib/services/asset.service.dart @@ -80,9 +80,7 @@ class AssetService { final syncedUserIds = await _etagRepository.getAllIds(); final List syncedUsers = syncedUserIds.isEmpty ? [] - : (await _isarUserRepository.getByUserIds(syncedUserIds)) - .nonNulls - .toList(); + : (await _isarUserRepository.getByUserIds(syncedUserIds)).nonNulls.toList(); final Stopwatch sw = Stopwatch()..start(); final bool changes = await _syncService.syncRemoteAssetsToDb( users: syncedUsers, @@ -94,12 +92,11 @@ class AssetService { } /// Returns `(null, null)` if changes are invalid -> requires full sync - Future<(List? toUpsert, List? toDelete)> - _getRemoteAssetChanges(List users, DateTime since) async { - final dto = AssetDeltaSyncDto( - updatedAfter: since, - userIds: users.map((e) => e.id).toList(), - ); + Future<(List? toUpsert, List? toDelete)> _getRemoteAssetChanges( + List users, + DateTime since, + ) async { + final dto = AssetDeltaSyncDto(updatedAfter: since, userIds: users.map((e) => e.id).toList()); final changes = await _apiService.syncApi.getDeltaSync(dto); return changes == null || changes.needsFullSync ? (null, null) @@ -108,20 +105,13 @@ class AssetService { /// Returns the list of people of the given asset id. // If the server is not reachable `null` is returned. - Future?> getRemotePeopleOfAsset( - String remoteId, - ) async { + Future?> getRemotePeopleOfAsset(String remoteId) async { try { - final AssetResponseDto? dto = - await _apiService.assetsApi.getAssetInfo(remoteId); + final AssetResponseDto? dto = await _apiService.assetsApi.getAssetInfo(remoteId); return dto?.people; } catch (error, stack) { - log.severe( - 'Error while getting remote asset info: ${error.toString()}', - error, - stack, - ); + log.severe('Error while getting remote asset info: ${error.toString()}', error, stack); return null; } @@ -135,19 +125,11 @@ class AssetService { String? lastId; // will break on error or once all assets are loaded while (true) { - final dto = AssetFullSyncDto( - limit: chunkSize, - updatedUntil: until, - lastId: lastId, - userId: user.id, - ); + final dto = AssetFullSyncDto(limit: chunkSize, updatedUntil: until, lastId: lastId, userId: user.id); log.fine("Requesting $chunkSize assets from $lastId"); - final List? assets = - await _apiService.syncApi.getFullSyncForUser(dto); + final List? assets = await _apiService.syncApi.getFullSyncForUser(dto); if (assets == null) return null; - log.fine( - "Received ${assets.length} assets from ${assets.firstOrNull?.id} to ${assets.lastOrNull?.id}", - ); + log.fine("Received ${assets.length} assets from ${assets.firstOrNull?.id} to ${assets.lastOrNull?.id}"); allAssets.addAll(assets.map(Asset.remote)); if (assets.length != chunkSize) break; lastId = assets.last.id; @@ -172,8 +154,7 @@ class AssetService { a.exifInfo = newExif; if (newExif != a.exifInfo) { if (a.isInDb) { - await _assetRepository - .transaction(() => _assetRepository.update(a)); + await _assetRepository.transaction(() => _assetRepository.update(a)); } else { debugPrint("[loadExif] parameter Asset is not from DB!"); } @@ -186,10 +167,7 @@ class AssetService { return a; } - Future updateAssets( - List assets, - UpdateAssetDto updateAssetDto, - ) async { + Future updateAssets(List assets, UpdateAssetDto updateAssetDto) async { return await _apiService.assetsApi.updateAssets( AssetBulkUpdateDto( ids: assets.map((e) => e.remoteId!).toList(), @@ -202,10 +180,7 @@ class AssetService { ); } - Future> changeFavoriteStatus( - List assets, - bool isFavorite, - ) async { + Future> changeFavoriteStatus(List assets, bool isFavorite) async { try { await updateAssets(assets, UpdateAssetDto(isFavorite: isFavorite)); @@ -222,24 +197,16 @@ class AssetService { } } - Future> changeArchiveStatus( - List assets, - bool isArchived, - ) async { + Future> changeArchiveStatus(List assets, bool isArchived) async { try { await updateAssets( assets, - UpdateAssetDto( - visibility: - isArchived ? AssetVisibility.archive : AssetVisibility.timeline, - ), + UpdateAssetDto(visibility: isArchived ? AssetVisibility.archive : AssetVisibility.timeline), ); for (var element in assets) { element.isArchived = isArchived; - element.visibility = isArchived - ? AssetVisibilityEnum.archive - : AssetVisibilityEnum.timeline; + element.visibility = isArchived ? AssetVisibilityEnum.archive : AssetVisibilityEnum.timeline; } await _syncService.upsertAssetsWithExif(assets); @@ -251,20 +218,13 @@ class AssetService { } } - Future?> changeDateTime( - List assets, - String updatedDt, - ) async { + Future?> changeDateTime(List assets, String updatedDt) async { try { - await updateAssets( - assets, - UpdateAssetDto(dateTimeOriginal: updatedDt), - ); + await updateAssets(assets, UpdateAssetDto(dateTimeOriginal: updatedDt)); for (var element in assets) { element.fileCreatedAt = DateTime.parse(updatedDt); - element.exifInfo = element.exifInfo - ?.copyWith(dateTimeOriginal: DateTime.parse(updatedDt)); + element.exifInfo = element.exifInfo?.copyWith(dateTimeOriginal: DateTime.parse(updatedDt)); } await _syncService.upsertAssetsWithExif(assets); @@ -276,24 +236,12 @@ class AssetService { } } - Future?> changeLocation( - List assets, - LatLng location, - ) async { + Future?> changeLocation(List assets, LatLng location) async { try { - await updateAssets( - assets, - UpdateAssetDto( - latitude: location.latitude, - longitude: location.longitude, - ), - ); + await updateAssets(assets, UpdateAssetDto(latitude: location.latitude, longitude: location.longitude)); for (var element in assets) { - element.exifInfo = element.exifInfo?.copyWith( - latitude: location.latitude, - longitude: location.longitude, - ); + element.exifInfo = element.exifInfo?.copyWith(latitude: location.latitude, longitude: location.longitude); } await _syncService.upsertAssetsWithExif(assets); @@ -307,10 +255,8 @@ class AssetService { Future syncUploadedAssetToAlbums() async { try { - final selectedAlbums = - await _backupRepository.getAllBySelection(BackupSelection.select); - final excludedAlbums = - await _backupRepository.getAllBySelection(BackupSelection.exclude); + final selectedAlbums = await _backupRepository.getAllBySelection(BackupSelection.select); + final excludedAlbums = await _backupRepository.getAllBySelection(BackupSelection.exclude); final candidates = await _backupService.buildUploadCandidates( selectedAlbums, @@ -320,18 +266,13 @@ class AssetService { await refreshRemoteAssets(); final owner = _userService.getMyUser(); - final remoteAssets = await _assetRepository.getAll( - ownerId: owner.id, - state: AssetState.merged, - ); + final remoteAssets = await _assetRepository.getAll(ownerId: owner.id, state: AssetState.merged); /// Map Map> assetToAlbums = {}; for (BackupCandidate candidate in candidates) { - final asset = remoteAssets.firstWhereOrNull( - (a) => a.localId == candidate.asset.localId, - ); + final asset = remoteAssets.firstWhereOrNull((a) => a.localId == candidate.asset.localId); if (asset != null) { for (final albumName in candidate.albumNames) { @@ -352,10 +293,7 @@ class AssetService { } } - Future setDescription( - Asset asset, - String newDescription, - ) async { + Future setDescription(Asset asset, String newDescription) async { final remoteAssetId = asset.remoteId; final localExifId = asset.exifInfo?.assetId; @@ -364,10 +302,7 @@ class AssetService { return; } - final result = await _assetApiRepository.update( - remoteAssetId, - description: newDescription, - ); + final result = await _assetApiRepository.update(remoteAssetId, description: newDescription); final description = result.exifInfo?.description; @@ -375,8 +310,7 @@ class AssetService { var exifInfo = await _exifInfoRepository.get(localExifId); if (exifInfo != null) { - await _exifInfoRepository - .update(exifInfo.copyWith(description: description)); + await _exifInfoRepository.update(exifInfo.copyWith(description: description)); } } } @@ -429,22 +363,16 @@ class AssetService { // Delete files from local gallery final candidates = assets.where((asset) => asset.isLocal); - final deletedIds = await _assetMediaRepository - .deleteAll(candidates.map((asset) => asset.localId!).toList()); + final deletedIds = await _assetMediaRepository.deleteAll(candidates.map((asset) => asset.localId!).toList()); // Modify local database by removing the reference to the local assets if (deletedIds.isNotEmpty) { // Delete records from local database - final isarIds = assets - .where((asset) => asset.storage == AssetState.local) - .map((asset) => asset.id) - .toList(); + final isarIds = assets.where((asset) => asset.storage == AssetState.local).map((asset) => asset.id).toList(); await _assetRepository.deleteByIds(isarIds); // Modify Merged asset to be remote only - final updatedAssets = assets - .where((asset) => asset.storage == AssetState.merged) - .map((asset) { + final updatedAssets = assets.where((asset) => asset.storage == AssetState.merged).map((asset) { asset.localId = null; return asset; }).toList(); @@ -454,10 +382,7 @@ class AssetService { } /// Delete assets from the server and unreference from the database - Future deleteRemoteAssets( - Iterable assets, { - bool shouldDeletePermanently = false, - }) async { + Future deleteRemoteAssets(Iterable assets, {bool shouldDeletePermanently = false}) async { final candidates = assets.where((a) => a.isRemote); if (candidates.isEmpty) { @@ -465,17 +390,12 @@ class AssetService { } await _apiService.assetsApi.deleteAssets( - AssetBulkDeleteDto( - ids: candidates.map((a) => a.remoteId!).toList(), - force: shouldDeletePermanently, - ), + AssetBulkDeleteDto(ids: candidates.map((a) => a.remoteId!).toList(), force: shouldDeletePermanently), ); /// Update asset info bassed on the deletion type. final payload = shouldDeletePermanently - ? assets - .where((asset) => asset.storage == AssetState.merged) - .map((asset) { + ? assets.where((asset) => asset.storage == AssetState.merged).map((asset) { asset.remoteId = null; asset.visibility = AssetVisibilityEnum.timeline; return asset; @@ -500,10 +420,7 @@ class AssetService { /// Delete assets on both local file system and the server. /// Unreference from the database. - Future deleteAssets( - Iterable assets, { - bool shouldDeletePermanently = false, - }) async { + Future deleteAssets(Iterable assets, {bool shouldDeletePermanently = false}) async { final hasLocal = assets.any((asset) => asset.isLocal); final hasRemote = assets.any((asset) => asset.isRemote); @@ -512,10 +429,7 @@ class AssetService { } if (hasRemote) { - await deleteRemoteAssets( - assets, - shouldDeletePermanently: shouldDeletePermanently, - ); + await deleteRemoteAssets(assets, shouldDeletePermanently: shouldDeletePermanently); } } @@ -533,14 +447,8 @@ class AssetService { return _assetRepository.getMotionAssets(me.id); } - Future setVisibility( - List assets, - AssetVisibilityEnum visibility, - ) async { - await _assetApiRepository.updateVisibility( - assets.map((asset) => asset.remoteId!).toList(), - visibility, - ); + Future setVisibility(List assets, AssetVisibilityEnum visibility) async { + await _assetApiRepository.updateVisibility(assets.map((asset) => asset.remoteId!).toList(), visibility); final updatedAssets = assets.map((asset) { asset.visibility = visibility; diff --git a/mobile/lib/services/auth.service.dart b/mobile/lib/services/auth.service.dart index 0eec253ee1..91c23cac1c 100644 --- a/mobile/lib/services/auth.service.dart +++ b/mobile/lib/services/auth.service.dart @@ -8,10 +8,12 @@ import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/models/auth/auxilary_endpoint.model.dart'; import 'package:immich_mobile/models/auth/login_response.model.dart'; import 'package:immich_mobile/providers/api.provider.dart'; +import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/providers/background_sync.provider.dart'; import 'package:immich_mobile/repositories/auth.repository.dart'; import 'package:immich_mobile/repositories/auth_api.repository.dart'; import 'package:immich_mobile/services/api.service.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/services/network.service.dart'; import 'package:logging/logging.dart'; import 'package:openapi/api.dart'; @@ -23,6 +25,7 @@ final authServiceProvider = Provider( ref.watch(apiServiceProvider), ref.watch(networkServiceProvider), ref.watch(backgroundSyncProvider), + ref.watch(appSettingsServiceProvider), ), ); @@ -32,7 +35,7 @@ class AuthService { final ApiService _apiService; final NetworkService _networkService; final BackgroundSyncManager _backgroundSyncManager; - + final AppSettingsService _appSettingsService; final _log = Logger("AuthService"); AuthService( @@ -41,6 +44,7 @@ class AuthService { this._apiService, this._networkService, this._backgroundSyncManager, + this._appSettingsService, ); /// Validates the provided server URL by resolving and setting the endpoint. @@ -106,6 +110,8 @@ class AuthService { await clearLocalData().catchError((error, stackTrace) { _log.severe("Error clearing local data", error, stackTrace); }); + + await _appSettingsService.setSetting(AppSettingsEnum.enableBackup, false); } } diff --git a/mobile/lib/services/background.service.dart b/mobile/lib/services/background.service.dart index 97acf2941f..dea20b6d98 100644 --- a/mobile/lib/services/background.service.dart +++ b/mobile/lib/services/background.service.dart @@ -39,10 +39,8 @@ final backgroundServiceProvider = Provider((ref) => BackgroundService()); /// Background backup service class BackgroundService { static const String _portNameLock = "immichLock"; - static const MethodChannel _foregroundChannel = - MethodChannel('immich/foregroundChannel'); - static const MethodChannel _backgroundChannel = - MethodChannel('immich/backgroundChannel'); + static const MethodChannel _foregroundChannel = MethodChannel('immich/foregroundChannel'); + static const MethodChannel _backgroundChannel = MethodChannel('immich/backgroundChannel'); static const notifyInterval = Duration(milliseconds: 400); bool _isBackgroundInitialized = false; CancellationToken? _cancellationToken; @@ -56,10 +54,11 @@ class BackgroundService { int _assetsToUploadCount = 0; String _lastPrintedDetailContent = ""; String? _lastPrintedDetailTitle; - late final ThrottleProgressUpdate _throttledNotifiy = - ThrottleProgressUpdate(_updateProgress, notifyInterval); - late final ThrottleProgressUpdate _throttledDetailNotify = - ThrottleProgressUpdate(_updateDetailProgress, notifyInterval); + late final ThrottleProgressUpdate _throttledNotifiy = ThrottleProgressUpdate(_updateProgress, notifyInterval); + late final ThrottleProgressUpdate _throttledDetailNotify = ThrottleProgressUpdate( + _updateDetailProgress, + notifyInterval, + ); bool get isBackgroundInitialized { return _isBackgroundInitialized; @@ -74,10 +73,8 @@ class BackgroundService { Future enableService({bool immediate = false}) async { try { final callback = PluginUtilities.getCallbackHandle(_nativeEntry)!; - final String title = - "backup_background_service_default_notification".tr(); - final bool ok = await _foregroundChannel - .invokeMethod('enable', [callback.toRawHandle(), title, immediate]); + final String title = "backup_background_service_default_notification".tr(); + final bool ok = await _foregroundChannel.invokeMethod('enable', [callback.toRawHandle(), title, immediate]); return ok; } catch (error) { return false; @@ -92,15 +89,12 @@ class BackgroundService { int triggerMaxDelay = 50000, }) async { try { - final bool ok = await _foregroundChannel.invokeMethod( - 'configure', - [ - requireUnmetered, - requireCharging, - triggerUpdateDelay, - triggerMaxDelay, - ], - ); + final bool ok = await _foregroundChannel.invokeMethod('configure', [ + requireUnmetered, + requireCharging, + triggerUpdateDelay, + triggerMaxDelay, + ]); return ok; } catch (error) { return false; @@ -133,8 +127,7 @@ class BackgroundService { return true; } try { - return await _foregroundChannel - .invokeMethod('isIgnoringBatteryOptimizations'); + return await _foregroundChannel.invokeMethod('isIgnoringBatteryOptimizations'); } catch (error) { return false; } @@ -146,10 +139,7 @@ class BackgroundService { } Future?> digestFiles(List paths) { - return _foregroundChannel.invokeListMethod( - "digestFiles", - paths, - ); + return _foregroundChannel.invokeListMethod("digestFiles", paths); } /// Updates the notification shown by the background service @@ -164,10 +154,15 @@ class BackgroundService { }) async { try { if (_isBackgroundInitialized) { - return _backgroundChannel.invokeMethod( - 'updateNotification', - [title, content, progress, max, indeterminate, isDetail, onlyIfFG], - ); + return _backgroundChannel.invokeMethod('updateNotification', [ + title, + content, + progress, + max, + indeterminate, + isDetail, + onlyIfFG, + ]); } } catch (error) { debugPrint("[_updateNotification] failed to communicate with plugin"); @@ -176,15 +171,10 @@ class BackgroundService { } /// Shows a new priority notification - Future _showErrorNotification({ - required String title, - String? content, - String? individualTag, - }) async { + Future _showErrorNotification({required String title, String? content, String? individualTag}) async { try { if (_isBackgroundInitialized && _errorGracePeriodExceeded) { - return await _backgroundChannel - .invokeMethod('showError', [title, content, individualTag]); + return await _backgroundChannel.invokeMethod('showError', [title, content, individualTag]); } } catch (error) { debugPrint("[_showErrorNotification] failed to communicate with plugin"); @@ -198,9 +188,7 @@ class BackgroundService { return await _backgroundChannel.invokeMethod('clearErrorNotifications'); } } catch (error) { - debugPrint( - "[_clearErrorNotifications] failed to communicate with plugin", - ); + debugPrint("[_clearErrorNotifications] failed to communicate with plugin"); } return false; } @@ -240,8 +228,7 @@ class BackgroundService { final bs = tempRp.asBroadcastStream(); while (_wantsLockTime == lockTime) { other.send(tempSp); - final dynamic answer = await bs.first - .timeout(const Duration(seconds: 3), onTimeout: () => null); + final dynamic answer = await bs.first.timeout(const Duration(seconds: 3), onTimeout: () => null); if (_wantsLockTime != lockTime) { break; } @@ -257,8 +244,7 @@ class BackgroundService { } else if (answer == false) { // other isolate is still active } - final dynamic isFinished = await bs.first - .timeout(const Duration(seconds: 3), onTimeout: () => false); + final dynamic isFinished = await bs.first.timeout(const Duration(seconds: 3), onTimeout: () => false); if (isFinished == true) { break; } @@ -311,10 +297,7 @@ class BackgroundService { // indefinitely and can run later // Android is fine to wait here until the lock releases final waitForLock = Platform.isIOS - ? acquireLock().timeout( - const Duration(seconds: 5), - onTimeout: () => false, - ) + ? acquireLock().timeout(const Duration(seconds: 5), onTimeout: () => false) : acquireLock(); final bool hasAccess = await waitForLock; @@ -350,30 +333,17 @@ class BackgroundService { final db = await Bootstrap.initIsar(); await Bootstrap.initDomain(db); - final ref = ProviderContainer( - overrides: [ - dbProvider.overrideWithValue(db), - isarProvider.overrideWithValue(db), - ], - ); + final ref = ProviderContainer(overrides: [dbProvider.overrideWithValue(db), isarProvider.overrideWithValue(db)]); HttpSSLOptions.apply(); - ref - .read(apiServiceProvider) - .setAccessToken(Store.get(StoreKey.accessToken)); + ref.read(apiServiceProvider).setAccessToken(Store.get(StoreKey.accessToken)); await ref.read(authServiceProvider).setOpenApiServiceEndpoint(); if (kDebugMode) { - debugPrint( - "[BG UPLOAD] Using endpoint: ${ref.read(apiServiceProvider).apiClient.basePath}", - ); + debugPrint("[BG UPLOAD] Using endpoint: ${ref.read(apiServiceProvider).apiClient.basePath}"); } - final selectedAlbums = await ref - .read(backupAlbumRepositoryProvider) - .getAllBySelection(BackupSelection.select); - final excludedAlbums = await ref - .read(backupAlbumRepositoryProvider) - .getAllBySelection(BackupSelection.exclude); + final selectedAlbums = await ref.read(backupAlbumRepositoryProvider).getAllBySelection(BackupSelection.select); + final excludedAlbums = await ref.read(backupAlbumRepositoryProvider).getAllBySelection(BackupSelection.exclude); if (selectedAlbums.isEmpty) { return true; } @@ -392,9 +362,7 @@ class BackgroundService { final backupAlbums = [...selectedAlbums, ...excludedAlbums]; backupAlbums.sortBy((e) => e.id); - final dbAlbums = await ref - .read(backupAlbumRepositoryProvider) - .getAll(sort: BackupAlbumSort.id); + final dbAlbums = await ref.read(backupAlbumRepositoryProvider).getAll(sort: BackupAlbumSort.id); final List toDelete = []; final List toUpsert = []; // stores the most recent `lastBackup` per album but always keeps the `selection` from the most recent DB state @@ -403,9 +371,7 @@ class BackgroundService { backupAlbums, compare: (BackupAlbum a, BackupAlbum b) => a.id.compareTo(b.id), both: (BackupAlbum a, BackupAlbum b) { - a.lastBackup = a.lastBackup.isAfter(b.lastBackup) - ? a.lastBackup - : b.lastBackup; + a.lastBackup = a.lastBackup.isAfter(b.lastBackup) ? a.lastBackup : b.lastBackup; toUpsert.add(a); return true; }, @@ -419,9 +385,7 @@ class BackgroundService { return false; } // Android should check for new assets added while performing backup - } while (Platform.isAndroid && - true == - await _backgroundChannel.invokeMethod("hasContentChanged")); + } while (Platform.isAndroid && true == await _backgroundChannel.invokeMethod("hasContentChanged")); return true; } @@ -432,19 +396,14 @@ class BackgroundService { List excludedAlbums, ) async { _errorGracePeriodExceeded = _isErrorGracePeriodExceeded(settingsService); - final bool notifyTotalProgress = settingsService - .getSetting(AppSettingsEnum.backgroundBackupTotalProgress); - final bool notifySingleProgress = settingsService - .getSetting(AppSettingsEnum.backgroundBackupSingleProgress); + final bool notifyTotalProgress = settingsService.getSetting(AppSettingsEnum.backgroundBackupTotalProgress); + final bool notifySingleProgress = settingsService.getSetting(AppSettingsEnum.backgroundBackupSingleProgress); if (_canceledBySystem) { return false; } - Set toUpload = await backupService.buildUploadCandidates( - selectedAlbums, - excludedAlbums, - ); + Set toUpload = await backupService.buildUploadCandidates(selectedAlbums, excludedAlbums); try { toUpload = await backupService.removeAlreadyUploadedAssets(toUpload); @@ -467,12 +426,7 @@ class BackgroundService { _uploadedAssetsCount = 0; _updateNotification( title: "backup_background_service_in_progress_notification".tr(), - content: notifyTotalProgress - ? formatAssetBackupProgress( - _uploadedAssetsCount, - _assetsToUploadCount, - ) - : null, + content: notifyTotalProgress ? formatAssetBackupProgress(_uploadedAssetsCount, _assetsToUploadCount) : null, progress: 0, max: notifyTotalProgress ? _assetsToUploadCount : 0, indeterminate: !notifyTotalProgress, @@ -486,13 +440,9 @@ class BackgroundService { toUpload, _cancellationToken!, pmProgressHandler: pmProgressHandler, - onSuccess: (result) => _onAssetUploaded( - shouldNotify: notifyTotalProgress, - ), - onProgress: (bytes, totalBytes) => - _onProgress(bytes, totalBytes, shouldNotify: notifySingleProgress), - onCurrentAsset: (asset) => - _onSetCurrentBackupAsset(asset, shouldNotify: notifySingleProgress), + onSuccess: (result) => _onAssetUploaded(shouldNotify: notifyTotalProgress), + onProgress: (bytes, totalBytes) => _onProgress(bytes, totalBytes, shouldNotify: notifySingleProgress), + onCurrentAsset: (asset) => _onSetCurrentBackupAsset(asset, shouldNotify: notifySingleProgress), onError: _onBackupError, isBackground: true, ); @@ -507,9 +457,7 @@ class BackgroundService { return ok; } - void _onAssetUploaded({ - bool shouldNotify = false, - }) async { + void _onAssetUploaded({bool shouldNotify = false}) async { if (!shouldNotify) { return; } @@ -527,8 +475,7 @@ class BackgroundService { } void _updateDetailProgress(String? title, int progress, int total) { - final String msg = - total > 0 ? humanReadableBytesProgress(progress, total) : ""; + final String msg = total > 0 ? humanReadableBytesProgress(progress, total) : ""; // only update if message actually differs (to stop many useless notification updates on large assets or slow connections) if (msg != _lastPrintedDetailContent || _lastPrintedDetailTitle != title) { _lastPrintedDetailContent = msg; @@ -548,39 +495,33 @@ class BackgroundService { progress: _uploadedAssetsCount, max: _assetsToUploadCount, title: title, - content: formatAssetBackupProgress( - _uploadedAssetsCount, - _assetsToUploadCount, - ), + content: formatAssetBackupProgress(_uploadedAssetsCount, _assetsToUploadCount), ); } void _onBackupError(ErrorUploadAsset errorAssetInfo) { _showErrorNotification( - title: "backup_background_service_upload_failure_notification" - .tr(namedArgs: {'filename': errorAssetInfo.fileName}), + title: "backup_background_service_upload_failure_notification".tr( + namedArgs: {'filename': errorAssetInfo.fileName}, + ), individualTag: errorAssetInfo.id, ); } - void _onSetCurrentBackupAsset( - CurrentUploadAsset currentUploadAsset, { - bool shouldNotify = false, - }) { + void _onSetCurrentBackupAsset(CurrentUploadAsset currentUploadAsset, {bool shouldNotify = false}) { if (!shouldNotify) { return; } - _throttledDetailNotify.title = - "backup_background_service_current_upload_notification" - .tr(namedArgs: {'filename': currentUploadAsset.fileName}); + _throttledDetailNotify.title = "backup_background_service_current_upload_notification".tr( + namedArgs: {'filename': currentUploadAsset.fileName}, + ); _throttledDetailNotify.progress = 0; _throttledDetailNotify.total = 0; } bool _isErrorGracePeriodExceeded(AppSettingsService appSettingsService) { - final int value = appSettingsService - .getSetting(AppSettingsEnum.uploadErrorNotificationGracePeriod); + final int value = appSettingsService.getSetting(AppSettingsEnum.uploadErrorNotificationGracePeriod); if (value == 0) { return true; } else if (value == 5) { diff --git a/mobile/lib/services/backup.service.dart b/mobile/lib/services/backup.service.dart index 370b64398d..3e29222b4c 100644 --- a/mobile/lib/services/backup.service.dart +++ b/mobile/lib/services/backup.service.dart @@ -75,9 +75,7 @@ class BackupService { } Future _saveDuplicatedAssetIds(List deviceAssetIds) => - _assetRepository.transaction( - () => _assetRepository.upsertDuplicatedAssets(deviceAssetIds), - ); + _assetRepository.transaction(() => _assetRepository.upsertDuplicatedAssets(deviceAssetIds)); /// Get duplicated asset id from database Future> getDuplicatedAssetIds() async { @@ -127,8 +125,7 @@ class BackupService { continue; } - if (useTimeFilter && - localAlbum.modifiedAt.isBefore(backupAlbum.lastBackup)) { + if (useTimeFilter && localAlbum.modifiedAt.isBefore(backupAlbum.lastBackup)) { continue; } final List assets; @@ -137,8 +134,8 @@ class BackupService { backupAlbum.id, modifiedFrom: useTimeFilter ? - // subtract 2 seconds to prevent missing assets due to rounding issues - backupAlbum.lastBackup.subtract(const Duration(seconds: 2)) + // subtract 2 seconds to prevent missing assets due to rounding issues + backupAlbum.lastBackup.subtract(const Duration(seconds: 2)) : null, modifiedUntil: useTimeFilter ? now : null, ); @@ -151,9 +148,7 @@ class BackupService { for (final asset in assets) { List albumNames = [localAlbum.name]; - final existingAsset = candidates.firstWhereOrNull( - (candidate) => candidate.asset.localId == asset.localId, - ); + final existingAsset = candidates.firstWhereOrNull((candidate) => candidate.asset.localId == asset.localId); if (existingAsset != null) { albumNames.addAll(existingAsset.albumNames); @@ -170,17 +165,13 @@ class BackupService { } /// Returns a new list of assets not yet uploaded - Future> removeAlreadyUploadedAssets( - Set candidates, - ) async { + Future> removeAlreadyUploadedAssets(Set candidates) async { if (candidates.isEmpty) { return candidates; } final Set duplicatedAssetIds = await getDuplicatedAssetIds(); - candidates.removeWhere( - (candidate) => duplicatedAssetIds.contains(candidate.asset.localId), - ); + candidates.removeWhere((candidate) => duplicatedAssetIds.contains(candidate.asset.localId)); if (candidates.isEmpty) { return candidates; @@ -189,12 +180,8 @@ class BackupService { final Set existing = {}; try { final String deviceId = Store.get(StoreKey.deviceId); - final CheckExistingAssetsResponseDto? duplicates = - await _apiService.assetsApi.checkExistingAssets( - CheckExistingAssetsDto( - deviceAssetIds: candidates.map((c) => c.asset.localId!).toList(), - deviceId: deviceId, - ), + final CheckExistingAssetsResponseDto? duplicates = await _apiService.assetsApi.checkExistingAssets( + CheckExistingAssetsDto(deviceAssetIds: candidates.map((c) => c.asset.localId!).toList(), deviceId: deviceId), ); if (duplicates != null) { existing.addAll(duplicates.existingIds); @@ -215,12 +202,13 @@ class BackupService { } Future _checkPermissions() async { - if (Platform.isAndroid && - !(await pm.Permission.accessMediaLocation.status).isGranted) { + if (Platform.isAndroid && !(await pm.Permission.accessMediaLocation.status).isGranted) { // double check that permission is granted here, to guard against // uploading corrupt assets without EXIF information - _log.warning("Media location permission is not granted. " - "Cannot access original assets for backup."); + _log.warning( + "Media location permission is not granted. " + "Cannot access original assets for backup.", + ); return false; } @@ -236,13 +224,11 @@ class BackupService { /// Upload images before video assets for background tasks /// these are further sorted by using their creation date List _sortPhotosFirst(List candidates) { - return candidates.sorted( - (a, b) { - final cmp = a.asset.type.index - b.asset.type.index; - if (cmp != 0) return cmp; - return a.asset.fileCreatedAt.compareTo(b.asset.fileCreatedAt); - }, - ); + return candidates.sorted((a, b) { + final cmp = a.asset.type.index - b.asset.type.index; + if (cmp != 0) return cmp; + return a.asset.fileCreatedAt.compareTo(b.asset.fileCreatedAt); + }); } Future backupAsset( @@ -255,8 +241,7 @@ class BackupService { required void Function(CurrentUploadAsset asset) onCurrentAsset, required void Function(ErrorUploadAsset error) onError, }) async { - final bool isIgnoreIcloudAssets = - _appSetting.getSetting(AppSettingsEnum.ignoreIcloudAssets); + final bool isIgnoreIcloudAssets = _appSetting.getSetting(AppSettingsEnum.ignoreIcloudAssets); final shouldSyncAlbums = _appSetting.getSetting(AppSettingsEnum.syncAlbums); final String deviceId = Store.get(StoreKey.deviceId); final String savedEndpoint = Store.get(StoreKey.serverEndpoint); @@ -279,8 +264,7 @@ class BackupService { File? livePhotoFile; try { - final isAvailableLocally = - await asset.local!.isLocallyAvailable(isOrigin: true); + final isAvailableLocally = await asset.local!.isLocallyAvailable(isOrigin: true); // Handle getting files from iCloud if (!isAvailableLocally && Platform.isIOS) { @@ -292,43 +276,32 @@ class BackupService { onCurrentAsset( CurrentUploadAsset( id: asset.localId!, - fileCreatedAt: asset.fileCreatedAt.year == 1970 - ? asset.fileModifiedAt - : asset.fileCreatedAt, + fileCreatedAt: asset.fileCreatedAt.year == 1970 ? asset.fileModifiedAt : asset.fileCreatedAt, fileName: asset.fileName, fileType: _getAssetType(asset.type), iCloudAsset: true, ), ); - file = - await asset.local!.loadFile(progressHandler: pmProgressHandler); + file = await asset.local!.loadFile(progressHandler: pmProgressHandler); if (asset.local!.isLivePhoto) { - livePhotoFile = await asset.local!.loadFile( - withSubtype: true, - progressHandler: pmProgressHandler, - ); + livePhotoFile = await asset.local!.loadFile(withSubtype: true, progressHandler: pmProgressHandler); } } else { - file = - await asset.local!.originFile.timeout(const Duration(seconds: 5)); + file = await asset.local!.originFile.timeout(const Duration(seconds: 5)); if (asset.local!.isLivePhoto) { - livePhotoFile = await asset.local!.originFileWithSubtype - .timeout(const Duration(seconds: 5)); + livePhotoFile = await asset.local!.originFileWithSubtype.timeout(const Duration(seconds: 5)); } } if (file != null) { - String? originalFileName = - await _assetMediaRepository.getOriginalFilename(asset.localId!); + String? originalFileName = await _assetMediaRepository.getOriginalFilename(asset.localId!); originalFileName ??= asset.fileName; if (asset.local!.isLivePhoto) { if (livePhotoFile == null) { - _log.warning( - "Failed to obtain motion part of the livePhoto - $originalFileName", - ); + _log.warning("Failed to obtain motion part of the livePhoto - $originalFileName"); } } @@ -349,10 +322,8 @@ class BackupService { baseRequest.headers.addAll(ApiService.getRequestHeaders()); baseRequest.fields['deviceAssetId'] = asset.localId!; baseRequest.fields['deviceId'] = deviceId; - baseRequest.fields['fileCreatedAt'] = - asset.fileCreatedAt.toUtc().toIso8601String(); - baseRequest.fields['fileModifiedAt'] = - asset.fileModifiedAt.toUtc().toIso8601String(); + baseRequest.fields['fileCreatedAt'] = asset.fileCreatedAt.toUtc().toIso8601String(); + baseRequest.fields['fileModifiedAt'] = asset.fileModifiedAt.toUtc().toIso8601String(); baseRequest.fields['isFavorite'] = asset.isFavorite.toString(); baseRequest.fields['duration'] = asset.duration.toString(); baseRequest.files.add(assetRawUploadData); @@ -360,9 +331,7 @@ class BackupService { onCurrentAsset( CurrentUploadAsset( id: asset.localId!, - fileCreatedAt: asset.fileCreatedAt.year == 1970 - ? asset.fileModifiedAt - : asset.fileCreatedAt, + fileCreatedAt: asset.fileCreatedAt.year == 1970 ? asset.fileModifiedAt : asset.fileCreatedAt, fileName: originalFileName, fileType: _getAssetType(asset.type), fileSize: file.lengthSync(), @@ -372,25 +341,16 @@ class BackupService { String? livePhotoVideoId; if (asset.local!.isLivePhoto && livePhotoFile != null) { - livePhotoVideoId = await uploadLivePhotoVideo( - originalFileName, - livePhotoFile, - baseRequest, - cancelToken, - ); + livePhotoVideoId = await uploadLivePhotoVideo(originalFileName, livePhotoFile, baseRequest, cancelToken); } if (livePhotoVideoId != null) { baseRequest.fields['livePhotoVideoId'] = livePhotoVideoId; } - final response = await httpClient.send( - baseRequest, - cancellationToken: cancelToken, - ); + final response = await httpClient.send(baseRequest, cancellationToken: cancelToken); - final responseBody = - jsonDecode(await response.stream.bytesToString()); + final responseBody = jsonDecode(await response.stream.bytesToString()); if (![200, 201].contains(response.statusCode)) { final error = responseBody; @@ -434,10 +394,7 @@ class BackupService { ); if (shouldSyncAlbums) { - await _albumService.syncUploadAlbums( - candidate.albumNames, - [responseBody['id'] as String], - ); + await _albumService.syncUploadAlbums(candidate.albumNames, [responseBody['id'] as String]); } } } on http.CancelledException { @@ -476,10 +433,7 @@ class BackupService { if (livePhotoVideoFile == null) { return null; } - final livePhotoTitle = p.setExtension( - originalFileName, - p.extension(livePhotoVideoFile.path), - ); + final livePhotoTitle = p.setExtension(originalFileName, p.extension(livePhotoVideoFile.path)); final fileStream = livePhotoVideoFile.openRead(); final livePhotoRawUploadData = http.MultipartFile( "assetData", @@ -487,49 +441,36 @@ class BackupService { livePhotoVideoFile.lengthSync(), filename: livePhotoTitle, ); - final livePhotoReq = MultipartRequest( - baseRequest.method, - baseRequest.url, - onProgress: baseRequest.onProgress, - ) + final livePhotoReq = MultipartRequest(baseRequest.method, baseRequest.url, onProgress: baseRequest.onProgress) ..headers.addAll(baseRequest.headers) ..fields.addAll(baseRequest.fields); livePhotoReq.files.add(livePhotoRawUploadData); - var response = await httpClient.send( - livePhotoReq, - cancellationToken: cancelToken, - ); + var response = await httpClient.send(livePhotoReq, cancellationToken: cancelToken); var responseBody = jsonDecode(await response.stream.bytesToString()); if (![200, 201].contains(response.statusCode)) { var error = responseBody; - debugPrint( - "Error(${error['statusCode']}) uploading livePhoto for assetId | $livePhotoTitle | ${error['error']}", - ); + debugPrint("Error(${error['statusCode']}) uploading livePhoto for assetId | $livePhotoTitle | ${error['error']}"); } return responseBody.containsKey('id') ? responseBody['id'] : null; } String _getAssetType(AssetType assetType) => switch (assetType) { - AssetType.audio => "AUDIO", - AssetType.image => "IMAGE", - AssetType.video => "VIDEO", - AssetType.other => "OTHER", - }; + AssetType.audio => "AUDIO", + AssetType.image => "IMAGE", + AssetType.video => "VIDEO", + AssetType.other => "OTHER", + }; } class MultipartRequest extends http.MultipartRequest { /// Creates a new [MultipartRequest]. - MultipartRequest( - super.method, - super.url, { - required this.onProgress, - }); + MultipartRequest(super.method, super.url, {required this.onProgress}); final void Function(int bytes, int totalBytes) onProgress; diff --git a/mobile/lib/services/backup_verification.service.dart b/mobile/lib/services/backup_verification.service.dart index 6d6884eb00..c0f3c0205e 100644 --- a/mobile/lib/services/backup_verification.service.dart +++ b/mobile/lib/services/backup_verification.service.dart @@ -37,11 +37,7 @@ class BackupVerificationService { /// Returns at most [limit] assets that were backed up without exif Future> findWronglyBackedUpAssets({int limit = 100}) async { final owner = _userService.getMyUser().id; - final List onlyLocal = await _assetRepository.getAll( - ownerId: owner, - state: AssetState.local, - limit: limit, - ); + final List onlyLocal = await _assetRepository.getAll(ownerId: owner, state: AssetState.local, limit: limit); final List remoteMatches = await _assetRepository.getMatches( assets: onlyLocal, ownerId: owner, @@ -75,41 +71,32 @@ class BackupVerificationService { if (deleteCandidates.length > 10) { // performs 2 checks in parallel for a nice speedup final half = deleteCandidates.length ~/ 2; - final lower = compute( - _computeSaveToDelete, - ( - deleteCandidates: deleteCandidates.slice(0, half), - originals: originals.slice(0, half), - auth: Store.get(StoreKey.accessToken), - endpoint: Store.get(StoreKey.serverEndpoint), - rootIsolateToken: isolateToken, - fileMediaRepository: _fileMediaRepository, - ), - ); - final upper = compute( - _computeSaveToDelete, - ( - deleteCandidates: deleteCandidates.slice(half), - originals: originals.slice(half), - auth: Store.get(StoreKey.accessToken), - endpoint: Store.get(StoreKey.serverEndpoint), - rootIsolateToken: isolateToken, - fileMediaRepository: _fileMediaRepository, - ), - ); + final lower = compute(_computeSaveToDelete, ( + deleteCandidates: deleteCandidates.slice(0, half), + originals: originals.slice(0, half), + auth: Store.get(StoreKey.accessToken), + endpoint: Store.get(StoreKey.serverEndpoint), + rootIsolateToken: isolateToken, + fileMediaRepository: _fileMediaRepository, + )); + final upper = compute(_computeSaveToDelete, ( + deleteCandidates: deleteCandidates.slice(half), + originals: originals.slice(half), + auth: Store.get(StoreKey.accessToken), + endpoint: Store.get(StoreKey.serverEndpoint), + rootIsolateToken: isolateToken, + fileMediaRepository: _fileMediaRepository, + )); toDelete = await lower + await upper; } else { - toDelete = await compute( - _computeSaveToDelete, - ( - deleteCandidates: deleteCandidates, - originals: originals, - auth: Store.get(StoreKey.accessToken), - endpoint: Store.get(StoreKey.serverEndpoint), - rootIsolateToken: isolateToken, - fileMediaRepository: _fileMediaRepository, - ), - ); + toDelete = await compute(_computeSaveToDelete, ( + deleteCandidates: deleteCandidates, + originals: originals, + auth: Store.get(StoreKey.accessToken), + endpoint: Store.get(StoreKey.serverEndpoint), + rootIsolateToken: isolateToken, + fileMediaRepository: _fileMediaRepository, + )); } return toDelete; } @@ -122,7 +109,8 @@ class BackupVerificationService { String endpoint, RootIsolateToken rootIsolateToken, FileMediaRepository fileMediaRepository, - }) tuple, + }) + tuple, ) async { assert(tuple.deleteCandidates.length == tuple.originals.length); final List result = []; @@ -134,22 +122,14 @@ class BackupVerificationService { apiService.setEndpoint(tuple.endpoint); apiService.setAccessToken(tuple.auth); for (int i = 0; i < tuple.deleteCandidates.length; i++) { - if (await _compareAssets( - tuple.deleteCandidates[i], - tuple.originals[i], - apiService, - )) { + if (await _compareAssets(tuple.deleteCandidates[i], tuple.originals[i], apiService)) { result.add(tuple.deleteCandidates[i]); } } return result; } - static Future _compareAssets( - Asset remote, - Asset local, - ApiService apiService, - ) async { + static Future _compareAssets(Asset remote, Asset local, ApiService apiService) async { if (remote.checksum == local.checksum) return false; ExifInfo? exif = remote.exifInfo; if (exif != null && exif.latitude != null) return false; @@ -169,10 +149,7 @@ class BackupVerificationService { latLng.latitude != null && (remote.fileCreatedAt.isAtSameMomentAs(local.fileCreatedAt) || remote.fileModifiedAt.isAtSameMomentAs(local.fileModifiedAt) || - _sameExceptTimeZone( - remote.fileCreatedAt, - local.fileCreatedAt, - ))) { + _sameExceptTimeZone(remote.fileCreatedAt, local.fileCreatedAt))) { if (remote.type == AssetType.video) { // it's very unlikely that a video of same length, filesize, name // and date is wrong match. Cannot easily compare videos anyway @@ -181,10 +158,8 @@ class BackupVerificationService { // for images: make sure they are pixel-wise identical // (skip first few KBs containing metadata) - final Uint64List localImage = - _fakeDecodeImg(await file.readAsBytes()); - final res = await apiService.assetsApi - .downloadAssetWithHttpInfo(remote.remoteId!); + final Uint64List localImage = _fakeDecodeImg(await file.readAsBytes()); + final res = await apiService.assetsApi.downloadAssetWithHttpInfo(remote.remoteId!); final Uint64List remoteImage = _fakeDecodeImg(res.bodyBytes); final eq = const ListEquality().equals(remoteImage, localImage); @@ -198,9 +173,7 @@ class BackupVerificationService { static Uint64List _fakeDecodeImg(Uint8List bytes) { const headerLength = 131072; // assume header is at most 128 KB - final start = bytes.length < headerLength * 2 - ? (bytes.length ~/ (4 * 8)) * 8 - : headerLength; + final start = bytes.length < headerLength * 2 ? (bytes.length ~/ (4 * 8)) * 8 : headerLength; return bytes.buffer.asUint64List(start); } diff --git a/mobile/lib/services/deep_link.service.dart b/mobile/lib/services/deep_link.service.dart index e97a370967..1b717a6eeb 100644 --- a/mobile/lib/services/deep_link.service.dart +++ b/mobile/lib/services/deep_link.service.dart @@ -1,6 +1,16 @@ import 'package:auto_route/auto_route.dart'; +import 'package:immich_mobile/domain/services/asset.service.dart' as beta_asset_service; +import 'package:immich_mobile/domain/services/memory.service.dart'; +import 'package:immich_mobile/domain/services/remote_album.service.dart'; +import 'package:immich_mobile/domain/services/timeline.service.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/providers/album/current_album.provider.dart'; import 'package:immich_mobile/providers/asset_viewer/current_asset.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/asset.provider.dart' as beta_asset_provider; +import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/memory.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/services/album.service.dart'; import 'package:immich_mobile/services/asset.service.dart'; @@ -15,24 +25,52 @@ final deepLinkServiceProvider = Provider( ref.watch(albumServiceProvider), ref.watch(currentAssetProvider.notifier), ref.watch(currentAlbumProvider.notifier), + // Below is used for beta timeline + ref.watch(timelineFactoryProvider), + ref.watch(beta_asset_provider.assetServiceProvider), + ref.watch(currentRemoteAlbumProvider.notifier), + ref.watch(remoteAlbumServiceProvider), + ref.watch(driftMemoryServiceProvider), ), ); class DeepLinkService { + /// TODO: Remove this when beta is default final MemoryService _memoryService; final AssetService _assetService; final AlbumService _albumService; final CurrentAsset _currentAsset; final CurrentAlbum _currentAlbum; + /// Used for beta timeline + final TimelineFactory _betaTimelineFactory; + final beta_asset_service.AssetService _betaAssetService; + final CurrentAlbumNotifier _betaCurrentAlbumNotifier; + final RemoteAlbumService _betaRemoteAlbumService; + final DriftMemoryService _betaMemoryServiceProvider; + const DeepLinkService( this._memoryService, this._assetService, this._albumService, this._currentAsset, this._currentAlbum, + this._betaTimelineFactory, + this._betaAssetService, + this._betaCurrentAlbumNotifier, + this._betaRemoteAlbumService, + this._betaMemoryServiceProvider, ); + DeepLink _handleColdStart(PageRouteInfo route, bool isColdStart) { + return DeepLink([ + // we need something to segue back to if the app was cold started + // TODO: use MainTimelineRoute this when beta is default + if (isColdStart) (Store.isBetaTimelineEnabled) ? const MainTimelineRoute() : const PhotosRoute(), + route, + ]); + } + Future handleScheme(PlatformDeepLink link, bool isColdStart) async { // get everything after the scheme, since Uri cannot parse path final intent = link.uri.host; @@ -54,21 +92,13 @@ class DeepLinkService { return DeepLink.none; } - return DeepLink([ - // we need something to segue back to if the app was cold started - if (isColdStart) const PhotosRoute(), - deepLinkRoute, - ]); + return _handleColdStart(deepLinkRoute, isColdStart); } - Future handleMyImmichApp( - PlatformDeepLink link, - bool isColdStart, - ) async { + Future handleMyImmichApp(PlatformDeepLink link, bool isColdStart) async { final path = link.uri.path; - const uuidRegex = - r'[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}'; + const uuidRegex = r'[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}'; final assetRegex = RegExp('/photos/($uuidRegex)'); final albumRegex = RegExp('/albums/($uuidRegex)'); @@ -87,49 +117,72 @@ class DeepLinkService { return DeepLink.none; } - return DeepLink([ - // we need something to segue back to if the app was cold started - if (isColdStart) const PhotosRoute(), - deepLinkRoute, - ]); + return _handleColdStart(deepLinkRoute, isColdStart); } Future _buildMemoryDeepLink(String memoryId) async { - final memory = await _memoryService.getMemoryById(memoryId); + if (Store.isBetaTimelineEnabled) { + final memory = await _betaMemoryServiceProvider.get(memoryId); - if (memory == null) { - return null; + if (memory == null) { + return null; + } + + return DriftMemoryRoute(memories: [memory], memoryIndex: 0); + } else { + // TODO: Remove this when beta is default + final memory = await _memoryService.getMemoryById(memoryId); + + if (memory == null) { + return null; + } + + return MemoryRoute(memories: [memory], memoryIndex: 0); } - - return MemoryRoute(memories: [memory], memoryIndex: 0); } Future _buildAssetDeepLink(String assetId) async { - final asset = await _assetService.getAssetByRemoteId(assetId); - if (asset == null) { - return null; + if (Store.isBetaTimelineEnabled) { + final asset = await _betaAssetService.getRemoteAsset(assetId); + if (asset == null) { + return null; + } + + return AssetViewerRoute(initialIndex: 0, timelineService: _betaTimelineFactory.fromAssets([asset])); + } else { + // TODO: Remove this when beta is default + final asset = await _assetService.getAssetByRemoteId(assetId); + if (asset == null) { + return null; + } + + _currentAsset.set(asset); + final renderList = await RenderList.fromAssets([asset], GroupAssetsBy.auto); + + return GalleryViewerRoute(renderList: renderList, initialIndex: 0, heroOffset: 0, showStack: true); } - - _currentAsset.set(asset); - final renderList = await RenderList.fromAssets([asset], GroupAssetsBy.auto); - - return GalleryViewerRoute( - renderList: renderList, - initialIndex: 0, - heroOffset: 0, - showStack: true, - ); } Future _buildAlbumDeepLink(String albumId) async { - final album = await _albumService.getAlbumByRemoteId(albumId); + if (Store.isBetaTimelineEnabled) { + final album = await _betaRemoteAlbumService.get(albumId); - if (album == null) { - return null; + if (album == null) { + return null; + } + + _betaCurrentAlbumNotifier.setAlbum(album); + return RemoteAlbumRoute(album: album); + } else { + // TODO: Remove this when beta is default + final album = await _albumService.getAlbumByRemoteId(albumId); + + if (album == null) { + return null; + } + + _currentAlbum.set(album); + return AlbumViewerRoute(albumId: album.id); } - - _currentAlbum.set(album); - - return AlbumViewerRoute(albumId: album.id); } } diff --git a/mobile/lib/services/download.service.dart b/mobile/lib/services/download.service.dart index 98f1765d04..7d2cf01b7c 100644 --- a/mobile/lib/services/download.service.dart +++ b/mobile/lib/services/download.service.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:background_downloader/background_downloader.dart'; import 'package:flutter/services.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; @@ -10,14 +11,10 @@ import 'package:immich_mobile/models/download/livephotos_medatada.model.dart'; import 'package:immich_mobile/repositories/download.repository.dart'; import 'package:immich_mobile/repositories/file_media.repository.dart'; import 'package:immich_mobile/services/api.service.dart'; -import 'package:immich_mobile/utils/download.dart'; import 'package:logging/logging.dart'; final downloadServiceProvider = Provider( - (ref) => DownloadService( - ref.watch(fileMediaRepositoryProvider), - ref.watch(downloadRepositoryProvider), - ), + (ref) => DownloadService(ref.watch(fileMediaRepositoryProvider), ref.watch(downloadRepositoryProvider)), ); class DownloadService { @@ -29,14 +26,10 @@ class DownloadService { void Function(TaskStatusUpdate)? onLivePhotoDownloadStatus; void Function(TaskProgressUpdate)? onTaskProgress; - DownloadService( - this._fileMediaRepository, - this._downloadRepository, - ) { + DownloadService(this._fileMediaRepository, this._downloadRepository) { _downloadRepository.onImageDownloadStatus = _onImageDownloadCallback; _downloadRepository.onVideoDownloadStatus = _onVideoDownloadCallback; - _downloadRepository.onLivePhotoDownloadStatus = - _onLivePhotoDownloadCallback; + _downloadRepository.onLivePhotoDownloadStatus = _onLivePhotoDownloadCallback; _downloadRepository.onTaskProgress = _onTaskProgressCallback; } @@ -83,11 +76,7 @@ class DownloadService { final relativePath = Platform.isAndroid ? 'DCIM/Immich' : null; final file = File(filePath); try { - final Asset? resultAsset = await _fileMediaRepository.saveVideo( - file, - title: title, - relativePath: relativePath, - ); + final Asset? resultAsset = await _fileMediaRepository.saveVideo(file, title: title, relativePath: relativePath); return resultAsset != null; } catch (error, stack) { _log.severe("Error saving video", error, stack); @@ -99,19 +88,14 @@ class DownloadService { } } - Future saveLivePhotos( - Task task, - String livePhotosId, - ) async { + Future saveLivePhotos(Task task, String livePhotosId) async { final records = await _downloadRepository.getLiveVideoTasks(); if (records.length < 2) { return false; } - final imageRecord = - _findTaskRecord(records, livePhotosId, LivePhotosPart.image); - final videoRecord = - _findTaskRecord(records, livePhotosId, LivePhotosPart.video); + final imageRecord = _findTaskRecord(records, livePhotosId, LivePhotosPart.image); + final videoRecord = _findTaskRecord(records, livePhotosId, LivePhotosPart.video); final imageFilePath = await imageRecord.task.filePath(); final videoFilePath = await videoRecord.task.filePath(); @@ -126,8 +110,7 @@ class DownloadService { } on PlatformException catch (error, stack) { // Handle saving MotionPhotos on iOS if (error.code == 'PHPhotosErrorDomain (-1)') { - final result = await _fileMediaRepository - .saveImageWithFile(imageFilePath, title: task.filename); + final result = await _fileMediaRepository.saveImageWithFile(imageFilePath, title: task.filename); return result != null; } _log.severe("Error saving live photo", error, stack); @@ -146,10 +129,7 @@ class DownloadService { await videoFile.delete(); } - await _downloadRepository.deleteRecordsWithIds([ - imageRecord.task.taskId, - videoRecord.task.taskId, - ]); + await _downloadRepository.deleteRecordsWithIds([imageRecord.task.taskId, videoRecord.task.taskId]); } } @@ -158,8 +138,7 @@ class DownloadService { } Future> downloadAll(List assets) async { - return await _downloadRepository - .downloadAll(assets.expand(_createDownloadTasks).toList()); + return await _downloadRepository.downloadAll(assets.expand(_createDownloadTasks).toList()); } Future download(Asset asset) async { @@ -173,22 +152,14 @@ class DownloadService { _buildDownloadTask( asset.remoteId!, asset.fileName, - group: downloadGroupLivePhoto, - metadata: LivePhotosMetadata( - part: LivePhotosPart.image, - id: asset.remoteId!, - ).toJson(), + group: kDownloadGroupLivePhoto, + metadata: LivePhotosMetadata(part: LivePhotosPart.image, id: asset.remoteId!).toJson(), ), _buildDownloadTask( asset.livePhotoVideoId!, - asset.fileName - .toUpperCase() - .replaceAll(RegExp(r"\.(JPG|HEIC)$"), '.MOV'), - group: downloadGroupLivePhoto, - metadata: LivePhotosMetadata( - part: LivePhotosPart.video, - id: asset.remoteId!, - ).toJson(), + asset.fileName.toUpperCase().replaceAll(RegExp(r"\.(JPG|HEIC)$"), '.MOV'), + group: kDownloadGroupLivePhoto, + metadata: LivePhotosMetadata(part: LivePhotosPart.video, id: asset.remoteId!).toJson(), ), ]; } @@ -201,17 +172,12 @@ class DownloadService { _buildDownloadTask( asset.remoteId!, asset.fileName, - group: asset.isImage ? downloadGroupImage : downloadGroupVideo, + group: asset.isImage ? kDownloadGroupImage : kDownloadGroupVideo, ), ]; } - DownloadTask _buildDownloadTask( - String id, - String filename, { - String? group, - String? metadata, - }) { + DownloadTask _buildDownloadTask(String id, String filename, {String? group, String? metadata}) { final path = r'/assets/{id}/original'.replaceAll('{id}', id); final serverEndpoint = Store.get(StoreKey.serverEndpoint); final headers = ApiService.getRequestHeaders(); @@ -228,11 +194,7 @@ class DownloadService { } } -TaskRecord _findTaskRecord( - List records, - String livePhotosId, - LivePhotosPart part, -) { +TaskRecord _findTaskRecord(List records, String livePhotosId, LivePhotosPart part) { return records.firstWhere((record) { final metadata = LivePhotosMetadata.fromJson(record.task.metaData); return metadata.id == livePhotosId && metadata.part == part; diff --git a/mobile/lib/services/entity.service.dart b/mobile/lib/services/entity.service.dart index 8ffead40fa..fe7358fce6 100644 --- a/mobile/lib/services/entity.service.dart +++ b/mobile/lib/services/entity.service.dart @@ -8,10 +8,7 @@ import 'package:immich_mobile/repositories/asset.repository.dart'; class EntityService { final AssetRepository _assetRepository; final IsarUserRepository _isarUserRepository; - const EntityService( - this._assetRepository, - this._isarUserRepository, - ); + const EntityService(this._assetRepository, this._isarUserRepository); Future fillAlbumWithDatabaseEntities(Album album) async { final ownerId = album.ownerId; @@ -20,25 +17,21 @@ class EntityService { final user = await _isarUserRepository.getByUserId(ownerId); album.owner.value = user == null ? null : User.fromDto(user); } - final thumbnailAssetId = - album.remoteThumbnailAssetId ?? album.thumbnail.value?.remoteId; + final thumbnailAssetId = album.remoteThumbnailAssetId ?? album.thumbnail.value?.remoteId; if (thumbnailAssetId != null) { // set thumbnail with asset from database - album.thumbnail.value = - await _assetRepository.getByRemoteId(thumbnailAssetId); + album.thumbnail.value = await _assetRepository.getByRemoteId(thumbnailAssetId); } if (album.remoteUsers.isNotEmpty) { // replace all users with users from database - final users = await _isarUserRepository - .getByUserIds(album.remoteUsers.map((user) => user.id).toList()); + final users = await _isarUserRepository.getByUserIds(album.remoteUsers.map((user) => user.id).toList()); album.sharedUsers.clear(); album.sharedUsers.addAll(users.nonNulls.map(User.fromDto)); album.shared = true; } if (album.remoteAssets.isNotEmpty) { // replace all assets with assets from database - final assets = await _assetRepository - .getAllByRemoteId(album.remoteAssets.map((asset) => asset.remoteId!)); + final assets = await _assetRepository.getAllByRemoteId(album.remoteAssets.map((asset) => asset.remoteId!)); album.assets.clear(); album.assets.addAll(assets); } @@ -47,8 +40,5 @@ class EntityService { } final entityServiceProvider = Provider( - (ref) => EntityService( - ref.watch(assetRepositoryProvider), - ref.watch(userRepositoryProvider), - ), + (ref) => EntityService(ref.watch(assetRepositoryProvider), ref.watch(userRepositoryProvider)), ); diff --git a/mobile/lib/services/etag.service.dart b/mobile/lib/services/etag.service.dart index 4b6f2279ed..00eb83fcea 100644 --- a/mobile/lib/services/etag.service.dart +++ b/mobile/lib/services/etag.service.dart @@ -1,8 +1,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/repositories/etag.repository.dart'; -final etagServiceProvider = - Provider((ref) => ETagService(ref.watch(etagRepositoryProvider))); +final etagServiceProvider = Provider((ref) => ETagService(ref.watch(etagRepositoryProvider))); class ETagService { final ETagRepository _eTagRepository; diff --git a/mobile/lib/services/exif.service.dart b/mobile/lib/services/exif.service.dart index 4d21614ec5..57f793b21e 100644 --- a/mobile/lib/services/exif.service.dart +++ b/mobile/lib/services/exif.service.dart @@ -2,8 +2,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/infrastructure/repositories/exif.repository.dart'; import 'package:immich_mobile/providers/infrastructure/exif.provider.dart'; -final exifServiceProvider = - Provider((ref) => ExifService(ref.watch(exifRepositoryProvider))); +final exifServiceProvider = Provider((ref) => ExifService(ref.watch(exifRepositoryProvider))); class ExifService { final IsarExifRepository _exifInfoRepository; diff --git a/mobile/lib/services/folder.service.dart b/mobile/lib/services/folder.service.dart index 5b97b475b2..91fb455110 100644 --- a/mobile/lib/services/folder.service.dart +++ b/mobile/lib/services/folder.service.dart @@ -6,9 +6,7 @@ import 'package:immich_mobile/models/folder/root_folder.model.dart'; import 'package:immich_mobile/repositories/folder_api.repository.dart'; import 'package:logging/logging.dart'; -final folderServiceProvider = Provider( - (ref) => FolderService(ref.watch(folderApiRepositoryProvider)), -); +final folderServiceProvider = Provider((ref) => FolderService(ref.watch(folderApiRepositoryProvider))); class FolderService { final FolderApiRepository _folderApiRepository; @@ -30,15 +28,13 @@ class FolderService { fullPath = '/$fullPath'; } - List segments = fullPath.split('/') - ..removeWhere((s) => s.isEmpty); + List segments = fullPath.split('/')..removeWhere((s) => s.isEmpty); String currentPath = ''; for (int i = 0; i < segments.length; i++) { String parentPath = currentPath.isEmpty ? '_root_' : currentPath; - currentPath = - i == 0 ? '/${segments[i]}' : '$currentPath/${segments[i]}'; + currentPath = i == 0 ? '/${segments[i]}' : '$currentPath/${segments[i]}'; if (!folderMap.containsKey(parentPath)) { folderMap[parentPath] = []; @@ -46,35 +42,23 @@ class FolderService { if (!folderMap[parentPath]!.any((f) => f.name == segments[i])) { folderMap[parentPath]!.add( - RecursiveFolder( - path: parentPath == '_root_' ? '' : parentPath, - name: segments[i], - subfolders: [], - ), + RecursiveFolder(path: parentPath == '_root_' ? '' : parentPath, name: segments[i], subfolders: []), ); // Sort folders based on order parameter folderMap[parentPath]!.sort( - (a, b) => order == SortOrder.desc - ? b.name.compareTo(a.name) - : a.name.compareTo(b.name), + (a, b) => order == SortOrder.desc ? b.name.compareTo(a.name) : a.name.compareTo(b.name), ); } } } void attachSubfolders(RecursiveFolder folder) { - String fullPath = folder.path.isEmpty - ? '/${folder.name}' - : '${folder.path}/${folder.name}'; + String fullPath = folder.path.isEmpty ? '/${folder.name}' : '${folder.path}/${folder.name}'; if (folderMap.containsKey(fullPath)) { folder.subfolders.addAll(folderMap[fullPath]!); // Sort subfolders based on order parameter - folder.subfolders.sort( - (a, b) => order == SortOrder.desc - ? b.name.compareTo(a.name) - : a.name.compareTo(b.name), - ); + folder.subfolders.sort((a, b) => order == SortOrder.desc ? b.name.compareTo(a.name) : a.name.compareTo(b.name)); for (var subfolder in folder.subfolders) { attachSubfolders(subfolder); } @@ -83,30 +67,19 @@ class FolderService { List rootSubfolders = folderMap['_root_'] ?? []; // Sort root subfolders based on order parameter - rootSubfolders.sort( - (a, b) => order == SortOrder.desc - ? b.name.compareTo(a.name) - : a.name.compareTo(b.name), - ); + rootSubfolders.sort((a, b) => order == SortOrder.desc ? b.name.compareTo(a.name) : a.name.compareTo(b.name)); for (var folder in rootSubfolders) { attachSubfolders(folder); } - return RootFolder( - subfolders: rootSubfolders, - path: '/', - ); + return RootFolder(subfolders: rootSubfolders, path: '/'); } - Future> getFolderAssets( - RootFolder folder, - SortOrder order, - ) async { + Future> getFolderAssets(RootFolder folder, SortOrder order) async { try { if (folder is RecursiveFolder) { - String fullPath = - folder.path.isEmpty ? folder.name : '${folder.path}/${folder.name}'; + String fullPath = folder.path.isEmpty ? folder.name : '${folder.path}/${folder.name}'; fullPath = fullPath[0] == '/' ? fullPath.substring(1) : fullPath; var result = await _folderApiRepository.getAssetsForPath(fullPath); @@ -121,11 +94,7 @@ class FolderService { final result = await _folderApiRepository.getAssetsForPath('/'); return result; } catch (e, stack) { - _log.severe( - "Failed to fetch assets for folder ${folder is RecursiveFolder ? folder.name : "root"}", - e, - stack, - ); + _log.severe("Failed to fetch assets for folder ${folder is RecursiveFolder ? folder.name : "root"}", e, stack); return []; } } diff --git a/mobile/lib/services/gcast.service.dart b/mobile/lib/services/gcast.service.dart index 6d6646fe50..dcf7685237 100644 --- a/mobile/lib/services/gcast.service.dart +++ b/mobile/lib/services/gcast.service.dart @@ -41,11 +41,7 @@ class GCastService { void Function(CastState)? onCastState; - GCastService( - this._gCastRepository, - this._sessionsApiService, - this._assetApiRepository, - ) { + GCastService(this._gCastRepository, this._sessionsApiService, this._assetApiRepository) { _gCastRepository.onCastStatus = _onCastStatusCallback; _gCastRepository.onCastMessage = _onCastMessageCallback; } @@ -71,8 +67,7 @@ class GCastService { } void _handleMediaStatus(Map message) { - final statusList = - (message['status'] as List).whereType>().toList(); + final statusList = (message['status'] as List).whereType>().toList(); if (statusList.isEmpty) { return; @@ -101,9 +96,7 @@ class GCastService { } if (status["media"] != null && status["media"]["duration"] != null) { - final duration = Duration( - milliseconds: (status["media"]["duration"] * 1000 ?? 0).toInt(), - ); + final duration = Duration(milliseconds: (status["media"]["duration"] * 1000 ?? 0).toInt()); onDuration?.call(duration); } @@ -112,8 +105,7 @@ class GCastService { } if (status["currentTime"] != null) { - final currentTime = - Duration(milliseconds: (status["currentTime"] * 1000 ?? 0).toInt()); + final currentTime = Duration(milliseconds: (status["currentTime"] * 1000 ?? 0).toInt()); onCurrentTime?.call(currentTime); } } @@ -150,8 +142,7 @@ class GCastService { // we want to make sure we have at least 10 seconds remaining in the session // this is to account for network latency and other delays when sending the request - final bufferedExpiration = - tokenExpiration.subtract(const Duration(seconds: 10)); + final bufferedExpiration = tokenExpiration.subtract(const Duration(seconds: 10)); return bufferedExpiration.isAfter(DateTime.now()); } @@ -173,16 +164,10 @@ class GCastService { } final unauthenticatedUrl = asset.isVideo - ? getPlaybackUrlForRemoteId( - asset.id, - ) - : getThumbnailUrlForRemoteId( - asset.id, - type: AssetMediaSize.fullsize, - ); + ? getPlaybackUrlForRemoteId(asset.id) + : getThumbnailUrlForRemoteId(asset.id, type: AssetMediaSize.fullsize); - final authenticatedURL = - "$unauthenticatedUrl&sessionKey=${sessionKey?.token}"; + final authenticatedURL = "$unauthenticatedUrl&sessionKey=${sessionKey?.token}"; // get image mime type final mimeType = await _assetApiRepository.getAssetMIMEType(asset.id); @@ -210,8 +195,7 @@ class GCastService { _mediaStatusPollingTimer?.cancel(); if (asset.isVideo) { - _mediaStatusPollingTimer = - Timer.periodic(const Duration(milliseconds: 500), (timer) { + _mediaStatusPollingTimer = Timer.periodic(const Duration(milliseconds: 500), (timer) { if (isConnected) { _gCastRepository.sendMessage(CastSession.kNamespaceMedia, { "type": "GET_STATUS", @@ -225,17 +209,11 @@ class GCastService { } void play() { - _gCastRepository.sendMessage(CastSession.kNamespaceMedia, { - "type": "PLAY", - "mediaSessionId": _sessionId, - }); + _gCastRepository.sendMessage(CastSession.kNamespaceMedia, {"type": "PLAY", "mediaSessionId": _sessionId}); } void pause() { - _gCastRepository.sendMessage(CastSession.kNamespaceMedia, { - "type": "PAUSE", - "mediaSessionId": _sessionId, - }); + _gCastRepository.sendMessage(CastSession.kNamespaceMedia, {"type": "PAUSE", "mediaSessionId": _sessionId}); } void seekTo(Duration position) { @@ -247,10 +225,7 @@ class GCastService { } void stop() { - _gCastRepository.sendMessage(CastSession.kNamespaceMedia, { - "type": "STOP", - "mediaSessionId": _sessionId, - }); + _gCastRepository.sendMessage(CastSession.kNamespaceMedia, {"type": "STOP", "mediaSessionId": _sessionId}); _mediaStatusPollingTimer?.cancel(); currentAssetId = null; @@ -263,18 +238,13 @@ class GCastService { final dests = await _gCastRepository.listDestinations(); return dests - .map( - (device) => ( - device.extras["fn"] ?? "Google Cast", - CastDestinationType.googleCast, - device - ), - ) + .map((device) => (device.extras["fn"] ?? "Google Cast", CastDestinationType.googleCast, device)) .where((device) { - final caString = device.$3.extras["ca"]; - final caNumber = int.tryParse(caString ?? "0") ?? 0; + final caString = device.$3.extras["ca"]; + final caNumber = int.tryParse(caString ?? "0") ?? 0; - return isDisplay(caNumber); - }).toList(growable: false); + return isDisplay(caNumber); + }) + .toList(growable: false); } } diff --git a/mobile/lib/services/hash.service.dart b/mobile/lib/services/hash.service.dart index f0554bf00b..48302be79c 100644 --- a/mobile/lib/services/hash.service.dart +++ b/mobile/lib/services/hash.service.dart @@ -17,8 +17,8 @@ class HashService { required BackgroundService backgroundService, this.batchSizeLimit = kBatchHashSizeLimit, this.batchFileLimit = kBatchHashFileLimit, - }) : _deviceAssetRepository = deviceAssetRepository, - _backgroundService = backgroundService; + }) : _deviceAssetRepository = deviceAssetRepository, + _backgroundService = backgroundService; final IsarDeviceAssetRepository _deviceAssetRepository; final BackgroundService _backgroundService; @@ -33,9 +33,7 @@ class HashService { assets.sort(Asset.compareByLocalId); // Get and sort DB entries - guaranteed to be a subset of assets - final hashesInDB = await _deviceAssetRepository.getByIds( - assets.map((a) => a.localId!).toList(), - ); + final hashesInDB = await _deviceAssetRepository.getByIds(assets.map((a) => a.localId!).toList()); hashesInDB.sort((a, b) => a.assetId.compareTo(b.assetId)); int dbIndex = 0; @@ -60,9 +58,7 @@ class HashService { matchingDbEntry.hash.isNotEmpty && matchingDbEntry.modifiedTime.isAtSameMomentAs(asset.fileModifiedAt)) { // Reuse the existing hash - hashedAssets.add( - asset.copyWith(checksum: base64.encode(matchingDbEntry.hash)), - ); + hashedAssets.add(asset.copyWith(checksum: base64.encode(matchingDbEntry.hash))); continue; } @@ -125,10 +121,7 @@ class HashService { /// Processes a batch of files and returns a list of successfully hashed assets after saving /// them in [DeviceAssetToHash] for future retrieval - Future> _processBatch( - List<_AssetPath> toBeHashed, - List toBeDeleted, - ) async { + Future> _processBatch(List<_AssetPath> toBeHashed, List toBeDeleted) async { _log.info("Hashing ${toBeHashed.length} files"); final hashes = await _hashFiles(toBeHashed.map((e) => e.path).toList()); assert( @@ -143,13 +136,7 @@ class HashService { final asset = toBeHashed.elementAtOrNull(index)?.asset; if (asset != null && hash?.length == 20) { hashedAssets.add(asset.copyWith(checksum: base64.encode(hash!))); - toBeAdded.add( - DeviceAsset( - assetId: asset.localId!, - hash: hash, - modifiedTime: asset.fileModifiedAt, - ), - ); + toBeAdded.add(DeviceAsset(assetId: asset.localId!, hash: hash, modifiedTime: asset.fileModifiedAt)); } else { _log.warning("Failed to hash file ${asset?.localId ?? ''}"); if (asset != null) { diff --git a/mobile/lib/services/local_auth.service.dart b/mobile/lib/services/local_auth.service.dart index 12da5f256b..4721911e8d 100644 --- a/mobile/lib/services/local_auth.service.dart +++ b/mobile/lib/services/local_auth.service.dart @@ -2,11 +2,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/models/auth/biometric_status.model.dart'; import 'package:immich_mobile/repositories/biometric.repository.dart'; -final localAuthServiceProvider = Provider( - (ref) => LocalAuthService( - ref.watch(biometricRepositoryProvider), - ), -); +final localAuthServiceProvider = Provider((ref) => LocalAuthService(ref.watch(biometricRepositoryProvider))); class LocalAuthService { final BiometricRepository _biometricRepository; diff --git a/mobile/lib/services/local_files_manager.service.dart b/mobile/lib/services/local_files_manager.service.dart index 6ba8b43fbf..7cb3067342 100644 --- a/mobile/lib/services/local_files_manager.service.dart +++ b/mobile/lib/services/local_files_manager.service.dart @@ -2,9 +2,7 @@ import 'package:flutter/services.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:logging/logging.dart'; -final localFileManagerServiceProvider = Provider( - (ref) => const LocalFilesManagerService(), -); +final localFileManagerServiceProvider = Provider((ref) => const LocalFilesManagerService()); class LocalFilesManagerService { const LocalFilesManagerService(); @@ -13,8 +11,7 @@ class LocalFilesManagerService { Future moveToTrash(List mediaUrls) async { try { - return await _channel - .invokeMethod('moveToTrash', {'mediaUrls': mediaUrls}); + return await _channel.invokeMethod('moveToTrash', {'mediaUrls': mediaUrls}); } catch (e, s) { _logger.warning('Error moving file to trash', e, s); return false; @@ -23,10 +20,7 @@ class LocalFilesManagerService { Future restoreFromTrash(String fileName, int type) async { try { - return await _channel.invokeMethod( - 'restoreFromTrash', - {'fileName': fileName, 'type': type}, - ); + return await _channel.invokeMethod('restoreFromTrash', {'fileName': fileName, 'type': type}); } catch (e, s) { _logger.warning('Error restore file from trash', e, s); return false; diff --git a/mobile/lib/services/local_notification.service.dart b/mobile/lib/services/local_notification.service.dart index b47ee280b8..e7fc3292e2 100644 --- a/mobile/lib/services/local_notification.service.dart +++ b/mobile/lib/services/local_notification.service.dart @@ -6,15 +6,11 @@ import 'package:immich_mobile/providers/notification_permission.provider.dart'; import 'package:permission_handler/permission_handler.dart'; final localNotificationService = Provider( - (ref) => LocalNotificationService( - ref.watch(notificationPermissionProvider), - ref, - ), + (ref) => LocalNotificationService(ref.watch(notificationPermissionProvider), ref), ); class LocalNotificationService { - final FlutterLocalNotificationsPlugin _localNotificationPlugin = - FlutterLocalNotificationsPlugin(); + final FlutterLocalNotificationsPlugin _localNotificationPlugin = FlutterLocalNotificationsPlugin(); final PermissionStatus _permissionStatus; final Ref ref; @@ -29,17 +25,14 @@ class LocalNotificationService { static const cancelUploadActionID = 'cancel_upload'; Future setup() async { - const androidSetting = - AndroidInitializationSettings('@drawable/notification_icon'); + const androidSetting = AndroidInitializationSettings('@drawable/notification_icon'); const iosSetting = DarwinInitializationSettings(); - const initSettings = - InitializationSettings(android: androidSetting, iOS: iosSetting); + const initSettings = InitializationSettings(android: androidSetting, iOS: iosSetting); await _localNotificationPlugin.initialize( initSettings, - onDidReceiveNotificationResponse: - _onDidReceiveForegroundNotificationResponse, + onDidReceiveNotificationResponse: _onDidReceiveForegroundNotificationResponse, ); } @@ -50,10 +43,7 @@ class LocalNotificationService { AndroidNotificationDetails androidNotificationDetails, DarwinNotificationDetails iosNotificationDetails, ) async { - final notificationDetails = NotificationDetails( - android: androidNotificationDetails, - iOS: iosNotificationDetails, - ); + final notificationDetails = NotificationDetails(android: androidNotificationDetails, iOS: iosNotificationDetails); if (_permissionStatus == PermissionStatus.granted) { await _localNotificationPlugin.show(id, title, body, notificationDetails); @@ -99,20 +89,12 @@ class LocalNotificationService { ongoing: true, actions: (showActions ?? false) ? [ - const AndroidNotificationAction( - cancelUploadActionID, - 'Cancel', - showsUserInterface: true, - ), + const AndroidNotificationAction(cancelUploadActionID, 'Cancel', showsUserInterface: true), ] : null, ) // Non-progress notification - : AndroidNotificationDetails( - androidChannelID, - androidChannelName, - playSound: false, - ); + : AndroidNotificationDetails(androidChannelID, androidChannelName, playSound: false); final iosNotificationDetails = DarwinNotificationDetails( presentBadge: true, @@ -120,18 +102,10 @@ class LocalNotificationService { presentBanner: presentBanner, ); - return _showOrUpdateNotification( - notificationlId, - title, - body, - androidNotificationDetails, - iosNotificationDetails, - ); + return _showOrUpdateNotification(notificationlId, title, body, androidNotificationDetails, iosNotificationDetails); } - void _onDidReceiveForegroundNotificationResponse( - NotificationResponse notificationResponse, - ) { + void _onDidReceiveForegroundNotificationResponse(NotificationResponse notificationResponse) { // Handle notification actions switch (notificationResponse.actionId) { case cancelUploadActionID: diff --git a/mobile/lib/services/memory.service.dart b/mobile/lib/services/memory.service.dart index a44a5ad1bd..e485bb0957 100644 --- a/mobile/lib/services/memory.service.dart +++ b/mobile/lib/services/memory.service.dart @@ -7,10 +7,7 @@ import 'package:immich_mobile/services/api.service.dart'; import 'package:logging/logging.dart'; final memoryServiceProvider = StateProvider((ref) { - return MemoryService( - ref.watch(apiServiceProvider), - ref.watch(assetRepositoryProvider), - ); + return MemoryService(ref.watch(apiServiceProvider), ref.watch(assetRepositoryProvider)); }); class MemoryService { @@ -35,21 +32,11 @@ class MemoryService { List memories = []; for (final memory in data) { - final dbAssets = await _assetRepository - .getAllByRemoteId(memory.assets.map((e) => e.id)); + final dbAssets = await _assetRepository.getAllByRemoteId(memory.assets.map((e) => e.id)); final yearsAgo = now.year - memory.data.year; if (dbAssets.isNotEmpty) { - final String title = 'years_ago'.t( - args: { - 'years': yearsAgo.toString(), - }, - ); - memories.add( - Memory( - title: title, - assets: dbAssets, - ), - ); + final String title = 'years_ago'.t(args: {'years': yearsAgo.toString()}); + memories.add(Memory(title: title, assets: dbAssets)); } } @@ -67,23 +54,15 @@ class MemoryService { if (memoryResponse == null) { return null; } - final dbAssets = await _assetRepository - .getAllByRemoteId(memoryResponse.assets.map((e) => e.id)); + final dbAssets = await _assetRepository.getAllByRemoteId(memoryResponse.assets.map((e) => e.id)); if (dbAssets.isEmpty) { log.warning("No assets found for memory with ID: $id"); return null; } final yearsAgo = DateTime.now().year - memoryResponse.data.year; - final String title = 'years_ago'.t( - args: { - 'years': yearsAgo.toString(), - }, - ); + final String title = 'years_ago'.t(args: {'years': yearsAgo.toString()}); - return Memory( - title: title, - assets: dbAssets, - ); + return Memory(title: title, assets: dbAssets); } catch (error, stack) { log.severe("Cannot get memory with ID: $id", error, stack); return null; diff --git a/mobile/lib/services/network.service.dart b/mobile/lib/services/network.service.dart index de55da8d7c..8622400e7a 100644 --- a/mobile/lib/services/network.service.dart +++ b/mobile/lib/services/network.service.dart @@ -3,10 +3,7 @@ import 'package:immich_mobile/repositories/network.repository.dart'; import 'package:immich_mobile/repositories/permission.repository.dart'; final networkServiceProvider = Provider((ref) { - return NetworkService( - ref.watch(networkRepositoryProvider), - ref.watch(permissionRepositoryProvider), - ); + return NetworkService(ref.watch(networkRepositoryProvider), ref.watch(permissionRepositoryProvider)); }); class NetworkService { diff --git a/mobile/lib/services/oauth.service.dart b/mobile/lib/services/oauth.service.dart index 9a54a8d7c9..99ceca3229 100644 --- a/mobile/lib/services/oauth.service.dart +++ b/mobile/lib/services/oauth.service.dart @@ -11,24 +11,14 @@ class OAuthService { final log = Logger('OAuthService'); OAuthService(this._apiService); - Future getOAuthServerUrl( - String serverUrl, - String state, - String codeChallenge, - ) async { + Future getOAuthServerUrl(String serverUrl, String state, String codeChallenge) async { // Resolve API server endpoint from user provided serverUrl await _apiService.resolveAndSetEndpoint(serverUrl); final redirectUri = '$callbackUrlScheme:///oauth-callback'; - log.info( - "Starting OAuth flow with redirect URI: $redirectUri", - ); + log.info("Starting OAuth flow with redirect URI: $redirectUri"); final dto = await _apiService.oAuthApi.startOAuth( - OAuthConfigDto( - redirectUri: redirectUri, - state: state, - codeChallenge: codeChallenge, - ), + OAuthConfigDto(redirectUri: redirectUri, state: state, codeChallenge: codeChallenge), ); final authUrl = dto?.url; @@ -37,31 +27,17 @@ class OAuthService { return authUrl; } - Future oAuthLogin( - String oauthUrl, - String state, - String codeVerifier, - ) async { - String result = await FlutterWebAuth2.authenticate( - url: oauthUrl, - callbackUrlScheme: callbackUrlScheme, - ); + Future oAuthLogin(String oauthUrl, String state, String codeVerifier) async { + String result = await FlutterWebAuth2.authenticate(url: oauthUrl, callbackUrlScheme: callbackUrlScheme); log.info('Received OAuth callback: $result'); if (result.startsWith('app.immich:/oauth-callback')) { - result = result.replaceAll( - 'app.immich:/oauth-callback', - 'app.immich:///oauth-callback', - ); + result = result.replaceAll('app.immich:/oauth-callback', 'app.immich:///oauth-callback'); } return await _apiService.oAuthApi.finishOAuth( - OAuthCallbackDto( - url: result, - state: state, - codeVerifier: codeVerifier, - ), + OAuthCallbackDto(url: result, state: state, codeVerifier: codeVerifier), ); } } diff --git a/mobile/lib/services/partner.service.dart b/mobile/lib/services/partner.service.dart index ec210fd587..b8e5ae9a4d 100644 --- a/mobile/lib/services/partner.service.dart +++ b/mobile/lib/services/partner.service.dart @@ -20,11 +20,7 @@ class PartnerService { final IsarUserRepository _isarUserRepository; final Logger _log = Logger("PartnerService"); - PartnerService( - this._partnerApiRepository, - this._isarUserRepository, - this._partnerRepository, - ); + PartnerService(this._partnerApiRepository, this._isarUserRepository, this._partnerRepository); Future> getSharedWith() async { return _partnerRepository.getSharedWith(); @@ -45,8 +41,7 @@ class PartnerService { Future removePartner(UserDto partner) async { try { await _partnerApiRepository.delete(partner.id); - await _isarUserRepository - .update(partner.copyWith(isPartnerSharedBy: false)); + await _isarUserRepository.update(partner.copyWith(isPartnerSharedBy: false)); } catch (e) { _log.warning("Failed to remove partner ${partner.id}", e); return false; @@ -57,8 +52,7 @@ class PartnerService { Future addPartner(UserDto partner) async { try { await _partnerApiRepository.create(partner.id); - await _isarUserRepository - .update(partner.copyWith(isPartnerSharedBy: true)); + await _isarUserRepository.update(partner.copyWith(isPartnerSharedBy: true)); return true; } catch (e) { _log.warning("Failed to add partner ${partner.id}", e); @@ -66,17 +60,10 @@ class PartnerService { return false; } - Future updatePartner( - UserDto partner, { - required bool inTimeline, - }) async { + Future updatePartner(UserDto partner, {required bool inTimeline}) async { try { - final dto = await _partnerApiRepository.update( - partner.id, - inTimeline: inTimeline, - ); - await _isarUserRepository - .update(partner.copyWith(inTimeline: dto.inTimeline)); + final dto = await _partnerApiRepository.update(partner.id, inTimeline: inTimeline); + await _isarUserRepository.update(partner.copyWith(inTimeline: dto.inTimeline)); return true; } catch (e) { _log.warning("Failed to update partner ${partner.id}", e); diff --git a/mobile/lib/services/person.service.dart b/mobile/lib/services/person.service.dart index a591ad4f27..37b16a8d29 100644 --- a/mobile/lib/services/person.service.dart +++ b/mobile/lib/services/person.service.dart @@ -11,10 +11,10 @@ part 'person.service.g.dart'; @riverpod PersonService personService(Ref ref) => PersonService( - ref.watch(personApiRepositoryProvider), - ref.watch(assetApiRepositoryProvider), - ref.read(assetRepositoryProvider), - ); + ref.watch(personApiRepositoryProvider), + ref.watch(assetApiRepositoryProvider), + ref.read(assetRepositoryProvider), +); class PersonService { final Logger _log = Logger("PersonService"); @@ -22,13 +22,9 @@ class PersonService { final AssetApiRepository _assetApiRepository; final AssetRepository _assetRepository; - PersonService( - this._personApiRepository, - this._assetApiRepository, - this._assetRepository, - ); + PersonService(this._personApiRepository, this._assetApiRepository, this._assetRepository); - Future> getAllPeople() async { + Future> getAllPeople() async { try { return await _personApiRepository.getAll(); } catch (error, stack) { @@ -40,15 +36,14 @@ class PersonService { Future> getPersonAssets(String id) async { try { final assets = await _assetApiRepository.search(personIds: [id]); - return await _assetRepository - .getAllByRemoteId(assets.map((a) => a.remoteId!)); + return await _assetRepository.getAllByRemoteId(assets.map((a) => a.remoteId!)); } catch (error, stack) { _log.severe("Error while fetching person assets", error, stack); } return []; } - Future updateName(String id, String name) async { + Future updateName(String id, String name) async { try { return await _personApiRepository.update(id, name: name); } catch (error, stack) { diff --git a/mobile/lib/services/search.service.dart b/mobile/lib/services/search.service.dart index aa72a7908b..250fb67d82 100644 --- a/mobile/lib/services/search.service.dart +++ b/mobile/lib/services/search.service.dart @@ -25,11 +25,7 @@ class SearchService { final SearchApiRepository _searchApiRepository; final _log = Logger("SearchService"); - SearchService( - this._apiService, - this._assetRepository, - this._searchApiRepository, - ); + SearchService(this._apiService, this._assetRepository, this._searchApiRepository); Future?> getSearchSuggestions( SearchSuggestionType type, { @@ -61,8 +57,7 @@ class SearchService { } return SearchResult( - assets: await _assetRepository - .getAllByRemoteId(response.assets.items.map((e) => e.id)), + assets: await _assetRepository.getAllByRemoteId(response.assets.items.map((e) => e.id)), nextPage: response.assets.nextPage?.toInt(), ); } catch (error, stackTrace) { diff --git a/mobile/lib/services/secure_storage.service.dart b/mobile/lib/services/secure_storage.service.dart index 38e6deb0d4..95d2e7a2c8 100644 --- a/mobile/lib/services/secure_storage.service.dart +++ b/mobile/lib/services/secure_storage.service.dart @@ -2,9 +2,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/repositories/secure_storage.repository.dart'; final secureStorageServiceProvider = Provider( - (ref) => SecureStorageService( - ref.watch(secureStorageRepositoryProvider), - ), + (ref) => SecureStorageService(ref.watch(secureStorageRepositoryProvider)), ); class SecureStorageService { diff --git a/mobile/lib/services/server_info.service.dart b/mobile/lib/services/server_info.service.dart index 75ce68a73c..4319d9dbae 100644 --- a/mobile/lib/services/server_info.service.dart +++ b/mobile/lib/services/server_info.service.dart @@ -7,11 +7,7 @@ import 'package:immich_mobile/models/server_info/server_version.model.dart'; import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/services/api.service.dart'; -final serverInfoServiceProvider = Provider( - (ref) => ServerInfoService( - ref.watch(apiServiceProvider), - ), -); +final serverInfoServiceProvider = Provider((ref) => ServerInfoService(ref.watch(apiServiceProvider))); class ServerInfoService { final ApiService _apiService; diff --git a/mobile/lib/services/share.service.dart b/mobile/lib/services/share.service.dart index 77afa10fb6..7ba385d71c 100644 --- a/mobile/lib/services/share.service.dart +++ b/mobile/lib/services/share.service.dart @@ -10,8 +10,7 @@ import 'package:path_provider/path_provider.dart'; import 'package:share_plus/share_plus.dart'; import 'api.service.dart'; -final shareServiceProvider = - Provider((ref) => ShareService(ref.watch(apiServiceProvider))); +final shareServiceProvider = Provider((ref) => ShareService(ref.watch(apiServiceProvider))); class ShareService { final ApiService _apiService; @@ -37,14 +36,10 @@ class ShareService { final tempDir = await getTemporaryDirectory(); final fileName = asset.fileName; final tempFile = await File('${tempDir.path}/$fileName').create(); - final res = await _apiService.assetsApi - .downloadAssetWithHttpInfo(asset.remoteId!); + final res = await _apiService.assetsApi.downloadAssetWithHttpInfo(asset.remoteId!); if (res.statusCode != 200) { - _log.severe( - "Asset download for ${asset.fileName} failed", - res.toLoggerString(), - ); + _log.severe("Asset download for ${asset.fileName} failed", res.toLoggerString()); continue; } @@ -59,18 +54,13 @@ class ShareService { } if (downloadedXFiles.length != assets.length) { - _log.warning( - "Partial share - Requested: ${assets.length}, Sharing: ${downloadedXFiles.length}", - ); + _log.warning("Partial share - Requested: ${assets.length}, Sharing: ${downloadedXFiles.length}"); } final size = MediaQuery.of(context).size; Share.shareXFiles( downloadedXFiles, - sharePositionOrigin: Rect.fromPoints( - Offset.zero, - Offset(size.width / 3, size.height), - ), + sharePositionOrigin: Rect.fromPoints(Offset.zero, Offset(size.width / 3, size.height)), ); return true; } catch (error) { diff --git a/mobile/lib/services/share_intent_service.dart b/mobile/lib/services/share_intent_service.dart index e514e5bbdc..fca5c4a188 100644 --- a/mobile/lib/services/share_intent_service.dart +++ b/mobile/lib/services/share_intent_service.dart @@ -2,19 +2,13 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/models/upload/share_intent_attachment.model.dart'; import 'package:immich_mobile/repositories/share_handler.repository.dart'; -final shareIntentServiceProvider = Provider( - (ref) => ShareIntentService( - ref.watch(shareHandlerRepositoryProvider), - ), -); +final shareIntentServiceProvider = Provider((ref) => ShareIntentService(ref.watch(shareHandlerRepositoryProvider))); class ShareIntentService { final ShareHandlerRepository shareHandlerRepository; void Function(List attachments)? onSharedMedia; - ShareIntentService( - this.shareHandlerRepository, - ); + ShareIntentService(this.shareHandlerRepository); void init() { shareHandlerRepository.onSharedMedia = onSharedMedia; diff --git a/mobile/lib/services/shared_link.service.dart b/mobile/lib/services/shared_link.service.dart index a2b5ed9062..25151c234f 100644 --- a/mobile/lib/services/shared_link.service.dart +++ b/mobile/lib/services/shared_link.service.dart @@ -5,9 +5,7 @@ import 'package:immich_mobile/services/api.service.dart'; import 'package:logging/logging.dart'; import 'package:openapi/api.dart'; -final sharedLinkServiceProvider = Provider( - (ref) => SharedLinkService(ref.watch(apiServiceProvider)), -); +final sharedLinkServiceProvider = Provider((ref) => SharedLinkService(ref.watch(apiServiceProvider))); class SharedLinkService { final ApiService _apiService; @@ -18,9 +16,7 @@ class SharedLinkService { Future>> getAllSharedLinks() async { try { final list = await _apiService.sharedLinksApi.getAllSharedLinks(); - return list != null - ? AsyncData(list.map(SharedLink.fromDto).toList()) - : const AsyncData([]); + return list != null ? AsyncData(list.map(SharedLink.fromDto).toList()) : const AsyncData([]); } catch (e, stack) { _log.severe("Failed to fetch shared links", e, stack); return AsyncError(e, stack); @@ -46,8 +42,7 @@ class SharedLinkService { DateTime? expiresAt, }) async { try { - final type = - albumId != null ? SharedLinkType.ALBUM : SharedLinkType.INDIVIDUAL; + final type = albumId != null ? SharedLinkType.ALBUM : SharedLinkType.INDIVIDUAL; SharedLinkCreateDto? dto; if (type == SharedLinkType.ALBUM) { dto = SharedLinkCreateDto( @@ -74,8 +69,7 @@ class SharedLinkService { } if (dto != null) { - final responseDto = - await _apiService.sharedLinksApi.createSharedLink(dto); + final responseDto = await _apiService.sharedLinksApi.createSharedLink(dto); if (responseDto != null) { return SharedLink.fromDto(responseDto); } diff --git a/mobile/lib/services/stack.service.dart b/mobile/lib/services/stack.service.dart index e2ab6c804b..c24b9fb7f8 100644 --- a/mobile/lib/services/stack.service.dart +++ b/mobile/lib/services/stack.service.dart @@ -23,24 +23,16 @@ class StackService { Future createStack(List assetIds) async { try { - return _api.stacksApi.createStack( - StackCreateDto(assetIds: assetIds), - ); + return _api.stacksApi.createStack(StackCreateDto(assetIds: assetIds)); } catch (error) { debugPrint("Error while creating stack: $error"); } return null; } - Future updateStack( - String stackId, - String primaryAssetId, - ) async { + Future updateStack(String stackId, String primaryAssetId) async { try { - return await _api.stacksApi.updateStack( - stackId, - StackUpdateDto(primaryAssetId: primaryAssetId), - ); + return await _api.stacksApi.updateStack(stackId, StackUpdateDto(primaryAssetId: primaryAssetId)); } catch (error) { debugPrint("Error while updating stack children: $error"); } @@ -60,8 +52,7 @@ class StackService { removeAssets.add(asset); } - await _assetRepository - .transaction(() => _assetRepository.updateAll(removeAssets)); + await _assetRepository.transaction(() => _assetRepository.updateAll(removeAssets)); } catch (error) { debugPrint("Error while deleting stack: $error"); } @@ -69,8 +60,5 @@ class StackService { } final stackServiceProvider = Provider( - (ref) => StackService( - ref.watch(apiServiceProvider), - ref.watch(assetRepositoryProvider), - ), + (ref) => StackService(ref.watch(apiServiceProvider), ref.watch(assetRepositoryProvider)), ); diff --git a/mobile/lib/services/sync.service.dart b/mobile/lib/services/sync.service.dart index 5a95be2237..7b420413cf 100644 --- a/mobile/lib/services/sync.service.dart +++ b/mobile/lib/services/sync.service.dart @@ -94,63 +94,44 @@ class SyncService { /// Syncs users from the server to the local database /// Returns `true`if there were any changes - Future syncUsersFromServer(List users) => - _lock.run(() => _syncUsersFromServer(users)); + Future syncUsersFromServer(List users) => _lock.run(() => _syncUsersFromServer(users)); /// Syncs remote assets owned by the logged-in user to the DB /// Returns `true` if there were any changes Future syncRemoteAssetsToDb({ required List users, - required Future<(List? toUpsert, List? toDelete)> Function( - List users, - DateTime since, - ) getChangedAssets, - required FutureOr?> Function(UserDto user, DateTime until) - loadAssets, - }) => - _lock.run( - () async => - await _syncRemoteAssetChanges(users, getChangedAssets) ?? - await _syncRemoteAssetsFull(getUsersFromServer, loadAssets), - ); + required Future<(List? toUpsert, List? toDelete)> Function(List users, DateTime since) + getChangedAssets, + required FutureOr?> Function(UserDto user, DateTime until) loadAssets, + }) => _lock.run( + () async => + await _syncRemoteAssetChanges(users, getChangedAssets) ?? + await _syncRemoteAssetsFull(getUsersFromServer, loadAssets), + ); /// Syncs remote albums to the database /// returns `true` if there were any changes - Future syncRemoteAlbumsToDb( - List remote, - ) => - _lock.run(() => _syncRemoteAlbumsToDb(remote)); + Future syncRemoteAlbumsToDb(List remote) => _lock.run(() => _syncRemoteAlbumsToDb(remote)); /// Syncs all device albums and their assets to the database /// Returns `true` if there were any changes - Future syncLocalAlbumAssetsToDb( - List onDevice, [ - Set? excludedAssets, - ]) => + Future syncLocalAlbumAssetsToDb(List onDevice, [Set? excludedAssets]) => _lock.run(() => _syncLocalAlbumAssetsToDb(onDevice, excludedAssets)); /// returns all Asset IDs that are not contained in the existing list - List sharedAssetsToRemove( - List deleteCandidates, - List existing, - ) { + List sharedAssetsToRemove(List deleteCandidates, List existing) { if (deleteCandidates.isEmpty) { return []; } deleteCandidates.sort(Asset.compareById); existing.sort(Asset.compareById); - return _diffAssets(existing, deleteCandidates, compare: Asset.compareById) - .$3 - .map((e) => e.id) - .toList(); + return _diffAssets(existing, deleteCandidates, compare: Asset.compareById).$3.map((e) => e.id).toList(); } /// Syncs a new asset to the db. Returns `true` if successful - Future syncNewAssetToDb(Asset newAsset) => - _lock.run(() => _syncNewAssetToDb(newAsset)); + Future syncNewAssetToDb(Asset newAsset) => _lock.run(() => _syncNewAssetToDb(newAsset)); - Future removeAllLocalAlbumsAndAssets() => - _lock.run(_removeAllLocalAlbumsAndAssets); + Future removeAllLocalAlbumsAndAssets() => _lock.run(_removeAllLocalAlbumsAndAssets); // private methods: @@ -189,8 +170,7 @@ class SyncService { /// Syncs a new asset to the db. Returns `true` if successful Future _syncNewAssetToDb(Asset a) async { - final Asset? inDb = - await _assetRepository.getByOwnerIdChecksum(a.ownerId, a.checksum); + final Asset? inDb = await _assetRepository.getByOwnerIdChecksum(a.ownerId, a.checksum); if (inDb != null) { // unify local/remote assets by replacing the // local-only asset in the DB with a local&remote asset @@ -208,14 +188,11 @@ class SyncService { /// Efficiently syncs assets via changes. Returns `null` when a full sync is required. Future _syncRemoteAssetChanges( List users, - Future<(List? toUpsert, List? toDelete)> Function( - List users, - DateTime since, - ) getChangedAssets, + Future<(List? toUpsert, List? toDelete)> Function(List users, DateTime since) + getChangedAssets, ) async { final currentUser = _userService.getMyUser(); - final DateTime? since = - (await _eTagRepository.get(currentUser.id))?.time?.toUtc(); + final DateTime? since = (await _eTagRepository.get(currentUser.id))?.time?.toUtc(); if (since == null) return null; final DateTime now = DateTime.now(); final (toUpsert, toDelete) = await getChangedAssets(users, since); @@ -244,14 +221,9 @@ class SyncService { Future _moveToTrashMatchedAssets(Iterable idsToDelete) async { final List localAssets = await _assetRepository.getAllLocal(); - final List matchedAssets = localAssets - .where((asset) => idsToDelete.contains(asset.remoteId)) - .toList(); + final List matchedAssets = localAssets.where((asset) => idsToDelete.contains(asset.remoteId)).toList(); - final mediaUrls = await Future.wait( - matchedAssets - .map((asset) => asset.local?.getMediaUrl() ?? Future.value(null)), - ); + final mediaUrls = await Future.wait(matchedAssets.map((asset) => asset.local?.getMediaUrl() ?? Future.value(null))); await _localFilesManager.moveToTrash(mediaUrls.nonNulls.toList()); } @@ -259,18 +231,9 @@ class SyncService { /// Deletes remote-only assets, updates merged assets to be local-only Future handleRemoteAssetRemoval(List idsToDelete) async { return _assetRepository.transaction(() async { - await _assetRepository.deleteAllByRemoteId( - idsToDelete, - state: AssetState.remote, - ); - final merged = await _assetRepository.getAllByRemoteId( - idsToDelete, - state: AssetState.merged, - ); - if (Platform.isAndroid && - _appSettingsService.getSetting( - AppSettingsEnum.manageLocalMediaAndroid, - )) { + await _assetRepository.deleteAllByRemoteId(idsToDelete, state: AssetState.remote); + final merged = await _assetRepository.getAllByRemoteId(idsToDelete, state: AssetState.merged); + if (Platform.isAndroid && _appSettingsService.getSetting(AppSettingsEnum.manageLocalMediaAndroid)) { await _moveToTrashMatchedAssets(idsToDelete); } if (merged.isEmpty) return; @@ -316,10 +279,7 @@ class SyncService { if (remote == null) { return false; } - final List inDb = await _assetRepository.getAll( - ownerId: user.id, - sortBy: AssetSort.checksum, - ); + final List inDb = await _assetRepository.getAll(ownerId: user.id, sortBy: AssetSort.checksum); assert(inDb.isSorted(Asset.compareByChecksum), "inDb not sorted!"); remote.sort(Asset.compareByChecksum); @@ -355,15 +315,10 @@ class SyncService { /// Syncs remote albums to the database /// returns `true` if there were any changes - Future _syncRemoteAlbumsToDb( - List remoteAlbums, - ) async { + Future _syncRemoteAlbumsToDb(List remoteAlbums) async { remoteAlbums.sortBy((e) => e.remoteId!); - final List dbAlbums = await _albumRepository.getAll( - remote: true, - sortBy: AlbumSort.remoteId, - ); + final List dbAlbums = await _albumRepository.getAll(remote: true, sortBy: AlbumSort.remoteId); final List toDelete = []; final List existing = []; @@ -371,10 +326,8 @@ class SyncService { final bool changes = await diffSortedLists( remoteAlbums, dbAlbums, - compare: (remoteAlbum, dbAlbum) => - remoteAlbum.remoteId!.compareTo(dbAlbum.remoteId!), - both: (remoteAlbum, dbAlbum) => - _syncRemoteAlbum(remoteAlbum, dbAlbum, toDelete, existing), + compare: (remoteAlbum, dbAlbum) => remoteAlbum.remoteId!.compareTo(dbAlbum.remoteId!), + both: (remoteAlbum, dbAlbum) => _syncRemoteAlbum(remoteAlbum, dbAlbum, toDelete, existing), onlyFirst: (remoteAlbum) => _addAlbumFromServer(remoteAlbum, existing), onlySecond: (dbAlbum) => _removeAlbumFromDb(dbAlbum, toDelete), ); @@ -393,12 +346,7 @@ class SyncService { /// syncs albums from the server to the local database (does not support /// syncing changes from local back to server) /// accumulates - Future _syncRemoteAlbum( - Album dto, - Album album, - List deleteCandidates, - List existing, - ) async { + Future _syncRemoteAlbum(Album dto, Album album, List deleteCandidates, List existing) async { if (!_hasRemoteAlbumChanged(dto, album)) { return false; } @@ -407,25 +355,16 @@ class SyncService { final originalDto = dto; dto = await _albumApiRepository.get(dto.remoteId!); - final assetsInDb = await _assetRepository.getByAlbum( - album, - sortBy: AssetSort.ownerIdChecksum, - ); + final assetsInDb = await _assetRepository.getByAlbum(album, sortBy: AssetSort.ownerIdChecksum); assert(assetsInDb.isSorted(Asset.compareByOwnerChecksum), "inDb unsorted!"); final List assetsOnRemote = dto.remoteAssets.toList(); assetsOnRemote.sort(Asset.compareByOwnerChecksum); - final (toAdd, toUpdate, toUnlink) = _diffAssets( - assetsOnRemote, - assetsInDb, - compare: Asset.compareByOwnerChecksum, - ); + final (toAdd, toUpdate, toUnlink) = _diffAssets(assetsOnRemote, assetsInDb, compare: Asset.compareByOwnerChecksum); // update shared users - final List sharedUsers = - album.sharedUsers.map((u) => u.toDto()).toList(growable: false); + final List sharedUsers = album.sharedUsers.map((u) => u.toDto()).toList(growable: false); sharedUsers.sort((a, b) => a.id.compareTo(b.id)); - final List users = dto.remoteUsers.map((u) => u.toDto()).toList() - ..sort((a, b) => a.id.compareTo(b.id)); + final List users = dto.remoteUsers.map((u) => u.toDto()).toList()..sort((a, b) => a.id.compareTo(b.id)); final List userIdsToAdd = []; final List usersToUnlink = []; diffSortedListsSync( @@ -456,10 +395,8 @@ class SyncService { album.sortOrder = dto.sortOrder; final remoteThumbnailAssetId = dto.remoteThumbnailAssetId; - if (remoteThumbnailAssetId != null && - album.thumbnail.value?.remoteId != remoteThumbnailAssetId) { - album.thumbnail.value = - await _assetRepository.getByRemoteId(remoteThumbnailAssetId); + if (remoteThumbnailAssetId != null && album.thumbnail.value?.remoteId != remoteThumbnailAssetId) { + album.thumbnail.value = await _assetRepository.getByRemoteId(remoteThumbnailAssetId); } // write & commit all changes to DB @@ -480,8 +417,7 @@ class SyncService { if (album.shared || dto.shared) { final userId = (_userService.getMyUser()).id; - final foreign = - await _assetRepository.getByAlbum(album, notOwnedBy: [userId]); + final foreign = await _assetRepository.getByAlbum(album, notOwnedBy: [userId]); existing.addAll(foreign); // delete assets in DB unless they belong to this user or part of some other shared album @@ -495,18 +431,14 @@ class SyncService { /// Adds a remote album to the database while making sure to add any foreign /// (shared) assets to the database beforehand /// accumulates assets already existing in the database - Future _addAlbumFromServer( - Album album, - List existing, - ) async { + Future _addAlbumFromServer(Album album, List existing) async { if (album.remoteAssetCount != album.remoteAssets.length) { album = await _albumApiRepository.get(album.remoteId!); } if (album.remoteAssetCount == album.remoteAssets.length) { // in case an album contains assets not yet present in local DB: // put missing album assets into local DB - final (existingInDb, updated) = - await _linkWithExistingFromDb(album.remoteAssets.toList()); + final (existingInDb, updated) = await _linkWithExistingFromDb(album.remoteAssets.toList()); existing.addAll(existingInDb); await upsertAssetsWithExif(updated); @@ -514,28 +446,23 @@ class SyncService { await _albumRepository.create(album); } else { _log.warning( - "Failed to add album from server: assetCount ${album.remoteAssetCount} != " - "asset array length ${album.remoteAssets.length} for album ${album.name}"); + "Failed to add album from server: assetCount ${album.remoteAssetCount} != " + "asset array length ${album.remoteAssets.length} for album ${album.name}", + ); } } /// Accumulates all suitable album assets to the `deleteCandidates` and /// removes the album from the database. - Future _removeAlbumFromDb( - Album album, - List deleteCandidates, - ) async { + Future _removeAlbumFromDb(Album album, List deleteCandidates) async { if (album.isLocal) { _log.info("Removing local album $album from DB"); // delete assets in DB unless they are remote or part of some other album - deleteCandidates.addAll( - await _assetRepository.getByAlbum(album, state: AssetState.local), - ); + deleteCandidates.addAll(await _assetRepository.getByAlbum(album, state: AssetState.local)); } else if (album.shared) { // delete assets in DB unless they belong to this user or are part of some other shared album or belong to a partner final userIds = (await _getAllAccessibleUsers()).map((user) => user.id); - final orphanedAssets = - await _assetRepository.getByAlbum(album, notOwnedBy: userIds); + final orphanedAssets = await _assetRepository.getByAlbum(album, notOwnedBy: userIds); deleteCandidates.addAll(orphanedAssets); } try { @@ -548,45 +475,28 @@ class SyncService { /// Syncs all device albums and their assets to the database /// Returns `true` if there were any changes - Future _syncLocalAlbumAssetsToDb( - List onDevice, [ - Set? excludedAssets, - ]) async { + Future _syncLocalAlbumAssetsToDb(List onDevice, [Set? excludedAssets]) async { onDevice.sort((a, b) => a.localId!.compareTo(b.localId!)); - final inDb = - await _albumRepository.getAll(remote: false, sortBy: AlbumSort.localId); + final inDb = await _albumRepository.getAll(remote: false, sortBy: AlbumSort.localId); final List deleteCandidates = []; final List existing = []; final bool anyChanges = await diffSortedLists( onDevice, inDb, compare: (Album a, Album b) => a.localId!.compareTo(b.localId!), - both: (Album a, Album b) => _syncAlbumInDbAndOnDevice( - a, - b, - deleteCandidates, - existing, - excludedAssets, - ), + both: (Album a, Album b) => _syncAlbumInDbAndOnDevice(a, b, deleteCandidates, existing, excludedAssets), onlyFirst: (Album a) => _addAlbumFromDevice(a, existing, excludedAssets), onlySecond: (Album a) => _removeAlbumFromDb(a, deleteCandidates), ); - _log.fine( - "Syncing all local albums almost done. Collected ${deleteCandidates.length} asset candidates to delete", - ); - final (toDelete, toUpdate) = - _handleAssetRemoval(deleteCandidates, existing, remote: false); - _log.fine( - "${toDelete.length} assets to delete, ${toUpdate.length} to update", - ); + _log.fine("Syncing all local albums almost done. Collected ${deleteCandidates.length} asset candidates to delete"); + final (toDelete, toUpdate) = _handleAssetRemoval(deleteCandidates, existing, remote: false); + _log.fine("${toDelete.length} assets to delete, ${toUpdate.length} to update"); if (toDelete.isNotEmpty || toUpdate.isNotEmpty) { await _assetRepository.transaction(() async { await _assetRepository.deleteByIds(toDelete); await _assetRepository.updateAll(toUpdate); }); - _log.info( - "Removed ${toDelete.length} and updated ${toUpdate.length} local assets from DB", - ); + _log.info("Removed ${toDelete.length} and updated ${toUpdate.length} local assets from DB"); } return anyChanges; } @@ -604,15 +514,11 @@ class SyncService { ]) async { _log.info("Syncing a local album to DB: ${deviceAlbum.name}"); if (!forceRefresh && !await _hasAlbumChangeOnDevice(deviceAlbum, dbAlbum)) { - _log.info( - "Local album ${deviceAlbum.name} has not changed. Skipping sync.", - ); + _log.info("Local album ${deviceAlbum.name} has not changed. Skipping sync."); return false; } _log.info("Local album ${deviceAlbum.name} has changed. Syncing..."); - if (!forceRefresh && - excludedAssets == null && - await _syncDeviceAlbumFast(deviceAlbum, dbAlbum)) { + if (!forceRefresh && excludedAssets == null && await _syncDeviceAlbumFast(deviceAlbum, dbAlbum)) { _log.info("Fast synced local album ${deviceAlbum.name} to DB"); return true; } @@ -624,12 +530,8 @@ class SyncService { ); assert(inDb.isSorted(Asset.compareByChecksum), "inDb not sorted!"); - final int assetCountOnDevice = - await _albumMediaRepository.getAssetCount(deviceAlbum.localId!); - final List onDevice = await _getHashedAssets( - deviceAlbum, - excludedAssets: excludedAssets, - ); + final int assetCountOnDevice = await _albumMediaRepository.getAssetCount(deviceAlbum.localId!); + final List onDevice = await _getHashedAssets(deviceAlbum, excludedAssets: excludedAssets); _removeDuplicates(onDevice); // _removeDuplicates sorts `onDevice` by checksum final (toAdd, toUpdate, toDelete) = _diffAssets(onDevice, inDb); @@ -640,18 +542,9 @@ class SyncService { dbAlbum.description == deviceAlbum.description && dbAlbum.modifiedAt.isAtSameMomentAs(deviceAlbum.modifiedAt)) { // changes only affeted excluded albums - _log.info( - "Only excluded assets in local album ${deviceAlbum.name} changed. Stopping sync.", - ); - if (assetCountOnDevice != - (await _eTagRepository.getById(deviceAlbum.eTagKeyAssetCount)) - ?.assetCount) { - await _eTagRepository.upsertAll([ - ETag( - id: deviceAlbum.eTagKeyAssetCount, - assetCount: assetCountOnDevice, - ), - ]); + _log.info("Only excluded assets in local album ${deviceAlbum.name} changed. Stopping sync."); + if (assetCountOnDevice != (await _eTagRepository.getById(deviceAlbum.eTagKeyAssetCount))?.assetCount) { + await _eTagRepository.upsertAll([ETag(id: deviceAlbum.eTagKeyAssetCount, assetCount: assetCountOnDevice)]); } return false; } @@ -667,8 +560,7 @@ class SyncService { dbAlbum.name = deviceAlbum.name; dbAlbum.description = deviceAlbum.description; dbAlbum.modifiedAt = deviceAlbum.modifiedAt; - if (dbAlbum.thumbnail.value != null && - toDelete.contains(dbAlbum.thumbnail.value)) { + if (dbAlbum.thumbnail.value != null && toDelete.contains(dbAlbum.thumbnail.value)) { dbAlbum.thumbnail.value = null; } try { @@ -678,12 +570,7 @@ class SyncService { await _albumRepository.removeAssets(dbAlbum, toDelete); await _albumRepository.recalculateMetadata(dbAlbum); await _albumRepository.update(dbAlbum); - await _eTagRepository.upsertAll([ - ETag( - id: deviceAlbum.eTagKeyAssetCount, - assetCount: assetCountOnDevice, - ), - ]); + await _eTagRepository.upsertAll([ETag(id: deviceAlbum.eTagKeyAssetCount, assetCount: assetCountOnDevice)]); }); _log.info("Synced changes of local album ${deviceAlbum.name} to DB"); } catch (e) { @@ -697,21 +584,13 @@ class SyncService { /// returns `true` if successful, else `false` Future _syncDeviceAlbumFast(Album deviceAlbum, Album dbAlbum) async { if (!deviceAlbum.modifiedAt.isAfter(dbAlbum.modifiedAt)) { - _log.info( - "Local album ${deviceAlbum.name} has not changed. Skipping sync.", - ); + _log.info("Local album ${deviceAlbum.name} has not changed. Skipping sync."); return false; } - final int totalOnDevice = - await _albumMediaRepository.getAssetCount(deviceAlbum.localId!); - final int lastKnownTotal = - (await _eTagRepository.getById(deviceAlbum.eTagKeyAssetCount)) - ?.assetCount ?? - 0; + final int totalOnDevice = await _albumMediaRepository.getAssetCount(deviceAlbum.localId!); + final int lastKnownTotal = (await _eTagRepository.getById(deviceAlbum.eTagKeyAssetCount))?.assetCount ?? 0; if (totalOnDevice <= lastKnownTotal) { - _log.info( - "Local album ${deviceAlbum.name} totalOnDevice is less than lastKnownTotal. Skipping sync.", - ); + _log.info("Local album ${deviceAlbum.name} totalOnDevice is less than lastKnownTotal. Skipping sync."); return false; } final List newAssets = await _getHashedAssets( @@ -735,16 +614,11 @@ class SyncService { await _albumRepository.addAssets(dbAlbum, existingInDb + updated); await _albumRepository.recalculateMetadata(dbAlbum); await _albumRepository.update(dbAlbum); - await _eTagRepository.upsertAll( - [ETag(id: deviceAlbum.eTagKeyAssetCount, assetCount: totalOnDevice)], - ); + await _eTagRepository.upsertAll([ETag(id: deviceAlbum.eTagKeyAssetCount, assetCount: totalOnDevice)]); }); _log.info("Fast synced local album ${deviceAlbum.name} to DB"); } catch (e) { - _log.severe( - "Failed to fast sync local album ${deviceAlbum.name} to DB", - e, - ); + _log.severe("Failed to fast sync local album ${deviceAlbum.name} to DB", e); return false; } @@ -753,21 +627,12 @@ class SyncService { /// Adds a new album from the device to the database and Accumulates all /// assets already existing in the database to the list of `existing` assets - Future _addAlbumFromDevice( - Album album, - List existing, [ - Set? excludedAssets, - ]) async { + Future _addAlbumFromDevice(Album album, List existing, [Set? excludedAssets]) async { _log.info("Adding a new local album to DB: ${album.name}"); - final assets = await _getHashedAssets( - album, - excludedAssets: excludedAssets, - ); + final assets = await _getHashedAssets(album, excludedAssets: excludedAssets); _removeDuplicates(assets); final (existingInDb, updated) = await _linkWithExistingFromDb(assets); - _log.info( - "${existingInDb.length} assets already existed in DB, to upsert ${updated.length}", - ); + _log.info("${existingInDb.length} assets already existed in DB, to upsert ${updated.length}"); await upsertAssetsWithExif(updated); existing.addAll(existingInDb); album.assets.addAll(existingInDb); @@ -776,11 +641,8 @@ class SyncService { album.thumbnail.value = thumb; try { await _albumRepository.create(album); - final int assetCount = - await _albumMediaRepository.getAssetCount(album.localId!); - await _eTagRepository.upsertAll([ - ETag(id: album.eTagKeyAssetCount, assetCount: assetCount), - ]); + final int assetCount = await _albumMediaRepository.getAssetCount(album.localId!); + await _eTagRepository.upsertAll([ETag(id: album.eTagKeyAssetCount, assetCount: assetCount)]); _log.info("Added a new local album to DB: ${album.name}"); } catch (e) { _log.severe("Failed to add new local album ${album.name} to DB", e); @@ -788,9 +650,7 @@ class SyncService { } /// Returns a tuple (existing, updated) - Future<(List existing, List updated)> _linkWithExistingFromDb( - List assets, - ) async { + Future<(List existing, List updated)> _linkWithExistingFromDb(List assets) async { if (assets.isEmpty) return ([].cast(), [].cast()); final List inDb = await _assetRepository.getAllByOwnerIdChecksum( @@ -824,17 +684,12 @@ class SyncService { if (asset.isTrashed) { final mediaUrl = await asset.local?.getMediaUrl(); if (mediaUrl == null) { - _log.warning( - "Failed to get media URL for asset ${asset.name} while moving to trash", - ); + _log.warning("Failed to get media URL for asset ${asset.name} while moving to trash"); continue; } trashMediaUrls.add(mediaUrl); } else { - await _localFilesManager.restoreFromTrash( - asset.fileName, - asset.type.index, - ); + await _localFilesManager.restoreFromTrash(asset.fileName, asset.type.index); } } @@ -847,10 +702,7 @@ class SyncService { Future upsertAssetsWithExif(List assets) async { if (assets.isEmpty) return; - if (Platform.isAndroid && - _appSettingsService.getSetting( - AppSettingsEnum.manageLocalMediaAndroid, - )) { + if (Platform.isAndroid && _appSettingsService.getSetting(AppSettingsEnum.manageLocalMediaAndroid)) { _toggleTrashStatusForAssets(assets); } @@ -877,21 +729,15 @@ class SyncService { final Asset? b = inDb[i]; if (b == null) { if (!a.isInDb) { - _log.warning( - "Trying to update an asset that does not exist in DB:\n$a", - ); + _log.warning("Trying to update an asset that does not exist in DB:\n$a"); } } else if (a.id != b.id) { - _log.warning( - "Trying to insert another asset with the same checksum+owner. In DB:\n$b\nTo insert:\n$a", - ); + _log.warning("Trying to insert another asset with the same checksum+owner. In DB:\n$b\nTo insert:\n$a"); } } for (int i = 1; i < assets.length; i++) { if (Asset.compareByOwnerChecksum(assets[i - 1], assets[i]) == 0) { - _log.warning( - "Trying to insert duplicate assets:\n${assets[i - 1]}\n${assets[i]}", - ); + _log.warning("Trying to insert duplicate assets:\n${assets[i - 1]}\n${assets[i]}"); } } } @@ -922,10 +768,7 @@ class SyncService { List _removeDuplicates(List assets) { final int before = assets.length; assets.sort(Asset.compareByOwnerChecksumCreatedModified); - assets.uniqueConsecutive( - compare: Asset.compareByOwnerChecksum, - onDuplicate: (a, b) => {}, - ); + assets.uniqueConsecutive(compare: Asset.compareByOwnerChecksum, onDuplicate: (a, b) => {}); final int duplicates = before - assets.length; if (duplicates > 0) { _log.warning("Ignored $duplicates duplicate assets on device"); @@ -934,23 +777,18 @@ class SyncService { } /// returns `true` if the albums differ on the surface - Future _hasAlbumChangeOnDevice( - Album deviceAlbum, - Album dbAlbum, - ) async { + Future _hasAlbumChangeOnDevice(Album deviceAlbum, Album dbAlbum) async { return deviceAlbum.name != dbAlbum.name || deviceAlbum.description != dbAlbum.description || !deviceAlbum.modifiedAt.isAtSameMomentAs(dbAlbum.modifiedAt) || await _albumMediaRepository.getAssetCount(deviceAlbum.localId!) != - (await _eTagRepository.getById(deviceAlbum.eTagKeyAssetCount)) - ?.assetCount; + (await _eTagRepository.getById(deviceAlbum.eTagKeyAssetCount))?.assetCount; } Future _removeAllLocalAlbumsAndAssets() async { try { final assets = await _assetRepository.getAllLocal(); - final (toDelete, toUpdate) = - _handleAssetRemoval(assets, [], remote: false); + final (toDelete, toUpdate) = _handleAssetRemoval(assets, [], remote: false); await _assetRepository.transaction(() async { await _assetRepository.deleteByIds(toDelete); await _assetRepository.updateAll(toUpdate); @@ -971,10 +809,8 @@ class SyncService { _log.warning("Failed to fetch users", e); users = null; } - final List sharedBy = - await _partnerApiRepository.getAll(Direction.sharedByMe); - final List sharedWith = - await _partnerApiRepository.getAll(Direction.sharedWithMe); + final List sharedBy = await _partnerApiRepository.getAll(Direction.sharedByMe); + final List sharedWith = await _partnerApiRepository.getAll(Direction.sharedWithMe); if (users == null) { _log.warning("Failed to refresh users"); @@ -1006,9 +842,7 @@ class SyncService { sharedWith, compare: (UserDto a, UserDto b) => a.id.compareTo(b.id), both: (UserDto a, UserDto b) { - updatedSharedWith.add( - a.copyWith(inTimeline: b.inTimeline, isPartnerSharedWith: true), - ); + updatedSharedWith.add(a.copyWith(inTimeline: b.inTimeline, isPartnerSharedWith: true)); return true; }, onlyFirst: (UserDto a) => updatedSharedWith.add(a), @@ -1105,8 +939,5 @@ bool _hasRemoteAlbumChanged(Album remoteAlbum, Album dbAlbum) { !remoteAlbum.modifiedAt.isAtSameMomentAs(dbAlbum.modifiedAt) || !isAtSameMomentAs(remoteAlbum.startDate, dbAlbum.startDate) || !isAtSameMomentAs(remoteAlbum.endDate, dbAlbum.endDate) || - !isAtSameMomentAs( - remoteAlbum.lastModifiedAssetTimestamp, - dbAlbum.lastModifiedAssetTimestamp, - ); + !isAtSameMomentAs(remoteAlbum.lastModifiedAssetTimestamp, dbAlbum.lastModifiedAssetTimestamp); } diff --git a/mobile/lib/services/timeline.service.dart b/mobile/lib/services/timeline.service.dart index 47ad17fc25..eaff1027d8 100644 --- a/mobile/lib/services/timeline.service.dart +++ b/mobile/lib/services/timeline.service.dart @@ -21,11 +21,7 @@ class TimelineService { final AppSettingsService _appSettingsService; final UserService _userService; - const TimelineService( - this._timelineRepository, - this._appSettingsService, - this._userService, - ); + const TimelineService(this._timelineRepository, this._appSettingsService, this._userService); Future> getTimelineUserIds() async { final me = _userService.getMyUser(); @@ -42,10 +38,7 @@ class TimelineService { } Stream watchMultiUsersTimeline(List userIds) { - return _timelineRepository.watchMultiUsersTimeline( - userIds, - _getGroupByOption(), - ); + return _timelineRepository.watchMultiUsersTimeline(userIds, _getGroupByOption()); } Stream watchArchiveTimeline() async* { @@ -61,10 +54,7 @@ class TimelineService { } Stream watchAlbumTimeline(Album album) async* { - yield* _timelineRepository.watchAlbumTimeline( - album, - _getGroupByOption(), - ); + yield* _timelineRepository.watchAlbumTimeline(album, _getGroupByOption()); } Stream watchTrashTimeline() async* { @@ -79,10 +69,7 @@ class TimelineService { return _timelineRepository.watchAllVideosTimeline(user.id); } - Future getTimelineFromAssets( - List assets, - GroupAssetsBy? groupBy, - ) { + Future getTimelineFromAssets(List assets, GroupAssetsBy? groupBy) { GroupAssetsBy groupOption = GroupAssetsBy.none; if (groupBy == null) { groupOption = _getGroupByOption(); @@ -90,10 +77,7 @@ class TimelineService { groupOption = groupBy; } - return _timelineRepository.getTimelineFromAssets( - assets, - groupOption, - ); + return _timelineRepository.getTimelineFromAssets(assets, groupOption); } Stream watchAssetSelectionTimeline() async* { @@ -103,16 +87,12 @@ class TimelineService { } GroupAssetsBy _getGroupByOption() { - return GroupAssetsBy - .values[_appSettingsService.getSetting(AppSettingsEnum.groupAssetsBy)]; + return GroupAssetsBy.values[_appSettingsService.getSetting(AppSettingsEnum.groupAssetsBy)]; } Stream watchLockedTimelineProvider() async* { final user = _userService.getMyUser(); - yield* _timelineRepository.watchLockedTimeline( - user.id, - _getGroupByOption(), - ); + yield* _timelineRepository.watchLockedTimeline(user.id, _getGroupByOption()); } } diff --git a/mobile/lib/services/trash.service.dart b/mobile/lib/services/trash.service.dart index 6cd7dfc641..2c51a68c59 100644 --- a/mobile/lib/services/trash.service.dart +++ b/mobile/lib/services/trash.service.dart @@ -20,17 +20,11 @@ class TrashService { final AssetRepository _assetRepository; final UserService _userService; - const TrashService( - this._apiService, - this._assetRepository, - this._userService, - ); + const TrashService(this._apiService, this._assetRepository, this._userService); Future restoreAssets(Iterable assetList) async { final remoteAssets = assetList.where((a) => a.isRemote); - await _apiService.trashApi.restoreAssets( - BulkIdsDto(ids: remoteAssets.map((e) => e.remoteId!).toList()), - ); + await _apiService.trashApi.restoreAssets(BulkIdsDto(ids: remoteAssets.map((e) => e.remoteId!).toList())); final updatedAssets = remoteAssets.map((asset) { asset.isTrashed = false; @@ -49,15 +43,9 @@ class TrashService { final ids = trashedAssets.map((e) => e.remoteId!).toList(); await _assetRepository.transaction(() async { - await _assetRepository.deleteAllByRemoteId( - ids, - state: AssetState.remote, - ); + await _assetRepository.deleteAllByRemoteId(ids, state: AssetState.remote); - final merged = await _assetRepository.getAllByRemoteId( - ids, - state: AssetState.merged, - ); + final merged = await _assetRepository.getAllByRemoteId(ids, state: AssetState.merged); if (merged.isEmpty) { return; } diff --git a/mobile/lib/services/upload.service.dart b/mobile/lib/services/upload.service.dart index 18f90ab844..9e5193c8cb 100644 --- a/mobile/lib/services/upload.service.dart +++ b/mobile/lib/services/upload.service.dart @@ -1,84 +1,336 @@ +import 'dart:async'; +import 'dart:convert'; import 'dart:io'; import 'package:background_downloader/background_downloader.dart'; +import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/constants.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/store.entity.dart'; +import 'package:immich_mobile/infrastructure/repositories/backup.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart'; +import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart'; +import 'package:immich_mobile/providers/app_settings.provider.dart'; +import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/storage.provider.dart'; import 'package:immich_mobile/repositories/upload.repository.dart'; import 'package:immich_mobile/services/api.service.dart'; -import 'package:immich_mobile/utils/upload.dart'; -import 'package:path/path.dart'; -// import 'package:logging/logging.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; +import 'package:path/path.dart' as p; -final uploadServiceProvider = Provider( - (ref) => UploadService( +final uploadServiceProvider = Provider((ref) { + final service = UploadService( ref.watch(uploadRepositoryProvider), - ), -); + ref.watch(backupRepositoryProvider), + ref.watch(storageRepositoryProvider), + ref.watch(localAssetRepository), + ref.watch(appSettingsServiceProvider), + ); + + ref.onDispose(service.dispose); + return service; +}); class UploadService { - final UploadRepository _uploadRepository; - // final Logger _log = Logger("UploadService"); - void Function(TaskStatusUpdate)? onUploadStatus; - void Function(TaskProgressUpdate)? onTaskProgress; - UploadService( this._uploadRepository, + this._backupRepository, + this._storageRepository, + this._localAssetRepository, + this._appSettingsService, ) { _uploadRepository.onUploadStatus = _onUploadCallback; _uploadRepository.onTaskProgress = _onTaskProgressCallback; } + final UploadRepository _uploadRepository; + final DriftBackupRepository _backupRepository; + final StorageRepository _storageRepository; + final DriftLocalAssetRepository _localAssetRepository; + final AppSettingsService _appSettingsService; + + final StreamController _taskStatusController = StreamController.broadcast(); + final StreamController _taskProgressController = StreamController.broadcast(); + + Stream get taskStatusStream => _taskStatusController.stream; + Stream get taskProgressStream => _taskProgressController.stream; + + bool shouldAbortQueuingTasks = false; + void _onTaskProgressCallback(TaskProgressUpdate update) { - onTaskProgress?.call(update); + if (!_taskProgressController.isClosed) { + _taskProgressController.add(update); + } } void _onUploadCallback(TaskStatusUpdate update) { - onUploadStatus?.call(update); + if (!_taskStatusController.isClosed) { + _taskStatusController.add(update); + } + _handleTaskStatusUpdate(update); } - Future cancelUpload(String id) { - return FileDownloader().cancelTaskWithId(id); + void dispose() { + _taskStatusController.close(); + _taskProgressController.close(); } - Future upload(File file) async { - final task = await _buildUploadTask( - hash(file.path).toString(), + void enqueueTasks(List tasks) { + _uploadRepository.enqueueBackgroundAll(tasks); + } + + Future> getActiveTasks(String group) { + return _uploadRepository.getActiveTasks(group); + } + + Future getBackupTotalCount() { + return _backupRepository.getTotalCount(); + } + + Future getBackupRemainderCount(String userId) { + return _backupRepository.getRemainderCount(userId); + } + + Future getBackupFinishedCount(String userId) { + return _backupRepository.getBackupCount(userId); + } + + Future manualBackup(List localAssets) async { + await _storageRepository.clearCache(); + List tasks = []; + for (final asset in localAssets) { + final task = await _getUploadTask( + asset, + group: kManualUploadGroup, + priority: 1, // High priority after upload motion photo part + ); + if (task != null) { + tasks.add(task); + } + } + + if (tasks.isNotEmpty) { + enqueueTasks(tasks); + } + } + + /// Find backup candidates + /// Build the upload tasks + /// Enqueue the tasks + Future startBackup(String userId, void Function(EnqueueStatus status) onEnqueueTasks) async { + await _storageRepository.clearCache(); + + shouldAbortQueuingTasks = false; + + final candidates = await _backupRepository.getCandidates(userId); + if (candidates.isEmpty) { + return; + } + + const batchSize = 100; + int count = 0; + for (int i = 0; i < candidates.length; i += batchSize) { + if (shouldAbortQueuingTasks) { + break; + } + + final batch = candidates.skip(i).take(batchSize).toList(); + + List tasks = []; + for (final asset in batch) { + final task = await _getUploadTask(asset); + if (task != null) { + tasks.add(task); + } + } + + if (tasks.isNotEmpty && !shouldAbortQueuingTasks) { + count += tasks.length; + enqueueTasks(tasks); + + onEnqueueTasks(EnqueueStatus(enqueueCount: count, totalCount: candidates.length)); + } + } + } + + /// Cancel all ongoing uploads and reset the upload queue + /// + /// Return the number of left over tasks in the queue + Future cancelBackup() async { + shouldAbortQueuingTasks = true; + + await _storageRepository.clearCache(); + await _uploadRepository.reset(kBackupGroup); + await _uploadRepository.deleteDatabaseRecords(kBackupGroup); + + final activeTasks = await _uploadRepository.getActiveTasks(kBackupGroup); + return activeTasks.length; + } + + Future resumeBackup() { + return _uploadRepository.start(); + } + + void _handleTaskStatusUpdate(TaskStatusUpdate update) { + switch (update.status) { + case TaskStatus.complete: + _handleLivePhoto(update); + break; + + default: + break; + } + } + + Future _handleLivePhoto(TaskStatusUpdate update) async { + try { + if (update.task.metaData.isEmpty || update.task.metaData == '') { + return; + } + + final metadata = UploadTaskMetadata.fromJson(update.task.metaData); + if (!metadata.isLivePhotos) { + return; + } + + if (update.responseBody == null || update.responseBody!.isEmpty) { + return; + } + final response = jsonDecode(update.responseBody!); + + final localAsset = await _localAssetRepository.getById(metadata.localAssetId); + if (localAsset == null) { + return; + } + + final uploadTask = await _getLivePhotoUploadTask(localAsset, response['id'] as String); + + if (uploadTask == null) { + return; + } + + enqueueTasks([uploadTask]); + } catch (error, stackTrace) { + debugPrint("Error handling live photo upload task: $error $stackTrace"); + } + } + + Future _getUploadTask(LocalAsset asset, {String group = kBackupGroup, int? priority}) async { + final entity = await _storageRepository.getAssetEntityForAsset(asset); + if (entity == null) { + return null; + } + + File? file; + + /// iOS LivePhoto has two files: a photo and a video. + /// They are uploaded separately, with video file being upload first, then returned with the assetId + /// The assetId is then used as a metadata for the photo file upload task. + /// + /// We implement two separate upload groups for this, the normal one for the video file + /// and the higher priority group for the photo file because the video file is already uploaded. + /// + /// The cancel operation will only cancel the video group (normal group), the photo group will not + /// be touched, as the video file is already uploaded. + + if (entity.isLivePhoto) { + file = await _storageRepository.getMotionFileForAsset(asset); + } else { + file = await _storageRepository.getFileForAsset(asset.id); + } + + if (file == null) { + return null; + } + + final originalFileName = entity.isLivePhoto ? p.setExtension(asset.name, p.extension(file.path)) : asset.name; + + String metadata = UploadTaskMetadata( + localAssetId: asset.id, + isLivePhotos: entity.isLivePhoto, + livePhotoVideoId: '', + ).toJson(); + + bool requiresWiFi = true; + + if (asset.isVideo && _appSettingsService.getSetting(AppSettingsEnum.useCellularForUploadVideos)) { + requiresWiFi = false; + } else if (!asset.isVideo && _appSettingsService.getSetting(AppSettingsEnum.useCellularForUploadPhotos)) { + requiresWiFi = false; + } + + return buildUploadTask( file, + originalFileName: originalFileName, + deviceAssetId: asset.id, + metadata: metadata, + group: group, + priority: priority, + isFavorite: asset.isFavorite, + requiresWiFi: requiresWiFi, ); - - await _uploadRepository.upload(task); } - Future _buildUploadTask( - String id, + Future _getLivePhotoUploadTask(LocalAsset asset, String livePhotoVideoId) async { + final entity = await _storageRepository.getAssetEntityForAsset(asset); + if (entity == null) { + return null; + } + + final file = await _storageRepository.getFileForAsset(asset.id); + if (file == null) { + return null; + } + + final fields = {'livePhotoVideoId': livePhotoVideoId}; + + return buildUploadTask( + file, + originalFileName: asset.name, + deviceAssetId: asset.id, + fields: fields, + group: kBackupLivePhotoGroup, + priority: 0, // Highest priority to get upload immediately + isFavorite: asset.isFavorite, + ); + } + + Future buildUploadTask( File file, { + required String group, Map? fields, + String? originalFileName, + String? deviceAssetId, + String? metadata, + int? priority, + bool? isFavorite, + bool requiresWiFi = true, }) async { final serverEndpoint = Store.get(StoreKey.serverEndpoint); final url = Uri.parse('$serverEndpoint/assets').toString(); final headers = ApiService.getRequestHeaders(); 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 = { - 'filename': filename, - 'deviceAssetId': id, + 'filename': originalFileName ?? filename, + 'deviceAssetId': deviceAssetId ?? '', 'deviceId': deviceId, 'fileCreatedAt': fileCreatedAt.toUtc().toIso8601String(), 'fileModifiedAt': fileModifiedAt.toUtc().toIso8601String(), - 'isFavorite': 'false', + 'isFavorite': isFavorite?.toString() ?? 'false', 'duration': '0', if (fields != null) ...fields, }; return UploadTask( - taskId: id, + taskId: deviceAssetId, + displayName: originalFileName ?? filename, httpRequestMethod: 'POST', url: url, headers: headers, @@ -87,8 +339,65 @@ class UploadService { baseDirectory: baseDirectory, directory: directory, fileField: 'assetData', - group: uploadGroup, + metaData: metadata ?? '', + group: group, + requiresWiFi: requiresWiFi, + priority: priority ?? 5, updates: Updates.statusAndProgress, + retries: 3, ); } } + +class UploadTaskMetadata { + final String localAssetId; + final bool isLivePhotos; + final String livePhotoVideoId; + + const UploadTaskMetadata({required this.localAssetId, required this.isLivePhotos, required this.livePhotoVideoId}); + + UploadTaskMetadata copyWith({String? localAssetId, bool? isLivePhotos, String? livePhotoVideoId}) { + return UploadTaskMetadata( + localAssetId: localAssetId ?? this.localAssetId, + isLivePhotos: isLivePhotos ?? this.isLivePhotos, + livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, + ); + } + + Map toMap() { + return { + 'localAssetId': localAssetId, + 'isLivePhotos': isLivePhotos, + 'livePhotoVideoId': livePhotoVideoId, + }; + } + + factory UploadTaskMetadata.fromMap(Map map) { + return UploadTaskMetadata( + localAssetId: map['localAssetId'] as String, + isLivePhotos: map['isLivePhotos'] as bool, + livePhotoVideoId: map['livePhotoVideoId'] as String, + ); + } + + String toJson() => json.encode(toMap()); + + factory UploadTaskMetadata.fromJson(String source) => + UploadTaskMetadata.fromMap(json.decode(source) as Map); + + @override + String toString() => + 'UploadTaskMetadata(localAssetId: $localAssetId, isLivePhotos: $isLivePhotos, livePhotoVideoId: $livePhotoVideoId)'; + + @override + bool operator ==(covariant UploadTaskMetadata other) { + if (identical(this, other)) return true; + + return other.localAssetId == localAssetId && + other.isLivePhotos == isLivePhotos && + other.livePhotoVideoId == livePhotoVideoId; + } + + @override + int get hashCode => localAssetId.hashCode ^ isLivePhotos.hashCode ^ livePhotoVideoId.hashCode; +} diff --git a/mobile/lib/services/widget.service.dart b/mobile/lib/services/widget.service.dart index 02ddedbe89..8c5d21b389 100644 --- a/mobile/lib/services/widget.service.dart +++ b/mobile/lib/services/widget.service.dart @@ -1,12 +1,9 @@ -import 'dart:io'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/constants/constants.dart'; import 'package:immich_mobile/repositories/widget.repository.dart'; final widgetServiceProvider = Provider((ref) { - return WidgetService( - ref.watch(widgetRepositoryProvider), - ); + return WidgetService(ref.watch(widgetRepositoryProvider)); }); class WidgetService { @@ -33,10 +30,8 @@ class WidgetService { } Future refreshWidgets() async { - if (Platform.isAndroid) return; - - for (final name in kWidgetNames) { - await _repository.refresh(name); + for (final (iOSName, androidName) in kWidgetNames) { + await _repository.refresh(iOSName, androidName); } } } diff --git a/mobile/lib/theme/color_scheme.dart b/mobile/lib/theme/color_scheme.dart index c01b7cfa5a..f32b358ad9 100644 --- a/mobile/lib/theme/color_scheme.dart +++ b/mobile/lib/theme/color_scheme.dart @@ -6,10 +6,7 @@ final Map _themePresets = { ImmichColorPreset.indigo: ImmichTheme( light: ColorScheme.fromSeed( seedColor: immichBrandColorLight, - ).copyWith( - primary: immichBrandColorLight, - onSurface: const Color.fromARGB(255, 34, 31, 32), - ), + ).copyWith(primary: immichBrandColorLight, onSurface: const Color.fromARGB(255, 34, 31, 32)), dark: ColorScheme.fromSeed( seedColor: immichBrandColorDark, brightness: Brightness.dark, @@ -17,24 +14,15 @@ final Map _themePresets = { ), ImmichColorPreset.deepPurple: ImmichTheme( light: ColorScheme.fromSeed(seedColor: const Color(0xFF6F43C0)), - dark: ColorScheme.fromSeed( - seedColor: const Color(0xFFD3BBFF), - brightness: Brightness.dark, - ), + dark: ColorScheme.fromSeed(seedColor: const Color(0xFFD3BBFF), brightness: Brightness.dark), ), ImmichColorPreset.pink: ImmichTheme( light: ColorScheme.fromSeed(seedColor: const Color(0xFFED79B5)), - dark: ColorScheme.fromSeed( - seedColor: const Color(0xFFED79B5), - brightness: Brightness.dark, - ), + dark: ColorScheme.fromSeed(seedColor: const Color(0xFFED79B5), brightness: Brightness.dark), ), ImmichColorPreset.red: ImmichTheme( light: ColorScheme.fromSeed(seedColor: const Color(0xFFC51C16)), - dark: ColorScheme.fromSeed( - seedColor: const Color(0xFFD3302F), - brightness: Brightness.dark, - ), + dark: ColorScheme.fromSeed(seedColor: const Color(0xFFD3302F), brightness: Brightness.dark), ), ImmichColorPreset.orange: ImmichTheme( light: ColorScheme.fromSeed( @@ -49,37 +37,22 @@ final Map _themePresets = { ), ImmichColorPreset.yellow: ImmichTheme( light: ColorScheme.fromSeed(seedColor: const Color(0xFFFFB400)), - dark: ColorScheme.fromSeed( - seedColor: const Color(0xFFFFB400), - brightness: Brightness.dark, - ), + dark: ColorScheme.fromSeed(seedColor: const Color(0xFFFFB400), brightness: Brightness.dark), ), ImmichColorPreset.lime: ImmichTheme( light: ColorScheme.fromSeed(seedColor: const Color(0xFFCDDC39)), - dark: ColorScheme.fromSeed( - seedColor: const Color(0xFFCDDC39), - brightness: Brightness.dark, - ), + dark: ColorScheme.fromSeed(seedColor: const Color(0xFFCDDC39), brightness: Brightness.dark), ), ImmichColorPreset.green: ImmichTheme( light: ColorScheme.fromSeed(seedColor: const Color(0xFF18C249)), - dark: ColorScheme.fromSeed( - seedColor: const Color(0xFF18C249), - brightness: Brightness.dark, - ), + dark: ColorScheme.fromSeed(seedColor: const Color(0xFF18C249), brightness: Brightness.dark), ), ImmichColorPreset.cyan: ImmichTheme( light: ColorScheme.fromSeed(seedColor: const Color(0xFF00BCD4)), - dark: ColorScheme.fromSeed( - seedColor: const Color(0xFF00BCD4), - brightness: Brightness.dark, - ), + dark: ColorScheme.fromSeed(seedColor: const Color(0xFF00BCD4), brightness: Brightness.dark), ), ImmichColorPreset.slateGray: ImmichTheme( - light: ColorScheme.fromSeed( - seedColor: const Color(0xFF696969), - dynamicSchemeVariant: DynamicSchemeVariant.neutral, - ), + light: ColorScheme.fromSeed(seedColor: const Color(0xFF696969), dynamicSchemeVariant: DynamicSchemeVariant.neutral), dark: ColorScheme.fromSeed( seedColor: const Color(0xff696969), brightness: Brightness.dark, diff --git a/mobile/lib/theme/dynamic_theme.dart b/mobile/lib/theme/dynamic_theme.dart index 8ebf783469..99b949c9ac 100644 --- a/mobile/lib/theme/dynamic_theme.dart +++ b/mobile/lib/theme/dynamic_theme.dart @@ -18,14 +18,8 @@ abstract final class DynamicTheme { // Some palettes do not generate surface container colors accurately, // so we regenerate all colors using the primary color _theme = ImmichTheme( - light: ColorScheme.fromSeed( - seedColor: primaryColor, - brightness: Brightness.light, - ), - dark: ColorScheme.fromSeed( - seedColor: primaryColor, - brightness: Brightness.dark, - ), + light: ColorScheme.fromSeed(seedColor: primaryColor, brightness: Brightness.light), + dark: ColorScheme.fromSeed(seedColor: primaryColor, brightness: Brightness.dark), ); } } catch (error) { diff --git a/mobile/lib/theme/theme_data.dart b/mobile/lib/theme/theme_data.dart index a351b09093..8e3773839c 100644 --- a/mobile/lib/theme/theme_data.dart +++ b/mobile/lib/theme/theme_data.dart @@ -10,10 +10,7 @@ class ImmichTheme { const ImmichTheme({required this.light, required this.dark}); } -ThemeData getThemeData({ - required ColorScheme colorScheme, - required Locale locale, -}) { +ThemeData getThemeData({required ColorScheme colorScheme, required Locale locale}) { final isDark = colorScheme.brightness == Brightness.dark; return ThemeData( @@ -26,9 +23,7 @@ ThemeData getThemeData({ scaffoldBackgroundColor: colorScheme.surface, splashColor: colorScheme.primary.withValues(alpha: 0.1), highlightColor: colorScheme.primary.withValues(alpha: 0.1), - bottomSheetTheme: BottomSheetThemeData( - backgroundColor: colorScheme.surfaceContainer, - ), + bottomSheetTheme: BottomSheetThemeData(backgroundColor: colorScheme.surfaceContainer), fontFamily: _getFontFamilyFromLocale(locale), snackBarTheme: SnackBarThemeData( contentTextStyle: TextStyle( @@ -45,38 +40,19 @@ ThemeData getThemeData({ fontWeight: FontWeight.w600, fontSize: 18, ), - backgroundColor: - isDark ? colorScheme.surfaceContainer : colorScheme.surface, + backgroundColor: isDark ? colorScheme.surfaceContainer : colorScheme.surface, foregroundColor: colorScheme.primary, elevation: 0, scrolledUnderElevation: 0, centerTitle: true, ), textTheme: const TextTheme( - displayLarge: TextStyle( - fontSize: 18, - fontWeight: FontWeight.w600, - ), - displayMedium: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w600, - ), - displaySmall: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - ), - titleSmall: TextStyle( - fontSize: 16.0, - fontWeight: FontWeight.w600, - ), - titleMedium: TextStyle( - fontSize: 18.0, - fontWeight: FontWeight.w600, - ), - titleLarge: TextStyle( - fontSize: 26.0, - fontWeight: FontWeight.w600, - ), + displayLarge: TextStyle(fontSize: 18, fontWeight: FontWeight.w600), + displayMedium: TextStyle(fontSize: 14, fontWeight: FontWeight.w600), + displaySmall: TextStyle(fontSize: 12, fontWeight: FontWeight.w600), + titleSmall: TextStyle(fontSize: 16.0, fontWeight: FontWeight.w600), + titleMedium: TextStyle(fontSize: 18.0, fontWeight: FontWeight.w600), + titleLarge: TextStyle(fontSize: 26.0, fontWeight: FontWeight.w600), ), elevatedButtonTheme: ElevatedButtonThemeData( style: ElevatedButton.styleFrom( @@ -84,82 +60,43 @@ ThemeData getThemeData({ foregroundColor: isDark ? Colors.black87 : Colors.white, ), ), - chipTheme: const ChipThemeData( - side: BorderSide.none, - ), - sliderTheme: const SliderThemeData( - thumbShape: RoundSliderThumbShape(enabledThumbRadius: 7), - trackHeight: 2.0, - ), - bottomNavigationBarTheme: const BottomNavigationBarThemeData( - type: BottomNavigationBarType.fixed, - ), + chipTheme: const ChipThemeData(side: BorderSide.none), + sliderTheme: const SliderThemeData(thumbShape: RoundSliderThumbShape(enabledThumbRadius: 7), trackHeight: 2.0), + bottomNavigationBarTheme: const BottomNavigationBarThemeData(type: BottomNavigationBarType.fixed), popupMenuTheme: const PopupMenuThemeData( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10)), - ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))), ), navigationBarTheme: NavigationBarThemeData( - backgroundColor: - isDark ? colorScheme.surfaceContainer : colorScheme.surface, - labelTextStyle: const WidgetStatePropertyAll( - TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, - ), - ), + backgroundColor: isDark ? colorScheme.surfaceContainer : colorScheme.surface, + labelTextStyle: const WidgetStatePropertyAll(TextStyle(fontSize: 14, fontWeight: FontWeight.w500)), ), inputDecorationTheme: InputDecorationTheme( focusedBorder: OutlineInputBorder( - borderSide: BorderSide( - color: colorScheme.primary, - ), + borderSide: BorderSide(color: colorScheme.primary), borderRadius: const BorderRadius.all(Radius.circular(15)), ), enabledBorder: OutlineInputBorder( - borderSide: BorderSide( - color: colorScheme.outlineVariant, - ), + borderSide: BorderSide(color: colorScheme.outlineVariant), borderRadius: const BorderRadius.all(Radius.circular(15)), ), - labelStyle: TextStyle( - color: colorScheme.primary, - ), - hintStyle: const TextStyle( - fontSize: 14.0, - fontWeight: FontWeight.normal, - ), - ), - textSelectionTheme: TextSelectionThemeData( - cursorColor: colorScheme.primary, + labelStyle: TextStyle(color: colorScheme.primary), + hintStyle: const TextStyle(fontSize: 14.0, fontWeight: FontWeight.normal), ), + textSelectionTheme: TextSelectionThemeData(cursorColor: colorScheme.primary), dropdownMenuTheme: DropdownMenuThemeData( menuStyle: const MenuStyle( shape: WidgetStatePropertyAll( - RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(15)), - ), + RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(15))), ), ), inputDecorationTheme: InputDecorationTheme( - focusedBorder: OutlineInputBorder( - borderSide: BorderSide( - color: colorScheme.primary, - ), - ), + focusedBorder: OutlineInputBorder(borderSide: BorderSide(color: colorScheme.primary)), enabledBorder: OutlineInputBorder( - borderSide: BorderSide( - color: colorScheme.outlineVariant, - ), + borderSide: BorderSide(color: colorScheme.outlineVariant), borderRadius: const BorderRadius.all(Radius.circular(15)), ), - labelStyle: TextStyle( - color: colorScheme.primary, - ), - hintStyle: const TextStyle( - fontSize: 14.0, - fontWeight: FontWeight.normal, - ), + labelStyle: TextStyle(color: colorScheme.primary), + hintStyle: const TextStyle(fontSize: 14.0, fontWeight: FontWeight.normal), ), ), dialogTheme: DialogThemeData(backgroundColor: colorScheme.surfaceContainer), @@ -176,9 +113,7 @@ ThemeData getThemeData({ // This method replaces all surface shades in ImmichTheme to a static ones // as we are creating the colorscheme through seedColor the default surfaces are // tinted with primary color -ImmichTheme decolorizeSurfaces({ - required ImmichTheme theme, -}) { +ImmichTheme decolorizeSurfaces({required ImmichTheme theme}) { return ImmichTheme( light: theme.light.copyWith( surface: const Color(0xFFf9f9f9), diff --git a/mobile/lib/utils/backup_progress.dart b/mobile/lib/utils/backup_progress.dart index 38cdeacdb2..36050f5e20 100644 --- a/mobile/lib/utils/backup_progress.dart +++ b/mobile/lib/utils/backup_progress.dart @@ -50,8 +50,7 @@ String humanReadableBytesProgress(int bytes, int bytesTotal) { } class ThrottleProgressUpdate { - ThrottleProgressUpdate(this._fun, Duration interval) - : _interval = interval.inMicroseconds; + ThrottleProgressUpdate(this._fun, Duration interval) : _interval = interval.inMicroseconds; final void Function(String?, int, int) _fun; final int _interval; int _invokedAt = 0; @@ -61,11 +60,7 @@ class ThrottleProgressUpdate { int progress = 0; int total = 0; - void call({ - final String? title, - final int progress = 0, - final int total = 0, - }) { + void call({final String? title, final int progress = 0, final int total = 0}) { final time = Timeline.now; this.title = title ?? this.title; this.progress = progress; diff --git a/mobile/lib/utils/bootstrap.dart b/mobile/lib/utils/bootstrap.dart index 26f3b49242..8c4ca077c4 100644 --- a/mobile/lib/utils/bootstrap.dart +++ b/mobile/lib/utils/bootstrap.dart @@ -48,10 +48,7 @@ abstract final class Bootstrap { ); } - static Future initDomain( - Isar db, { - bool shouldBufferLogs = true, - }) async { + static Future initDomain(Isar db, {bool shouldBufferLogs = true}) async { await StoreService.init(storeRepository: IsarStoreRepository(db)); await LogService.init( logRepository: IsarLogRepository(db), diff --git a/mobile/lib/utils/cache/custom_image_cache.dart b/mobile/lib/utils/cache/custom_image_cache.dart index 8c70472765..194c55f9df 100644 --- a/mobile/lib/utils/cache/custom_image_cache.dart +++ b/mobile/lib/utils/cache/custom_image_cache.dart @@ -39,7 +39,8 @@ final class CustomImageCache implements ImageCache { /// Gets the cache for the given key /// [_large] is used for [ImmichLocalImageProvider] and [ImmichRemoteImageProvider] /// [_small] is used for [ImmichLocalThumbnailProvider] and [ImmichRemoteThumbnailProvider] - ImageCache _cacheForKey(Object key) => (key is ImmichLocalImageProvider || + ImageCache _cacheForKey(Object key) => + (key is ImmichLocalImageProvider || key is ImmichRemoteImageProvider || key is LocalFullImageProvider || key is RemoteFullImageProvider) @@ -60,25 +61,21 @@ final class CustomImageCache implements ImageCache { int get currentSizeBytes => _small.currentSizeBytes + _large.currentSizeBytes; @override - bool evict(Object key, {bool includeLive = true}) => - _cacheForKey(key).evict(key, includeLive: includeLive); + bool evict(Object key, {bool includeLive = true}) => _cacheForKey(key).evict(key, includeLive: includeLive); @override int get liveImageCount => _small.liveImageCount + _large.liveImageCount; @override - int get pendingImageCount => - _small.pendingImageCount + _large.pendingImageCount; + int get pendingImageCount => _small.pendingImageCount + _large.pendingImageCount; @override ImageStreamCompleter? putIfAbsent( Object key, ImageStreamCompleter Function() loader, { ImageErrorListener? onError, - }) => - _cacheForKey(key).putIfAbsent(key, loader, onError: onError); + }) => _cacheForKey(key).putIfAbsent(key, loader, onError: onError); @override - ImageCacheStatus statusForKey(Object key) => - _cacheForKey(key).statusForKey(key); + ImageCacheStatus statusForKey(Object key) => _cacheForKey(key).statusForKey(key); } diff --git a/mobile/lib/utils/color_filter_generator.dart b/mobile/lib/utils/color_filter_generator.dart index c155823264..92aed4b1a0 100644 --- a/mobile/lib/utils/color_filter_generator.dart +++ b/mobile/lib/utils/color_filter_generator.dart @@ -27,9 +27,7 @@ class BrightnessFilter extends StatelessWidget { @override Widget build(BuildContext context) { return ColorFiltered( - colorFilter: ColorFilter.matrix( - _ColorFilterGenerator.brightnessAdjustMatrix(brightness), - ), + colorFilter: ColorFilter.matrix(_ColorFilterGenerator.brightnessAdjustMatrix(brightness)), child: child, ); } @@ -44,9 +42,7 @@ class SaturationFilter extends StatelessWidget { @override Widget build(BuildContext context) { return ColorFiltered( - colorFilter: ColorFilter.matrix( - _ColorFilterGenerator.saturationAdjustMatrix(saturation), - ), + colorFilter: ColorFilter.matrix(_ColorFilterGenerator.saturationAdjustMatrix(saturation)), child: child, ); } @@ -82,8 +78,7 @@ class _ColorFilterGenerator { ]; } - double x = - ((1 + ((value > 0) ? ((3 * value) / 100) : (value / 100)))).toDouble(); + double x = ((1 + ((value > 0) ? ((3 * value) / 100) : (value / 100)))).toDouble(); double lumR = 0.3086; double lumG = 0.6094; double lumB = 0.082; diff --git a/mobile/lib/utils/database.utils.dart b/mobile/lib/utils/database.utils.dart new file mode 100644 index 0000000000..446b92db19 --- /dev/null +++ b/mobile/lib/utils/database.utils.dart @@ -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, + ); + } +} diff --git a/mobile/lib/utils/datetime_comparison.dart b/mobile/lib/utils/datetime_comparison.dart index 8c53ea45ba..f8ddcfea11 100644 --- a/mobile/lib/utils/datetime_comparison.dart +++ b/mobile/lib/utils/datetime_comparison.dart @@ -1,3 +1,2 @@ bool isAtSameMomentAs(DateTime? a, DateTime? b) => - (a == null && b == null) || - ((a != null && b != null) && a.isAtSameMomentAs(b)); + (a == null && b == null) || ((a != null && b != null) && a.isAtSameMomentAs(b)); diff --git a/mobile/lib/utils/debounce.dart b/mobile/lib/utils/debounce.dart index 78870151a6..5451b569ca 100644 --- a/mobile/lib/utils/debounce.dart +++ b/mobile/lib/utils/debounce.dart @@ -19,8 +19,7 @@ class Debouncer { if (maxWaitTime != null && // _actionFuture == null && // TODO: should this check be here? - (_lastActionTime == null || - DateTime.now().difference(_lastActionTime!) > maxWaitTime!)) { + (_lastActionTime == null || DateTime.now().difference(_lastActionTime!) > maxWaitTime!)) { _callAndRest(); return; } @@ -60,8 +59,7 @@ class Debouncer { _actionFuture = null; } - bool get isActive => - _actionFuture != null || (_timer != null && _timer!.isActive); + bool get isActive => _actionFuture != null || (_timer != null && _timer!.isActive); } /// Creates a [Debouncer] that will be disposed automatically. If no [interval] is provided, a @@ -70,21 +68,10 @@ Debouncer useDebouncer({ Duration interval = const Duration(milliseconds: 300), Duration? maxWaitTime, List? keys, -}) => - use( - _DebouncerHook( - interval: interval, - maxWaitTime: maxWaitTime, - keys: keys, - ), - ); +}) => use(_DebouncerHook(interval: interval, maxWaitTime: maxWaitTime, keys: keys)); class _DebouncerHook extends Hook { - const _DebouncerHook({ - required this.interval, - this.maxWaitTime, - super.keys, - }); + const _DebouncerHook({required this.interval, this.maxWaitTime, super.keys}); final Duration interval; final Duration? maxWaitTime; @@ -94,10 +81,7 @@ class _DebouncerHook extends Hook { } class _DebouncerHookState extends HookState { - late final debouncer = Debouncer( - interval: hook.interval, - maxWaitTime: hook.maxWaitTime, - ); + late final debouncer = Debouncer(interval: hook.interval, maxWaitTime: hook.maxWaitTime); @override Debouncer build(_) => debouncer; diff --git a/mobile/lib/utils/download.dart b/mobile/lib/utils/download.dart deleted file mode 100644 index c701f353a2..0000000000 --- a/mobile/lib/utils/download.dart +++ /dev/null @@ -1,3 +0,0 @@ -const downloadGroupImage = 'group_image'; -const downloadGroupVideo = 'group_video'; -const downloadGroupLivePhoto = 'group_livephoto'; diff --git a/mobile/lib/utils/draggable_scroll_controller.dart b/mobile/lib/utils/draggable_scroll_controller.dart index 1d22905d1f..bab5214446 100644 --- a/mobile/lib/utils/draggable_scroll_controller.dart +++ b/mobile/lib/utils/draggable_scroll_controller.dart @@ -5,29 +5,20 @@ import 'package:flutter_hooks/flutter_hooks.dart'; /// /// See also: /// - [DraggableScrollableController] -DraggableScrollableController useDraggableScrollController({ - List? keys, -}) { - return use( - _DraggableScrollControllerHook( - keys: keys, - ), - ); +DraggableScrollableController useDraggableScrollController({List? keys}) { + return use(_DraggableScrollControllerHook(keys: keys)); } -class _DraggableScrollControllerHook - extends Hook { - const _DraggableScrollControllerHook({ - super.keys, - }); +class _DraggableScrollControllerHook extends Hook { + const _DraggableScrollControllerHook({super.keys}); @override - HookState> - createState() => _DraggableScrollControllerHookState(); + HookState> createState() => + _DraggableScrollControllerHookState(); } -class _DraggableScrollControllerHookState extends HookState< - DraggableScrollableController, _DraggableScrollControllerHook> { +class _DraggableScrollControllerHookState + extends HookState { late final controller = DraggableScrollableController(); @override diff --git a/mobile/lib/utils/hooks/app_settings_update_hook.dart b/mobile/lib/utils/hooks/app_settings_update_hook.dart index a4968feeae..954e44229a 100644 --- a/mobile/lib/utils/hooks/app_settings_update_hook.dart +++ b/mobile/lib/utils/hooks/app_settings_update_hook.dart @@ -3,16 +3,11 @@ import 'package:flutter_hooks/flutter_hooks.dart' hide Store; import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/entities/store.entity.dart'; -ValueNotifier useAppSettingsState( - AppSettingsEnum key, -) { +ValueNotifier useAppSettingsState(AppSettingsEnum key) { final notifier = useState(Store.get(key.storeKey, key.defaultValue)); // Listen to changes to the notifier and update app settings - useValueChanged( - notifier.value, - (_, __) => Store.put(key.storeKey, notifier.value), - ); + useValueChanged(notifier.value, (_, __) => Store.put(key.storeKey, notifier.value)); return notifier; } diff --git a/mobile/lib/utils/hooks/blurhash_hook.dart b/mobile/lib/utils/hooks/blurhash_hook.dart index 62208c4cf5..ac5fd31724 100644 --- a/mobile/lib/utils/hooks/blurhash_hook.dart +++ b/mobile/lib/utils/hooks/blurhash_hook.dart @@ -10,9 +10,7 @@ ObjectRef useBlurHashRef(Asset? asset) { return useRef(null); } - final rbga = thumbhash.thumbHashToRGBA( - base64Decode(asset!.thumbhash!), - ); + final rbga = thumbhash.thumbHashToRGBA(base64Decode(asset!.thumbhash!)); return useRef(thumbhash.rgbaToBmp(rbga)); } @@ -22,9 +20,7 @@ ObjectRef useDriftBlurHashRef(RemoteAsset? asset) { return useRef(null); } - final rbga = thumbhash.thumbHashToRGBA( - base64Decode(asset!.thumbHash!), - ); + final rbga = thumbhash.thumbHashToRGBA(base64Decode(asset!.thumbHash!)); return useRef(thumbhash.rgbaToBmp(rbga)); } diff --git a/mobile/lib/utils/hooks/crop_controller_hook.dart b/mobile/lib/utils/hooks/crop_controller_hook.dart index 04bc978754..663bca3dbf 100644 --- a/mobile/lib/utils/hooks/crop_controller_hook.dart +++ b/mobile/lib/utils/hooks/crop_controller_hook.dart @@ -4,9 +4,5 @@ import 'dart:ui'; // Import the dart:ui library for Rect /// A hook that provides a [CropController] instance. CropController useCropController() { - return useMemoized( - () => CropController( - defaultCrop: const Rect.fromLTRB(0, 0, 1, 1), - ), - ); + return useMemoized(() => CropController(defaultCrop: const Rect.fromLTRB(0, 0, 1, 1))); } diff --git a/mobile/lib/utils/hooks/interval_hook.dart b/mobile/lib/utils/hooks/interval_hook.dart index 0c346065f7..907fbad102 100644 --- a/mobile/lib/utils/hooks/interval_hook.dart +++ b/mobile/lib/utils/hooks/interval_hook.dart @@ -8,11 +8,8 @@ void useInterval(Duration delay, VoidCallback callback) { final savedCallback = useRef(callback); savedCallback.value = callback; - useEffect( - () { - final timer = Timer.periodic(delay, (_) => savedCallback.value()); - return timer.cancel; - }, - [delay], - ); + useEffect(() { + final timer = Timer.periodic(delay, (_) => savedCallback.value()); + return timer.cancel; + }, [delay]); } diff --git a/mobile/lib/utils/hooks/timer_hook.dart b/mobile/lib/utils/hooks/timer_hook.dart index a78fed42c3..36b78d8631 100644 --- a/mobile/lib/utils/hooks/timer_hook.dart +++ b/mobile/lib/utils/hooks/timer_hook.dart @@ -2,29 +2,17 @@ import 'package:async/async.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; -RestartableTimer useTimer( - Duration duration, - void Function() callback, -) { - return use( - _TimerHook( - duration: duration, - callback: callback, - ), - ); +RestartableTimer useTimer(Duration duration, void Function() callback) { + return use(_TimerHook(duration: duration, callback: callback)); } class _TimerHook extends Hook { final Duration duration; final void Function() callback; - const _TimerHook({ - required this.duration, - required this.callback, - }); + const _TimerHook({required this.duration, required this.callback}); @override - HookState> createState() => - _TimerHookState(); + HookState> createState() => _TimerHookState(); } class _TimerHookState extends HookState { diff --git a/mobile/lib/utils/http_ssl_cert_override.dart b/mobile/lib/utils/http_ssl_cert_override.dart index f64757cf9d..a4c97a532f 100644 --- a/mobile/lib/utils/http_ssl_cert_override.dart +++ b/mobile/lib/utils/http_ssl_cert_override.dart @@ -10,11 +10,7 @@ class HttpSSLCertOverride extends HttpOverrides { final SSLClientCertStoreVal? _clientCert; late final SecurityContext? _ctxWithCert; - HttpSSLCertOverride( - this._allowSelfSignedSSLCert, - this._serverHost, - this._clientCert, - ) { + HttpSSLCertOverride(this._allowSelfSignedSSLCert, this._serverHost, this._clientCert) { if (_clientCert != null) { _ctxWithCert = SecurityContext(withTrustedRoots: true); if (_ctxWithCert != null) { diff --git a/mobile/lib/utils/http_ssl_options.dart b/mobile/lib/utils/http_ssl_options.dart index 04c01d36d9..c4e2ad69f7 100644 --- a/mobile/lib/utils/http_ssl_options.dart +++ b/mobile/lib/utils/http_ssl_options.dart @@ -10,18 +10,17 @@ import 'package:logging/logging.dart'; class HttpSSLOptions { static const MethodChannel _channel = MethodChannel('immich/httpSSLOptions'); - static void apply() { + static void apply({bool applyNative = true}) { AppSettingsEnum setting = AppSettingsEnum.allowSelfSignedSSLCert; - bool allowSelfSignedSSLCert = - Store.get(setting.storeKey as StoreKey, setting.defaultValue); - _apply(allowSelfSignedSSLCert); + bool allowSelfSignedSSLCert = Store.get(setting.storeKey as StoreKey, setting.defaultValue); + _apply(allowSelfSignedSSLCert, applyNative: applyNative); } static void applyFromSettings(bool newValue) { _apply(newValue); } - static void _apply(bool allowSelfSignedSSLCert) { + static void _apply(bool allowSelfSignedSSLCert, {bool applyNative = true}) { String? serverHost; if (allowSelfSignedSSLCert && Store.tryGet(StoreKey.currentUser) != null) { serverHost = Uri.parse(Store.tryGet(StoreKey.serverEndpoint) ?? "").host; @@ -29,19 +28,15 @@ class HttpSSLOptions { SSLClientCertStoreVal? clientCert = SSLClientCertStoreVal.load(); - HttpOverrides.global = - HttpSSLCertOverride(allowSelfSignedSSLCert, serverHost, clientCert); + HttpOverrides.global = HttpSSLCertOverride(allowSelfSignedSSLCert, serverHost, clientCert); - if (Platform.isAndroid) { - _channel.invokeMethod("apply", [ - allowSelfSignedSSLCert, - serverHost, - clientCert?.data, - clientCert?.password, - ]).onError((e, _) { - final log = Logger("HttpSSLOptions"); - log.severe('Failed to set SSL options', e.message); - }); + if (applyNative && Platform.isAndroid) { + _channel + .invokeMethod("apply", [allowSelfSignedSSLCert, serverHost, clientCert?.data, clientCert?.password]) + .onError((e, _) { + final log = Logger("HttpSSLOptions"); + log.severe('Failed to set SSL options', e.message); + }); } } } diff --git a/mobile/lib/utils/image_url_builder.dart b/mobile/lib/utils/image_url_builder.dart index bde50f3a90..21722cb901 100644 --- a/mobile/lib/utils/image_url_builder.dart +++ b/mobile/lib/utils/image_url_builder.dart @@ -5,24 +5,15 @@ import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:openapi/api.dart'; -String getThumbnailUrl( - final Asset asset, { - AssetMediaSize type = AssetMediaSize.thumbnail, -}) { +String getThumbnailUrl(final Asset asset, {AssetMediaSize type = AssetMediaSize.thumbnail}) { return getThumbnailUrlForRemoteId(asset.remoteId!, type: type); } -String getThumbnailCacheKey( - final Asset asset, { - AssetMediaSize type = AssetMediaSize.thumbnail, -}) { +String getThumbnailCacheKey(final Asset asset, {AssetMediaSize type = AssetMediaSize.thumbnail}) { return getThumbnailCacheKeyForRemoteId(asset.remoteId!, type: type); } -String getThumbnailCacheKeyForRemoteId( - final String id, { - AssetMediaSize type = AssetMediaSize.thumbnail, -}) { +String getThumbnailCacheKeyForRemoteId(final String id, {AssetMediaSize type = AssetMediaSize.thumbnail}) { if (type == AssetMediaSize.thumbnail) { return 'thumbnail-image-$id'; } else { @@ -30,30 +21,18 @@ String getThumbnailCacheKeyForRemoteId( } } -String getAlbumThumbnailUrl( - final Album album, { - AssetMediaSize type = AssetMediaSize.thumbnail, -}) { +String getAlbumThumbnailUrl(final Album album, {AssetMediaSize type = AssetMediaSize.thumbnail}) { if (album.thumbnail.value?.remoteId == null) { return ''; } - return getThumbnailUrlForRemoteId( - album.thumbnail.value!.remoteId!, - type: type, - ); + return getThumbnailUrlForRemoteId(album.thumbnail.value!.remoteId!, type: type); } -String getAlbumThumbNailCacheKey( - final Album album, { - AssetMediaSize type = AssetMediaSize.thumbnail, -}) { +String getAlbumThumbNailCacheKey(final Album album, {AssetMediaSize type = AssetMediaSize.thumbnail}) { if (album.thumbnail.value?.remoteId == null) { return ''; } - return getThumbnailCacheKeyForRemoteId( - album.thumbnail.value!.remoteId!, - type: type, - ); + return getThumbnailCacheKeyForRemoteId(album.thumbnail.value!.remoteId!, type: type); } String getOriginalUrlForRemoteId(final String id) { @@ -66,10 +45,7 @@ String getImageCacheKey(final Asset asset) { return '${isFromDto ? asset.remoteId : asset.id}_fullStage'; } -String getThumbnailUrlForRemoteId( - final String id, { - AssetMediaSize type = AssetMediaSize.thumbnail, -}) { +String getThumbnailUrlForRemoteId(final String id, {AssetMediaSize type = AssetMediaSize.thumbnail}) { return '${Store.get(StoreKey.serverEndpoint)}/assets/$id/thumbnail?size=${type.value}'; } diff --git a/mobile/lib/utils/immich_loading_overlay.dart b/mobile/lib/utils/immich_loading_overlay.dart index fcc47b1542..be49c3bae9 100644 --- a/mobile/lib/utils/immich_loading_overlay.dart +++ b/mobile/lib/utils/immich_loading_overlay.dart @@ -7,13 +7,9 @@ final _loadingEntry = OverlayEntry( builder: (context) => SizedBox.square( dimension: double.infinity, child: DecoratedBox( - decoration: - BoxDecoration(color: context.colorScheme.surface.withAlpha(200)), + decoration: BoxDecoration(color: context.colorScheme.surface.withAlpha(200)), child: const Center( - child: DelayedLoadingIndicator( - delay: Duration(seconds: 1), - fadeInDuration: Duration(milliseconds: 400), - ), + child: DelayedLoadingIndicator(delay: Duration(seconds: 1), fadeInDuration: Duration(milliseconds: 400)), ), ), ), @@ -30,8 +26,7 @@ class _LoadingOverlay extends Hook> { _LoadingOverlayState createState() => _LoadingOverlayState(); } -class _LoadingOverlayState - extends HookState, _LoadingOverlay> { +class _LoadingOverlayState extends HookState, _LoadingOverlay> { late final _isLoading = ValueNotifier(false)..addListener(_listener); OverlayEntry? _loadingOverlay; diff --git a/mobile/lib/utils/isolate.dart b/mobile/lib/utils/isolate.dart index 2263bf3d76..a57c3ebbd5 100644 --- a/mobile/lib/utils/isolate.dart +++ b/mobile/lib/utils/isolate.dart @@ -9,6 +9,7 @@ import 'package:immich_mobile/providers/db.provider.dart'; import 'package:immich_mobile/providers/infrastructure/cancel.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; import 'package:immich_mobile/utils/bootstrap.dart'; +import 'package:immich_mobile/utils/http_ssl_options.dart'; import 'package:logging/logging.dart'; import 'package:worker_manager/worker_manager.dart'; @@ -16,8 +17,7 @@ class InvalidIsolateUsageException implements Exception { const InvalidIsolateUsageException(); @override - String toString() => - "IsolateHelper should only be used from the root isolate"; + String toString() => "IsolateHelper should only be used from the root isolate"; } // !! Should be used only from the root isolate @@ -48,22 +48,27 @@ Cancelable runInIsolateGentle({ Logger log = Logger("IsolateLogger"); try { + HttpSSLOptions.apply(applyNative: false); return await computation(ref); } on CanceledError { - log.warning( - "Computation cancelled ${debugLabel == null ? '' : ' for $debugLabel'}", - ); + log.warning("Computation cancelled ${debugLabel == null ? '' : ' for $debugLabel'}"); } catch (error, stack) { - log.severe( - "Error in runInIsolateGentle ${debugLabel == null ? '' : ' for $debugLabel'}", - error, - stack, - ); + log.severe("Error in runInIsolateGentle ${debugLabel == null ? '' : ' for $debugLabel'}", error, stack); } finally { try { await LogService.I.flushBuffer(); await ref.read(driftProvider).close(); - await ref.read(isarProvider).close(); + + // Close Isar safely + try { + final isar = ref.read(isarProvider); + if (isar.isOpen) { + await isar.close(); + } + } catch (e) { + debugPrint("Error closing Isar: $e"); + } + ref.dispose(); } catch (error) { debugPrint("Error closing resources in isolate: $error"); diff --git a/mobile/lib/utils/map_utils.dart b/mobile/lib/utils/map_utils.dart index 39443fb225..80e20b7c6c 100644 --- a/mobile/lib/utils/map_utils.dart +++ b/mobile/lib/utils/map_utils.dart @@ -48,21 +48,18 @@ class MapUtils { ); static Map _addFeature(MapMarker marker) => { - 'type': 'Feature', - 'id': marker.assetRemoteId, - 'geometry': { - 'type': 'Point', - 'coordinates': [marker.latLng.longitude, marker.latLng.latitude], - }, - }; + 'type': 'Feature', + 'id': marker.assetRemoteId, + 'geometry': { + 'type': 'Point', + 'coordinates': [marker.latLng.longitude, marker.latLng.latitude], + }, + }; - static Map generateGeoJsonForMarkers( - List markers, - ) => - { - 'type': 'FeatureCollection', - 'features': markers.map(_addFeature).toList(), - }; + static Map generateGeoJsonForMarkers(List markers) => { + 'type': 'FeatureCollection', + 'features': markers.map(_addFeature).toList(), + }; static Future<(Position?, LocationPermission?)> checkPermAndGetLocation({ required BuildContext context, @@ -71,10 +68,7 @@ class MapUtils { try { bool serviceEnabled = await Geolocator.isLocationServiceEnabled(); if (!serviceEnabled && !silent) { - showDialog( - context: context, - builder: (context) => _LocationServiceDisabledDialog(), - ); + showDialog(context: context, builder: (context) => _LocationServiceDisabledDialog()); return (null, LocationPermission.deniedForever); } @@ -91,12 +85,9 @@ class MapUtils { } } - if (permission == LocationPermission.denied || - permission == LocationPermission.deniedForever) { + if (permission == LocationPermission.denied || permission == LocationPermission.deniedForever) { // Open app settings only if you did not request for permission before - if (permission == LocationPermission.deniedForever && - !shouldRequestPermission && - !silent) { + if (permission == LocationPermission.deniedForever && !shouldRequestPermission && !silent) { await Geolocator.openAppSettings(); } return (null, LocationPermission.deniedForever); @@ -119,24 +110,24 @@ class MapUtils { class _LocationServiceDisabledDialog extends ConfirmDialog { _LocationServiceDisabledDialog() - : super( - title: 'map_location_service_disabled_title'.tr(), - content: 'map_location_service_disabled_content'.tr(), - cancel: 'cancel'.tr(), - ok: 'yes'.tr(), - onOk: () async { - await Geolocator.openLocationSettings(); - }, - ); + : super( + title: 'map_location_service_disabled_title'.tr(), + content: 'map_location_service_disabled_content'.tr(), + cancel: 'cancel'.tr(), + ok: 'yes'.tr(), + onOk: () async { + await Geolocator.openLocationSettings(); + }, + ); } class _LocationPermissionDisabledDialog extends ConfirmDialog { _LocationPermissionDisabledDialog() - : super( - title: 'map_no_location_permission_title'.tr(), - content: 'map_no_location_permission_content'.tr(), - cancel: 'cancel'.tr(), - ok: 'yes'.tr(), - onOk: () {}, - ); + : super( + title: 'map_no_location_permission_title'.tr(), + content: 'map_no_location_permission_content'.tr(), + cancel: 'cancel'.tr(), + ok: 'yes'.tr(), + onOk: () {}, + ); } diff --git a/mobile/lib/utils/migration.dart b/mobile/lib/utils/migration.dart index d09f2b9978..609b3cfca8 100644 --- a/mobile/lib/utils/migration.dart +++ b/mobile/lib/utils/migration.dart @@ -2,19 +2,22 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'package:collection/collection.dart'; import 'package:drift/drift.dart'; import 'package:flutter/foundation.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/utils/background_sync.dart'; import 'package:immich_mobile/entities/album.entity.dart'; import 'package:immich_mobile/entities/android_device_asset.entity.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; +import 'package:immich_mobile/entities/backup_album.entity.dart' as isar_backup_album; import 'package:immich_mobile/entities/etag.entity.dart'; import 'package:immich_mobile/entities/ios_device_asset.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/infrastructure/entities/device_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/store.entity.dart'; import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; @@ -27,7 +30,7 @@ import 'package:logging/logging.dart'; // ignore: import_rule_photo_manager import 'package:photo_manager/photo_manager.dart'; -const int targetVersion = 14; +const int targetVersion = 13; Future migrateDatabaseIfNeeded(Isar db) async { final int version = Store.get(StoreKey.version, targetVersion); @@ -40,8 +43,7 @@ Future migrateDatabaseIfNeeded(Isar db) async { if (id != null) { await db.writeTxn(() async { final user = await db.users.get(id); - await db.storeValues - .put(StoreValue(StoreKey.currentUser.id, strValue: user?.id)); + await db.storeValues.put(StoreValue(StoreKey.currentUser.id, strValue: user?.id)); }); } } @@ -56,18 +58,6 @@ Future migrateDatabaseIfNeeded(Isar db) async { await Store.put(StoreKey.photoManagerCustomFilter, true); } - if (version < 14) { - if (!Store.isBetaTimelineEnabled) { - // Try again when beta timeline is enabled and the app is restarted - return; - } - final backgroundSync = BackgroundSyncManager(); - await backgroundSync.syncLocal(); - final drift = Drift(); - await migrateDeviceAssetToSqlite(db, drift); - await drift.close(); - } - if (targetVersion >= 12) { await Store.put(StoreKey.version, targetVersion); return; @@ -95,46 +85,33 @@ Future _migrateTo(Isar db, int version) async { Future _migrateDeviceAsset(Isar db) async { final ids = Platform.isAndroid ? (await db.androidDeviceAssets.where().findAll()) - .map((a) => _DeviceAsset(assetId: a.id.toString(), hash: a.hash)) - .toList() - : (await db.iOSDeviceAssets.where().findAll()) - .map((i) => _DeviceAsset(assetId: i.id, hash: i.hash)) - .toList(); + .map((a) => _DeviceAsset(assetId: a.id.toString(), hash: a.hash)) + .toList() + : (await db.iOSDeviceAssets.where().findAll()).map((i) => _DeviceAsset(assetId: i.id, hash: i.hash)).toList(); final PermissionState ps = await PhotoManager.requestPermissionExtend(); if (!ps.hasAccess) { if (kDebugMode) { - debugPrint( - "[MIGRATION] Photo library permission not granted. Skipping device asset migration.", - ); + debugPrint("[MIGRATION] Photo library permission not granted. Skipping device asset migration."); } return; } List<_DeviceAsset> localAssets = []; - final List paths = - await PhotoManager.getAssetPathList(onlyAll: true); + final List paths = await PhotoManager.getAssetPathList(onlyAll: true); if (paths.isEmpty) { - localAssets = (await db.assets - .where() - .anyOf(ids, (query, id) => query.localIdEqualTo(id.assetId)) - .findAll()) - .map( - (a) => _DeviceAsset(assetId: a.localId!, dateTime: a.fileModifiedAt), - ) + localAssets = (await db.assets.where().anyOf(ids, (query, id) => query.localIdEqualTo(id.assetId)).findAll()) + .map((a) => _DeviceAsset(assetId: a.localId!, dateTime: a.fileModifiedAt)) .toList(); } else { final AssetPathEntity albumWithAll = paths.first; final int assetCount = await albumWithAll.assetCountAsync; - final List allDeviceAssets = - await albumWithAll.getAssetListRange(start: 0, end: assetCount); + final List allDeviceAssets = await albumWithAll.getAssetListRange(start: 0, end: assetCount); - localAssets = allDeviceAssets - .map((a) => _DeviceAsset(assetId: a.id, dateTime: a.modifiedDateTime)) - .toList(); + localAssets = allDeviceAssets.map((a) => _DeviceAsset(assetId: a.id, dateTime: a.modifiedDateTime)).toList(); } debugPrint("[MIGRATION] Device Asset Ids length - ${ids.length}"); @@ -148,34 +125,24 @@ Future _migrateDeviceAsset(Isar db) async { compare: (a, b) => a.assetId.compareTo(b.assetId), both: (deviceAsset, asset) { toAdd.add( - DeviceAssetEntity( - assetId: deviceAsset.assetId, - hash: deviceAsset.hash!, - modifiedTime: asset.dateTime!, - ), + DeviceAssetEntity(assetId: deviceAsset.assetId, hash: deviceAsset.hash!, modifiedTime: asset.dateTime!), ); return false; }, onlyFirst: (deviceAsset) { if (kDebugMode) { - debugPrint( - '[MIGRATION] Local asset not found in DeviceAsset: ${deviceAsset.assetId}', - ); + debugPrint('[MIGRATION] Local asset not found in DeviceAsset: ${deviceAsset.assetId}'); } }, onlySecond: (asset) { if (kDebugMode) { - debugPrint( - '[MIGRATION] Local asset not found in DeviceAsset: ${asset.assetId}', - ); + debugPrint('[MIGRATION] Local asset not found in DeviceAsset: ${asset.assetId}'); } }, ); if (kDebugMode) { - debugPrint( - "[MIGRATION] Total number of device assets migrated - ${toAdd.length}", - ); + debugPrint("[MIGRATION] Total number of device assets migrated - ${toAdd.length}"); } await db.writeTxn(() async { @@ -190,17 +157,61 @@ Future migrateDeviceAssetToSqlite(Isar db, Drift drift) async { for (final deviceAsset in isarDeviceAssets) { batch.update( drift.localAssetEntity, - LocalAssetEntityCompanion( - checksum: Value(base64.encode(deviceAsset.hash)), - ), + LocalAssetEntityCompanion(checksum: Value(base64.encode(deviceAsset.hash))), where: (t) => t.id.equals(deviceAsset.assetId), ); } }); } catch (error) { - debugPrint( - "[MIGRATION] Error while migrating device assets to SQLite: $error", - ); + debugPrint("[MIGRATION] Error while migrating device assets to SQLite: $error"); + } +} + +Future migrateBackupAlbumsToSqlite(Isar db, Drift drift) async { + try { + final isarBackupAlbums = await db.backupAlbums.where().findAll(); + // Recents is a virtual album on Android, and we don't have it with the new sync + // If recents is selected previously, select all albums during migration except the excluded ones + if (Platform.isAndroid) { + final recentAlbum = isarBackupAlbums.firstWhereOrNull((album) => album.id == 'isAll'); + if (recentAlbum != null) { + await drift.localAlbumEntity.update().write( + const LocalAlbumEntityCompanion(backupSelection: Value(BackupSelection.selected)), + ); + final excluded = isarBackupAlbums + .where((album) => album.selection == isar_backup_album.BackupSelection.exclude) + .map((album) => album.id) + .toList(); + await drift.batch((batch) async { + for (final id in excluded) { + batch.update( + drift.localAlbumEntity, + const LocalAlbumEntityCompanion(backupSelection: Value(BackupSelection.excluded)), + where: (t) => t.id.equals(id), + ); + } + }); + return; + } + } + + await drift.batch((batch) { + for (final album in isarBackupAlbums) { + batch.update( + drift.localAlbumEntity, + LocalAlbumEntityCompanion( + backupSelection: Value(switch (album.selection) { + isar_backup_album.BackupSelection.none => BackupSelection.none, + isar_backup_album.BackupSelection.select => BackupSelection.selected, + isar_backup_album.BackupSelection.exclude => BackupSelection.excluded, + }), + ), + where: (t) => t.id.equals(album.id), + ); + } + }); + } catch (error) { + debugPrint("[MIGRATION] Error while migrating backup albums to SQLite: $error"); } } @@ -217,12 +228,10 @@ Future runNewSync(WidgetRef ref, {bool full = false}) async { final backgroundManager = ref.read(backgroundSyncProvider); Future.wait([ - backgroundManager.syncLocal(full: full).then( - (_) { - Logger("runNewSync").fine("Hashing assets after syncLocal"); - backgroundManager.hashAssets(); - }, - ), + backgroundManager.syncLocal(full: full).then((_) { + Logger("runNewSync").fine("Hashing assets after syncLocal"); + backgroundManager.hashAssets(); + }), backgroundManager.syncRemote(), ]); } diff --git a/mobile/lib/utils/openapi_patching.dart b/mobile/lib/utils/openapi_patching.dart index 8e14d232f8..3e45390198 100644 --- a/mobile/lib/utils/openapi_patching.dart +++ b/mobile/lib/utils/openapi_patching.dart @@ -17,16 +17,8 @@ dynamic upgradeDto(dynamic value, String targetType) { break; case 'ServerConfigDto': if (value is Map) { - addDefault( - value, - 'mapLightStyleUrl', - 'https://tiles.immich.cloud/v1/style/light.json', - ); - addDefault( - value, - 'mapDarkStyleUrl', - 'https://tiles.immich.cloud/v1/style/dark.json', - ); + addDefault(value, 'mapLightStyleUrl', 'https://tiles.immich.cloud/v1/style/light.json'); + addDefault(value, 'mapDarkStyleUrl', 'https://tiles.immich.cloud/v1/style/dark.json'); } case 'UserResponseDto': if (value is Map) { @@ -48,6 +40,11 @@ dynamic upgradeDto(dynamic value, String targetType) { addDefault(value, 'isOnboarded', false); } break; + case 'SyncUserV1': + if (value is Map) { + addDefault(value, 'profileChangedAt', DateTime.now().toIso8601String()); + addDefault(value, 'hasProfileImage', false); + } } } diff --git a/mobile/lib/utils/people.utils.dart b/mobile/lib/utils/people.utils.dart new file mode 100644 index 0000000000..1b0a81a8cc --- /dev/null +++ b/mobile/lib/utils/people.utils.dart @@ -0,0 +1,54 @@ +import 'package:flutter/material.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/people/person_edit_birthday_modal.widget.dart'; +import 'package:immich_mobile/presentation/widgets/people/person_edit_name_modal.widget.dart'; + +String formatAge(DateTime birthDate, DateTime referenceDate) { + int ageInYears = _calculateAge(birthDate, referenceDate); + int ageInMonths = _calculateAgeInMonths(birthDate, referenceDate); + + if (ageInMonths <= 11) { + return "exif_bottom_sheet_person_age_months".t(args: {'months': ageInMonths.toString()}); + } else if (ageInMonths > 12 && ageInMonths <= 23) { + return "exif_bottom_sheet_person_age_year_months".t(args: {'months': (ageInMonths - 12).toString()}); + } else { + return "exif_bottom_sheet_person_age_years".t(args: {'years': ageInYears.toString()}); + } +} + +int _calculateAge(DateTime birthDate, DateTime referenceDate) { + int age = referenceDate.year - birthDate.year; + if (referenceDate.month < birthDate.month || + (referenceDate.month == birthDate.month && referenceDate.day < birthDate.day)) { + age--; + } + return age; +} + +int _calculateAgeInMonths(DateTime birthDate, DateTime referenceDate) { + return (referenceDate.year - birthDate.year) * 12 + + referenceDate.month - + birthDate.month - + (referenceDate.day < birthDate.day ? 1 : 0); +} + +Future showNameEditModal(BuildContext context, DriftPerson person) { + return showDialog( + context: context, + useRootNavigator: false, + builder: (BuildContext context) { + return DriftPersonNameEditForm(person: person); + }, + ); +} + +Future showBirthdayEditModal(BuildContext context, DriftPerson person) { + return showDialog( + context: context, + useRootNavigator: false, + builder: (BuildContext context) { + return DriftPersonBirthdayEditForm(person: person); + }, + ); +} diff --git a/mobile/lib/utils/remote_album.utils.dart b/mobile/lib/utils/remote_album.utils.dart index 04184ee367..b63df899ce 100644 --- a/mobile/lib/utils/remote_album.utils.dart +++ b/mobile/lib/utils/remote_album.utils.dart @@ -1,56 +1,37 @@ import 'package:collection/collection.dart'; import 'package:immich_mobile/domain/models/album/album.model.dart'; -typedef AlbumSortFn = List Function( - List albums, - bool isReverse, -); +typedef AlbumSortFn = List Function(List albums, bool isReverse); class _RemoteAlbumSortHandlers { const _RemoteAlbumSortHandlers._(); static const AlbumSortFn created = _sortByCreated; - static List _sortByCreated( - List albums, - bool isReverse, - ) { + static List _sortByCreated(List albums, bool isReverse) { final sorted = albums.sortedBy((album) => album.createdAt); return (isReverse ? sorted.reversed : sorted).toList(); } static const AlbumSortFn title = _sortByTitle; - static List _sortByTitle( - List albums, - bool isReverse, - ) { + static List _sortByTitle(List albums, bool isReverse) { final sorted = albums.sortedBy((album) => album.name); return (isReverse ? sorted.reversed : sorted).toList(); } static const AlbumSortFn lastModified = _sortByLastModified; - static List _sortByLastModified( - List albums, - bool isReverse, - ) { + static List _sortByLastModified(List albums, bool isReverse) { final sorted = albums.sortedBy((album) => album.updatedAt); return (isReverse ? sorted.reversed : sorted).toList(); } static const AlbumSortFn assetCount = _sortByAssetCount; - static List _sortByAssetCount( - List albums, - bool isReverse, - ) { - final sorted = - albums.sorted((a, b) => a.assetCount.compareTo(b.assetCount)); + static List _sortByAssetCount(List albums, bool isReverse) { + final sorted = albums.sorted((a, b) => a.assetCount.compareTo(b.assetCount)); return (isReverse ? sorted.reversed : sorted).toList(); } static const AlbumSortFn mostRecent = _sortByMostRecent; - static List _sortByMostRecent( - List albums, - bool isReverse, - ) { + static List _sortByMostRecent(List albums, bool isReverse) { final sorted = albums.sorted((a, b) { // For most recent, we sort by updatedAt in descending order return b.updatedAt.compareTo(a.updatedAt); @@ -59,10 +40,7 @@ class _RemoteAlbumSortHandlers { } static const AlbumSortFn mostOldest = _sortByMostOldest; - static List _sortByMostOldest( - List albums, - bool isReverse, - ) { + static List _sortByMostOldest(List albums, bool isReverse) { final sorted = albums.sorted((a, b) { // For oldest, we sort by createdAt in ascending order return a.createdAt.compareTo(b.createdAt); @@ -73,14 +51,8 @@ class _RemoteAlbumSortHandlers { enum RemoteAlbumSortMode { title("library_page_sort_title", _RemoteAlbumSortHandlers.title), - assetCount( - "library_page_sort_asset_count", - _RemoteAlbumSortHandlers.assetCount, - ), - lastModified( - "library_page_sort_last_modified", - _RemoteAlbumSortHandlers.lastModified, - ), + assetCount("library_page_sort_asset_count", _RemoteAlbumSortHandlers.assetCount), + lastModified("library_page_sort_last_modified", _RemoteAlbumSortHandlers.lastModified), created("library_page_sort_created", _RemoteAlbumSortHandlers.created), mostRecent("sort_recent", _RemoteAlbumSortHandlers.mostRecent), mostOldest("sort_oldest", _RemoteAlbumSortHandlers.mostOldest); diff --git a/mobile/lib/utils/selection_handlers.dart b/mobile/lib/utils/selection_handlers.dart index a5466c83a2..d128ef8fac 100644 --- a/mobile/lib/utils/selection_handlers.dart +++ b/mobile/lib/utils/selection_handlers.dart @@ -16,30 +16,21 @@ import 'package:immich_mobile/widgets/common/location_picker.dart'; import 'package:immich_mobile/widgets/common/share_dialog.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; -void handleShareAssets( - WidgetRef ref, - BuildContext context, - Iterable selection, -) { +void handleShareAssets(WidgetRef ref, BuildContext context, Iterable selection) { showDialog( context: context, builder: (BuildContext buildContext) { - ref - .watch(shareServiceProvider) - .shareAssets(selection.toList(), context) - .then( - (bool status) { - if (!status) { - ImmichToast.show( - context: context, - msg: 'image_viewer_page_state_provider_share_error'.tr(), - toastType: ToastType.error, - gravity: ToastGravity.BOTTOM, - ); - } - buildContext.pop(); - }, - ); + ref.watch(shareServiceProvider).shareAssets(selection.toList(), context).then((bool status) { + if (!status) { + ImmichToast.show( + context: context, + msg: 'image_viewer_page_state_provider_share_error'.tr(), + toastType: ToastType.error, + gravity: ToastGravity.BOTTOM, + ); + } + buildContext.pop(); + }); return const ShareDialog(); }, barrierDismissible: false, @@ -56,20 +47,12 @@ Future handleArchiveAssets( }) async { if (selection.isNotEmpty) { shouldArchive ??= !selection.every((a) => a.isArchived); - await ref - .read(assetProvider.notifier) - .toggleArchive(selection, shouldArchive); + await ref.read(assetProvider.notifier).toggleArchive(selection, shouldArchive); final message = shouldArchive - ? 'moved_to_archive' - .t(context: context, args: {'count': selection.length}) - : 'moved_to_library' - .t(context: context, args: {'count': selection.length}); + ? 'moved_to_archive'.t(context: context, args: {'count': selection.length}) + : 'moved_to_library'.t(context: context, args: {'count': selection.length}); if (context.mounted) { - ImmichToast.show( - context: context, - msg: message, - gravity: toastGravity, - ); + ImmichToast.show(context: context, msg: message, gravity: toastGravity); } } } @@ -83,29 +66,19 @@ Future handleFavoriteAssets( }) async { if (selection.isNotEmpty) { shouldFavorite ??= !selection.every((a) => a.isFavorite); - await ref - .watch(assetProvider.notifier) - .toggleFavorite(selection, shouldFavorite); + await ref.watch(assetProvider.notifier).toggleFavorite(selection, shouldFavorite); final assetOrAssets = selection.length > 1 ? 'assets' : 'asset'; final toastMessage = shouldFavorite ? 'Added ${selection.length} $assetOrAssets to favorites' : 'Removed ${selection.length} $assetOrAssets from favorites'; if (context.mounted) { - ImmichToast.show( - context: context, - msg: toastMessage, - gravity: toastGravity, - ); + ImmichToast.show(context: context, msg: toastMessage, gravity: toastGravity); } } } -Future handleEditDateTime( - WidgetRef ref, - BuildContext context, - List selection, -) async { +Future handleEditDateTime(WidgetRef ref, BuildContext context, List selection) async { DateTime? initialDate; String? timeZone; Duration? offset; @@ -131,28 +104,17 @@ Future handleEditDateTime( ref.read(assetServiceProvider).changeDateTime(selection.toList(), dateTime); } -Future handleEditLocation( - WidgetRef ref, - BuildContext context, - List selection, -) async { +Future handleEditLocation(WidgetRef ref, BuildContext context, List selection) async { LatLng? initialLatLng; if (selection.length == 1) { final asset = selection.first; final assetWithExif = await ref.watch(assetServiceProvider).loadExif(asset); - if (assetWithExif.exifInfo?.latitude != null && - assetWithExif.exifInfo?.longitude != null) { - initialLatLng = LatLng( - assetWithExif.exifInfo!.latitude!, - assetWithExif.exifInfo!.longitude!, - ); + if (assetWithExif.exifInfo?.latitude != null && assetWithExif.exifInfo?.longitude != null) { + initialLatLng = LatLng(assetWithExif.exifInfo!.latitude!, assetWithExif.exifInfo!.longitude!); } } - final location = await showLocationPicker( - context: context, - initialLatLng: initialLatLng, - ); + final location = await showLocationPicker(context: context, initialLatLng: initialLatLng); if (location == null) { return; @@ -168,20 +130,14 @@ Future handleSetAssetsVisibility( List selection, ) async { if (selection.isNotEmpty) { - await ref - .watch(assetProvider.notifier) - .setLockedView(selection, visibility); + await ref.watch(assetProvider.notifier).setLockedView(selection, visibility); final assetOrAssets = selection.length > 1 ? 'assets' : 'asset'; final toastMessage = visibility == AssetVisibilityEnum.locked ? 'Added ${selection.length} $assetOrAssets to locked folder' : 'Removed ${selection.length} $assetOrAssets from locked folder'; if (context.mounted) { - ImmichToast.show( - context: context, - msg: toastMessage, - gravity: ToastGravity.BOTTOM, - ); + ImmichToast.show(context: context, msg: toastMessage, gravity: ToastGravity.BOTTOM); } } } diff --git a/mobile/lib/utils/throttle.dart b/mobile/lib/utils/throttle.dart index bc0dcf9e2f..8b41d92318 100644 --- a/mobile/lib/utils/throttle.dart +++ b/mobile/lib/utils/throttle.dart @@ -9,8 +9,7 @@ class Throttler { Throttler({required this.interval}); T? run(T Function() action) { - if (_lastActionTime == null || - (DateTime.now().difference(_lastActionTime!) > interval)) { + if (_lastActionTime == null || (DateTime.now().difference(_lastActionTime!) > interval)) { final response = action(); _lastActionTime = DateTime.now(); return response; @@ -26,17 +25,11 @@ class Throttler { /// Creates a [Throttler] that will be disposed automatically. If no [interval] is provided, a /// default interval of 300ms is used to throttle the function calls -Throttler useThrottler({ - Duration interval = const Duration(milliseconds: 300), - List? keys, -}) => +Throttler useThrottler({Duration interval = const Duration(milliseconds: 300), List? keys}) => use(_ThrottleHook(interval: interval, keys: keys)); class _ThrottleHook extends Hook { - const _ThrottleHook({ - required this.interval, - super.keys, - }); + const _ThrottleHook({required this.interval, super.keys}); final Duration interval; diff --git a/mobile/lib/utils/thumbnail_utils.dart b/mobile/lib/utils/thumbnail_utils.dart index 33dd916980..685dc2b1c2 100644 --- a/mobile/lib/utils/thumbnail_utils.dart +++ b/mobile/lib/utils/thumbnail_utils.dart @@ -3,17 +3,11 @@ import 'package:immich_mobile/domain/models/exif.model.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; -String getAltText( - ExifInfo? exifInfo, - DateTime fileCreatedAt, - AssetType type, - List peopleNames, -) { +String getAltText(ExifInfo? exifInfo, DateTime fileCreatedAt, AssetType type, List peopleNames) { if (exifInfo?.description != null && exifInfo!.description!.isNotEmpty) { return exifInfo.description!; } - final (template, args) = - getAltTextTemplate(exifInfo, fileCreatedAt, type, peopleNames); + final (template, args) = getAltTextTemplate(exifInfo, fileCreatedAt, type, peopleNames); return template.t(args: args); } @@ -42,14 +36,14 @@ String getAltText( 1 => "image_alt_text_date_place_1_person", 2 => "image_alt_text_date_place_2_people", 3 => "image_alt_text_date_place_3_people", - _ => "image_alt_text_date_place_4_or_more_people" + _ => "image_alt_text_date_place_4_or_more_people", }) : (switch (peopleNames.length) { 0 => "image_alt_text_date", 1 => "image_alt_text_date_1_person", 2 => "image_alt_text_date_2_people", 3 => "image_alt_text_date_3_people", - _ => "image_alt_text_date_4_or_more_people" + _ => "image_alt_text_date_4_or_more_people", }); return (template, args); } diff --git a/mobile/lib/utils/upload.dart b/mobile/lib/utils/upload.dart deleted file mode 100644 index a0b77f1d93..0000000000 --- a/mobile/lib/utils/upload.dart +++ /dev/null @@ -1 +0,0 @@ -const uploadGroup = 'upload_group'; diff --git a/mobile/lib/utils/url_helper.dart b/mobile/lib/utils/url_helper.dart index 187026b53c..e3d5b8ed57 100644 --- a/mobile/lib/utils/url_helper.dart +++ b/mobile/lib/utils/url_helper.dart @@ -4,8 +4,7 @@ import 'package:punycode/punycode.dart'; String sanitizeUrl(String url) { // Add schema if none is set - final urlWithSchema = - url.trimLeft().startsWith(RegExp(r"https?://")) ? url : "https://$url"; + final urlWithSchema = url.trimLeft().startsWith(RegExp(r"https?://")) ? url : "https://$url"; // Remove trailing slash(es) return urlWithSchema.trimRight().replaceFirst(RegExp(r"/+$"), ""); @@ -45,13 +44,14 @@ String punycodeEncodeUrl(String serverUrl) { final serverUri = Uri.tryParse(serverUrl); if (serverUri == null || serverUri.host.isEmpty) return ''; - final encodedHost = Uri.decodeComponent(serverUri.host).split('.').map( - (segment) { - // If segment is already ASCII, then return as it is. - if (segment.runes.every((c) => c < 0x80)) return segment; - return 'xn--${punycodeEncode(segment)}'; - }, - ).join('.'); + final encodedHost = Uri.decodeComponent(serverUri.host) + .split('.') + .map((segment) { + // If segment is already ASCII, then return as it is. + if (segment.runes.every((c) => c < 0x80)) return segment; + return 'xn--${punycodeEncode(segment)}'; + }) + .join('.'); return serverUri.replace(host: encodedHost).toString(); } @@ -77,15 +77,16 @@ String? punycodeDecodeUrl(String? serverUrl) { final serverUri = serverUrl != null ? Uri.tryParse(serverUrl) : null; if (serverUri == null || serverUri.host.isEmpty) return null; - final decodedHost = serverUri.host.split('.').map( - (segment) { - if (segment.toLowerCase().startsWith('xn--')) { - return punycodeDecode(segment.substring(4)); - } - // If segment is not punycode encoded, then return as it is. - return segment; - }, - ).join('.'); + final decodedHost = serverUri.host + .split('.') + .map((segment) { + if (segment.toLowerCase().startsWith('xn--')) { + return punycodeDecode(segment.substring(4)); + } + // If segment is not punycode encoded, then return as it is. + return segment; + }) + .join('.'); return Uri.decodeFull(serverUri.replace(host: decodedHost).toString()); } diff --git a/mobile/lib/utils/version_compatibility.dart b/mobile/lib/utils/version_compatibility.dart index 19d9aa38d4..fa8dfb0b9e 100644 --- a/mobile/lib/utils/version_compatibility.dart +++ b/mobile/lib/utils/version_compatibility.dart @@ -1,9 +1,4 @@ -String? getVersionCompatibilityMessage( - int appMajor, - int appMinor, - int serverMajor, - int serverMinor, -) { +String? getVersionCompatibilityMessage(int appMajor, int appMinor, int serverMajor, int serverMinor) { if (serverMajor != appMajor) { return 'Your app major version is not compatible with the server!'; } diff --git a/mobile/lib/widgets/activities/activity_text_field.dart b/mobile/lib/widgets/activities/activity_text_field.dart index ce4f1364a3..e3958b6287 100644 --- a/mobile/lib/widgets/activities/activity_text_field.dart +++ b/mobile/lib/widgets/activities/activity_text_field.dart @@ -13,32 +13,23 @@ class ActivityTextField extends HookConsumerWidget { final String? likeId; final Function(String) onSubmit; - const ActivityTextField({ - required this.onSubmit, - this.isEnabled = true, - this.likeId, - super.key, - }); + const ActivityTextField({required this.onSubmit, this.isEnabled = true, this.likeId, super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final album = ref.watch(currentAlbumProvider)!; final asset = ref.watch(currentAssetProvider); - final activityNotifier = ref - .read(albumActivityProvider(album.remoteId!, asset?.remoteId).notifier); + final activityNotifier = ref.read(albumActivityProvider(album.remoteId!, asset?.remoteId).notifier); final user = ref.watch(currentUserProvider); final inputController = useTextEditingController(); final inputFocusNode = useFocusNode(); final liked = likeId != null; // Show keyboard immediately on activities open - useEffect( - () { - inputFocusNode.requestFocus(); - return null; - }, - [], - ); + useEffect(() { + inputFocusNode.requestFocus(); + return null; + }, []); // Pass text to callback and reset controller void onEditingComplete() { @@ -71,31 +62,19 @@ class ActivityTextField extends HookConsumerWidget { prefixIcon: user != null ? Padding( padding: const EdgeInsets.symmetric(horizontal: 15), - child: UserCircleAvatar( - user: user, - size: 30, - radius: 15, - ), + child: UserCircleAvatar(user: user, size: 30, radius: 15), ) : null, suffixIcon: Padding( padding: const EdgeInsets.only(right: 10), child: IconButton( - icon: Icon( - liked ? Icons.favorite_rounded : Icons.favorite_border_rounded, - ), + icon: Icon(liked ? Icons.favorite_rounded : Icons.favorite_border_rounded), onPressed: liked ? removeLike : addLike, ), ), suffixIconColor: liked ? Colors.red[700] : null, - hintText: !isEnabled - ? 'shared_album_activities_input_disable'.tr() - : 'say_something'.tr(), - hintStyle: TextStyle( - fontWeight: FontWeight.normal, - fontSize: 14, - color: Colors.grey[600], - ), + hintText: !isEnabled ? 'shared_album_activities_input_disable'.tr() : 'say_something'.tr(), + hintStyle: TextStyle(fontWeight: FontWeight.normal, fontSize: 14, color: Colors.grey[600]), ), onEditingComplete: onEditingComplete, onTapOutside: (_) => inputFocusNode.unfocus(), diff --git a/mobile/lib/widgets/activities/activity_tile.dart b/mobile/lib/widgets/activities/activity_tile.dart index 2dd16b73cb..4b66bd5eaf 100644 --- a/mobile/lib/widgets/activities/activity_tile.dart +++ b/mobile/lib/widgets/activities/activity_tile.dart @@ -26,10 +26,7 @@ class ActivityTile extends HookConsumerWidget { ? Container( width: 44, alignment: Alignment.center, - child: Icon( - Icons.favorite_rounded, - color: Colors.red[700], - ), + child: Icon(Icons.favorite_rounded, color: Colors.red[700]), ) : UserCircleAvatar(user: activity.user), title: _ActivityTitle( @@ -38,11 +35,8 @@ class ActivityTile extends HookConsumerWidget { leftAlign: isLike || showAssetThumbnail, ), // No subtitle for like, so center title - titleAlignment: - !isLike ? ListTileTitleAlignment.top : ListTileTitleAlignment.center, - trailing: showAssetThumbnail - ? _ActivityAssetThumbnail(activity.assetId!) - : null, + titleAlignment: !isLike ? ListTileTitleAlignment.top : ListTileTitleAlignment.center, + trailing: showAssetThumbnail ? _ActivityAssetThumbnail(activity.assetId!) : null, subtitle: !isLike ? Text(activity.comment!) : null, ); } @@ -53,33 +47,19 @@ class _ActivityTitle extends StatelessWidget { final String createdAt; final bool leftAlign; - const _ActivityTitle({ - required this.userName, - required this.createdAt, - required this.leftAlign, - }); + const _ActivityTitle({required this.userName, required this.createdAt, required this.leftAlign}); @override Widget build(BuildContext context) { final textColor = context.isDarkTheme ? Colors.white : Colors.black; - final textStyle = context.textTheme.bodyMedium - ?.copyWith(color: textColor.withValues(alpha: 0.6)); + final textStyle = context.textTheme.bodyMedium?.copyWith(color: textColor.withValues(alpha: 0.6)); return Row( - mainAxisAlignment: - leftAlign ? MainAxisAlignment.start : MainAxisAlignment.spaceBetween, + mainAxisAlignment: leftAlign ? MainAxisAlignment.start : MainAxisAlignment.spaceBetween, mainAxisSize: leftAlign ? MainAxisSize.min : MainAxisSize.max, children: [ - Text( - userName, - style: textStyle, - overflow: TextOverflow.ellipsis, - ), - if (leftAlign) - Text( - " • ", - style: textStyle, - ), + Text(userName, style: textStyle, overflow: TextOverflow.ellipsis), + if (leftAlign) Text(" • ", style: textStyle), Expanded( child: Text( createdAt, @@ -106,9 +86,7 @@ class _ActivityAssetThumbnail extends StatelessWidget { decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(4)), image: DecorationImage( - image: ImmichRemoteThumbnailProvider( - assetId: assetId, - ), + image: ImmichRemoteThumbnailProvider(assetId: assetId), fit: BoxFit.cover, ), ), diff --git a/mobile/lib/widgets/activities/dismissible_activity.dart b/mobile/lib/widgets/activities/dismissible_activity.dart index b6c083f616..2f017d51ed 100644 --- a/mobile/lib/widgets/activities/dismissible_activity.dart +++ b/mobile/lib/widgets/activities/dismissible_activity.dart @@ -8,20 +8,13 @@ class DismissibleActivity extends StatelessWidget { final ActivityTile body; final Function(String)? onDismiss; - const DismissibleActivity( - this.activityId, - this.body, { - this.onDismiss, - super.key, - }); + const DismissibleActivity(this.activityId, this.body, {this.onDismiss, super.key}); @override Widget build(BuildContext context) { return Dismissible( key: Key(activityId), - dismissThresholds: const { - DismissDirection.horizontal: 0.7, - }, + dismissThresholds: const {DismissDirection.horizontal: 0.7}, direction: DismissDirection.horizontal, confirmDismiss: (direction) => onDismiss != null ? showDialog( @@ -51,10 +44,7 @@ class _DismissBackground extends StatelessWidget { final AlignmentDirectional alignment; final bool withDeleteIcon; - const _DismissBackground({ - required this.withDeleteIcon, - this.alignment = AlignmentDirectional.centerStart, - }); + const _DismissBackground({required this.withDeleteIcon, this.alignment = AlignmentDirectional.centerStart}); @override Widget build(BuildContext context) { @@ -64,10 +54,7 @@ class _DismissBackground extends StatelessWidget { child: withDeleteIcon ? const Padding( padding: EdgeInsets.all(15), - child: Icon( - Icons.delete_sweep_rounded, - color: Colors.black, - ), + child: Icon(Icons.delete_sweep_rounded, color: Colors.black), ) : null, ); diff --git a/mobile/lib/widgets/album/add_to_album_bottom_sheet.dart b/mobile/lib/widgets/album/add_to_album_bottom_sheet.dart index c256c558d6..d8f6a8885a 100644 --- a/mobile/lib/widgets/album/add_to_album_bottom_sheet.dart +++ b/mobile/lib/widgets/album/add_to_album_bottom_sheet.dart @@ -17,46 +17,33 @@ class AddToAlbumBottomSheet extends HookConsumerWidget { /// The asset to add to an album final List assets; - const AddToAlbumBottomSheet({ - super.key, - required this.assets, - }); + const AddToAlbumBottomSheet({super.key, required this.assets}); @override Widget build(BuildContext context, WidgetRef ref) { final albums = ref.watch(albumProvider).where((a) => a.isRemote).toList(); final albumService = ref.watch(albumServiceProvider); - useEffect( - () { - // Fetch album updates, e.g., cover image - ref.read(albumProvider.notifier).refreshRemoteAlbums(); + useEffect(() { + // Fetch album updates, e.g., cover image + ref.read(albumProvider.notifier).refreshRemoteAlbums(); - return null; - }, - [], - ); + return null; + }, []); void addToAlbum(Album album) async { - final result = await albumService.addAssets( - album, - assets, - ); + final result = await albumService.addAssets(album, assets); if (result != null) { if (result.alreadyInAlbum.isNotEmpty) { ImmichToast.show( context: context, - msg: 'add_to_album_bottom_sheet_already_exists'.tr( - namedArgs: {"album": album.name}, - ), + msg: 'add_to_album_bottom_sheet_already_exists'.tr(namedArgs: {"album": album.name}), ); } else { ImmichToast.show( context: context, - msg: 'add_to_album_bottom_sheet_added'.tr( - namedArgs: {"album": album.name}, - ), + msg: 'add_to_album_bottom_sheet_added'.tr(namedArgs: {"album": album.name}), ); } } @@ -66,10 +53,7 @@ class AddToAlbumBottomSheet extends HookConsumerWidget { return Card( elevation: 0, shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(15), - topRight: Radius.circular(15), - ), + borderRadius: BorderRadius.only(topLeft: Radius.circular(15), topRight: Radius.circular(15)), ), child: CustomScrollView( slivers: [ @@ -80,33 +64,17 @@ class AddToAlbumBottomSheet extends HookConsumerWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 12), - const Align( - alignment: Alignment.center, - child: CustomDraggingHandle(), - ), + const Align(alignment: Alignment.center, child: CustomDraggingHandle()), const SizedBox(height: 12), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - 'add_to_album'.tr(), - style: context.textTheme.displayMedium, - ), + Text('add_to_album'.tr(), style: context.textTheme.displayMedium), TextButton.icon( - icon: Icon( - Icons.add, - color: context.primaryColor, - ), - label: Text( - 'common_create_new_album'.tr(), - style: TextStyle(color: context.primaryColor), - ), + icon: Icon(Icons.add, color: context.primaryColor), + label: Text('common_create_new_album'.tr(), style: TextStyle(color: context.primaryColor)), onPressed: () { - context.pushRoute( - CreateAlbumRoute( - assets: assets, - ), - ); + context.pushRoute(CreateAlbumRoute(assets: assets)); }, ), ], diff --git a/mobile/lib/widgets/album/add_to_album_sliverlist.dart b/mobile/lib/widgets/album/add_to_album_sliverlist.dart index 3472e2179b..defbd90388 100644 --- a/mobile/lib/widgets/album/add_to_album_sliverlist.dart +++ b/mobile/lib/widgets/album/add_to_album_sliverlist.dart @@ -25,13 +25,13 @@ class AddToAlbumSliverList extends HookConsumerWidget { final albumSortMode = ref.watch(albumSortByOptionsProvider); final albumSortIsReverse = ref.watch(albumSortOrderProvider); final sortedAlbums = albumSortMode.sortFn(albums, albumSortIsReverse); - final sortedSharedAlbums = - albumSortMode.sortFn(sharedAlbums, albumSortIsReverse); + final sortedSharedAlbums = albumSortMode.sortFn(sharedAlbums, albumSortIsReverse); return SliverList( - delegate: SliverChildBuilderDelegate( - childCount: albums.length + (sharedAlbums.isEmpty ? 0 : 1), - (context, index) { + delegate: SliverChildBuilderDelegate(childCount: albums.length + (sharedAlbums.isEmpty ? 0 : 1), ( + context, + index, + ) { // Build shared expander if (index == 0 && sortedSharedAlbums.isNotEmpty) { return Padding( @@ -47,9 +47,7 @@ class AddToAlbumSliverList extends HookConsumerWidget { itemCount: sortedSharedAlbums.length, itemBuilder: (context, index) => AlbumThumbnailListTile( album: sortedSharedAlbums[index], - onTap: enabled - ? () => onAddToAlbum(sortedSharedAlbums[index]) - : () {}, + onTap: enabled ? () => onAddToAlbum(sortedSharedAlbums[index]) : () {}, ), ), ], @@ -60,10 +58,7 @@ class AddToAlbumSliverList extends HookConsumerWidget { // Build albums list final offset = index - (sharedAlbums.isNotEmpty ? 1 : 0); final album = sortedAlbums[offset]; - return AlbumThumbnailListTile( - album: album, - onTap: enabled ? () => onAddToAlbum(album) : () {}, - ); + return AlbumThumbnailListTile(album: album, onTap: enabled ? () => onAddToAlbum(album) : () {}); }), ); } diff --git a/mobile/lib/widgets/album/album_action_filled_button.dart b/mobile/lib/widgets/album/album_action_filled_button.dart index 48a8a27f59..04447ffab6 100644 --- a/mobile/lib/widgets/album/album_action_filled_button.dart +++ b/mobile/lib/widgets/album/album_action_filled_button.dart @@ -6,12 +6,7 @@ class AlbumActionFilledButton extends StatelessWidget { final String labelText; final IconData iconData; - const AlbumActionFilledButton({ - super.key, - this.onPressed, - required this.labelText, - required this.iconData, - }); + const AlbumActionFilledButton({super.key, this.onPressed, required this.labelText, required this.iconData}); @override Widget build(BuildContext context) { @@ -20,24 +15,12 @@ class AlbumActionFilledButton extends StatelessWidget { child: OutlinedButton.icon( style: OutlinedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 0, horizontal: 16), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(20)), - ), - side: BorderSide( - color: context.colorScheme.surfaceContainerHighest, - width: 1, - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(20))), + side: BorderSide(color: context.colorScheme.surfaceContainerHighest, width: 1), backgroundColor: context.colorScheme.surfaceContainerHigh, ), - icon: Icon( - iconData, - size: 18, - color: context.primaryColor, - ), - label: Text( - labelText, - style: context.textTheme.labelLarge?.copyWith(), - ), + icon: Icon(iconData, size: 18, color: context.primaryColor), + label: Text(labelText, style: context.textTheme.labelLarge?.copyWith()), onPressed: onPressed, ), ); diff --git a/mobile/lib/widgets/album/album_thumbnail_card.dart b/mobile/lib/widgets/album/album_thumbnail_card.dart index d78f391754..6c56f5d843 100644 --- a/mobile/lib/widgets/album/album_thumbnail_card.dart +++ b/mobile/lib/widgets/album/album_thumbnail_card.dart @@ -16,13 +16,7 @@ class AlbumThumbnailCard extends ConsumerWidget { final bool showOwner; final bool showTitle; - const AlbumThumbnailCard({ - super.key, - required this.album, - this.onTap, - this.showOwner = false, - this.showTitle = true, - }); + const AlbumThumbnailCard({super.key, required this.album, this.onTap, this.showOwner = false, this.showTitle = true}); final Album album; @@ -36,24 +30,14 @@ class AlbumThumbnailCard extends ConsumerWidget { return Container( height: cardSize, width: cardSize, - decoration: BoxDecoration( - color: context.colorScheme.surfaceContainerHigh, - ), + decoration: BoxDecoration(color: context.colorScheme.surfaceContainerHigh), child: Center( - child: Icon( - Icons.no_photography, - size: cardSize * .15, - color: context.colorScheme.primary, - ), + child: Icon(Icons.no_photography, size: cardSize * .15, color: context.colorScheme.primary), ), ); } - buildAlbumThumbnail() => ImmichThumbnail( - asset: album.thumbnail.value, - width: cardSize, - height: cardSize, - ); + buildAlbumThumbnail() => ImmichThumbnail(asset: album.thumbnail.value, width: cardSize, height: cardSize); buildAlbumTextRow() { // Add the owner name to the subtitle @@ -62,12 +46,7 @@ class AlbumThumbnailCard extends ConsumerWidget { if (album.ownerId == ref.read(currentUserProvider)?.id) { owner = 'owned'.tr(); } else if (album.ownerName != null) { - owner = 'shared_by_user'.t( - context: context, - args: { - 'user': album.ownerName!, - }, - ); + owner = 'shared_by_user'.t(context: context, args: {'user': album.ownerName!}); } } @@ -75,19 +54,12 @@ class AlbumThumbnailCard extends ConsumerWidget { TextSpan( children: [ TextSpan( - text: 'items_count'.t( - context: context, - args: { - 'count': album.assetCount, - }, - ), + text: 'items_count'.t(context: context, args: {'count': album.assetCount}), ), if (owner != null) const TextSpan(text: ' • '), if (owner != null) TextSpan(text: owner), ], - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), overflow: TextOverflow.fade, ); @@ -106,12 +78,8 @@ class AlbumThumbnailCard extends ConsumerWidget { width: cardSize, height: cardSize, child: ClipRRect( - borderRadius: const BorderRadius.all( - Radius.circular(20), - ), - child: album.thumbnail.value == null - ? buildEmptyThumbnail() - : buildAlbumThumbnail(), + borderRadius: const BorderRadius.all(Radius.circular(20)), + child: album.thumbnail.value == null ? buildEmptyThumbnail() : buildAlbumThumbnail(), ), ), if (showTitle) ...[ diff --git a/mobile/lib/widgets/album/album_thumbnail_listtile.dart b/mobile/lib/widgets/album/album_thumbnail_listtile.dart index f35d4b7ede..423410eedf 100644 --- a/mobile/lib/widgets/album/album_thumbnail_listtile.dart +++ b/mobile/lib/widgets/album/album_thumbnail_listtile.dart @@ -11,11 +11,7 @@ import 'package:immich_mobile/utils/image_url_builder.dart'; import 'package:openapi/api.dart'; class AlbumThumbnailListTile extends StatelessWidget { - const AlbumThumbnailListTile({ - super.key, - required this.album, - this.onTap, - }); + const AlbumThumbnailListTile({super.key, required this.album, this.onTap}); final Album album; final void Function()? onTap; @@ -26,15 +22,11 @@ class AlbumThumbnailListTile extends StatelessWidget { buildEmptyThumbnail() { return Container( - decoration: BoxDecoration( - color: context.isDarkTheme ? Colors.grey[800] : Colors.grey[200], - ), + decoration: BoxDecoration(color: context.isDarkTheme ? Colors.grey[800] : Colors.grey[200]), child: SizedBox( height: cardSize, width: cardSize, - child: const Center( - child: Icon(Icons.no_photography), - ), + child: const Center(child: Icon(Icons.no_photography)), ), ); } @@ -45,21 +37,17 @@ class AlbumThumbnailListTile extends StatelessWidget { height: cardSize, fit: BoxFit.cover, fadeInDuration: const Duration(milliseconds: 200), - imageUrl: getAlbumThumbnailUrl( - album, - type: AssetMediaSize.thumbnail, - ), + imageUrl: getAlbumThumbnailUrl(album, type: AssetMediaSize.thumbnail), httpHeaders: ApiService.getRequestHeaders(), - cacheKey: - getAlbumThumbNailCacheKey(album, type: AssetMediaSize.thumbnail), - errorWidget: (context, url, error) => - const Icon(Icons.image_not_supported_outlined), + cacheKey: getAlbumThumbNailCacheKey(album, type: AssetMediaSize.thumbnail), + errorWidget: (context, url, error) => const Icon(Icons.image_not_supported_outlined), ); } return GestureDetector( behavior: HitTestBehavior.opaque, - onTap: onTap ?? + onTap: + onTap ?? () { context.pushRoute(AlbumViewerRoute(albumId: album.id)); }, @@ -70,9 +58,7 @@ class AlbumThumbnailListTile extends StatelessWidget { children: [ ClipRRect( borderRadius: const BorderRadius.all(Radius.circular(8)), - child: album.thumbnail.value == null - ? buildEmptyThumbnail() - : buildAlbumThumbnail(), + child: album.thumbnail.value == null ? buildEmptyThumbnail() : buildAlbumThumbnail(), ), Expanded( child: Padding( @@ -83,37 +69,18 @@ class AlbumThumbnailListTile extends StatelessWidget { Text( album.name, overflow: TextOverflow.ellipsis, - style: const TextStyle( - fontWeight: FontWeight.bold, - ), + style: const TextStyle(fontWeight: FontWeight.bold), ), Row( mainAxisSize: MainAxisSize.min, children: [ Text( - 'items_count'.t( - context: context, - args: { - 'count': album.assetCount, - }, - ), - style: const TextStyle( - fontSize: 12, - ), + 'items_count'.t(context: context, args: {'count': album.assetCount}), + style: const TextStyle(fontSize: 12), ), if (album.shared) ...[ - const Text( - ' • ', - style: TextStyle( - fontSize: 12, - ), - ), - Text( - 'shared'.tr(), - style: const TextStyle( - fontSize: 12, - ), - ), + const Text(' • ', style: TextStyle(fontSize: 12)), + Text('shared'.tr(), style: const TextStyle(fontSize: 12)), ], ], ), diff --git a/mobile/lib/widgets/album/album_title_text_field.dart b/mobile/lib/widgets/album/album_title_text_field.dart index 7807a6e6ae..0a7438b7ae 100644 --- a/mobile/lib/widgets/album/album_title_text_field.dart +++ b/mobile/lib/widgets/album/album_title_text_field.dart @@ -31,11 +31,7 @@ class AlbumTitleTextField extends ConsumerWidget { ref.watch(albumTitleProvider.notifier).setAlbumTitle(v); }, focusNode: albumTitleTextFieldFocusNode, - style: TextStyle( - fontSize: 28, - color: context.colorScheme.onSurface, - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 28, color: context.colorScheme.onSurface, fontWeight: FontWeight.bold), controller: albumTitleController, onTap: () { isAlbumTitleTextFieldFocus.value = true; @@ -52,24 +48,17 @@ class AlbumTitleTextField extends ConsumerWidget { albumTitleController.clear(); isAlbumTitleEmpty.value = true; }, - icon: Icon( - Icons.cancel_rounded, - color: context.primaryColor, - ), + icon: Icon(Icons.cancel_rounded, color: context.primaryColor), splashRadius: 10, ) : null, enabledBorder: const OutlineInputBorder( borderSide: BorderSide(color: Colors.transparent), - borderRadius: BorderRadius.all( - Radius.circular(10), - ), + borderRadius: BorderRadius.all(Radius.circular(10)), ), focusedBorder: const OutlineInputBorder( borderSide: BorderSide(color: Colors.transparent), - borderRadius: BorderRadius.all( - Radius.circular(10), - ), + borderRadius: BorderRadius.all(Radius.circular(10)), ), hintText: 'add_a_title'.tr(), hintStyle: context.themeData.inputDecorationTheme.hintStyle?.copyWith( diff --git a/mobile/lib/widgets/album/album_viewer_appbar.dart b/mobile/lib/widgets/album/album_viewer_appbar.dart index 14715e40a9..420218d7e5 100644 --- a/mobile/lib/widgets/album/album_viewer_appbar.dart +++ b/mobile/lib/widgets/album/album_viewer_appbar.dart @@ -12,8 +12,7 @@ import 'package:immich_mobile/providers/album/current_album.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; -class AlbumViewerAppbar extends HookConsumerWidget - implements PreferredSizeWidget { +class AlbumViewerAppbar extends HookConsumerWidget implements PreferredSizeWidget { const AlbumViewerAppbar({ super.key, required this.userId, @@ -53,13 +52,10 @@ class AlbumViewerAppbar extends HookConsumerWidget final newAlbumDescription = albumViewer.editDescriptionText; final isEditAlbum = albumViewer.isEditAlbum; - final comments = album.shared - ? ref.watch(activityStatisticsProvider(album.remoteId!)) - : 0; + final comments = album.shared ? ref.watch(activityStatisticsProvider(album.remoteId!)) : 0; deleteAlbum() async { - final bool success = - await ref.watch(albumProvider.notifier).deleteAlbum(album); + final bool success = await ref.watch(albumProvider.notifier).deleteAlbum(album); context.navigateTo(const TabControllerRoute(children: [AlbumsRoute()])); @@ -86,10 +82,7 @@ class AlbumViewerAppbar extends HookConsumerWidget onPressed: () => context.pop('Cancel'), child: Text( 'cancel', - style: TextStyle( - color: context.primaryColor, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold), ).tr(), ), TextButton( @@ -99,10 +92,7 @@ class AlbumViewerAppbar extends HookConsumerWidget }, child: Text( 'confirm', - style: TextStyle( - fontWeight: FontWeight.bold, - color: context.colorScheme.error, - ), + style: TextStyle(fontWeight: FontWeight.bold, color: context.colorScheme.error), ).tr(), ), ], @@ -112,8 +102,7 @@ class AlbumViewerAppbar extends HookConsumerWidget } void onLeaveAlbumPressed() async { - bool isSuccess = - await ref.watch(albumProvider.notifier).leaveAlbum(album); + bool isSuccess = await ref.watch(albumProvider.notifier).leaveAlbum(album); if (isSuccess) { context.navigateTo(const TabControllerRoute(children: [AlbumsRoute()])); @@ -133,10 +122,7 @@ class AlbumViewerAppbar extends HookConsumerWidget album.ownerId == userId ? ListTile( leading: const Icon(Icons.delete_forever_rounded), - title: const Text( - 'delete_album', - style: TextStyle(fontWeight: FontWeight.w500), - ).tr(), + title: const Text('delete_album', style: TextStyle(fontWeight: FontWeight.w500)).tr(), onTap: onDeleteAlbumPressed, ) : ListTile( @@ -152,8 +138,7 @@ class AlbumViewerAppbar extends HookConsumerWidget } void onSortOrderToggled() async { - final updatedAlbum = - await ref.read(albumProvider.notifier).toggleSortOrder(album); + final updatedAlbum = await ref.read(albumProvider.notifier).toggleSortOrder(album); if (updatedAlbum == null) { ImmichToast.show( @@ -178,18 +163,12 @@ class AlbumViewerAppbar extends HookConsumerWidget onAddUsers(); } }, - title: const Text( - "album_viewer_page_share_add_users", - style: TextStyle(fontWeight: FontWeight.w500), - ).tr(), + title: const Text("album_viewer_page_share_add_users", style: TextStyle(fontWeight: FontWeight.w500)).tr(), ), ListTile( leading: const Icon(Icons.swap_vert_rounded), onTap: onSortOrderToggled, - title: const Text( - "change_display_order", - style: TextStyle(fontWeight: FontWeight.w500), - ).tr(), + title: const Text("change_display_order", style: TextStyle(fontWeight: FontWeight.w500)).tr(), ), ListTile( leading: const Icon(Icons.link_rounded), @@ -197,18 +176,12 @@ class AlbumViewerAppbar extends HookConsumerWidget context.pushRoute(SharedLinkEditRoute(albumId: album.remoteId)); context.pop(); }, - title: const Text( - "control_bottom_app_bar_share_link", - style: TextStyle(fontWeight: FontWeight.w500), - ).tr(), + title: const Text("control_bottom_app_bar_share_link", style: TextStyle(fontWeight: FontWeight.w500)).tr(), ), ListTile( leading: const Icon(Icons.settings_rounded), onTap: () => context.navigateTo(const AlbumOptionsRoute()), - title: const Text( - "options", - style: TextStyle(fontWeight: FontWeight.w500), - ).tr(), + title: const Text("options", style: TextStyle(fontWeight: FontWeight.w500)).tr(), ), ]; @@ -222,10 +195,7 @@ class AlbumViewerAppbar extends HookConsumerWidget onAddPhotos(); } }, - title: const Text( - "add_photos", - style: TextStyle(fontWeight: FontWeight.w500), - ).tr(), + title: const Text("add_photos", style: TextStyle(fontWeight: FontWeight.w500)).tr(), ), ]; showModalBottomSheet( @@ -241,8 +211,7 @@ class AlbumViewerAppbar extends HookConsumerWidget children: [ ...buildBottomSheetActions(), if (onAddPhotos != null) ...commonActions, - if (onAddPhotos != null && userId == album.ownerId) - ...ownerActions, + if (onAddPhotos != null && userId == album.ownerId) ...ownerActions, ], ), ), @@ -257,18 +226,13 @@ class AlbumViewerAppbar extends HookConsumerWidget icon: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ - const Icon( - Icons.mode_comment_outlined, - ), + const Icon(Icons.mode_comment_outlined), if (comments != 0) Padding( padding: const EdgeInsets.only(left: 5), child: Text( comments.toString(), - style: TextStyle( - fontWeight: FontWeight.bold, - color: context.primaryColor, - ), + style: TextStyle(fontWeight: FontWeight.bold, color: context.primaryColor), ), ), ], @@ -281,9 +245,7 @@ class AlbumViewerAppbar extends HookConsumerWidget return IconButton( onPressed: () async { if (newAlbumTitle.isNotEmpty) { - bool isSuccess = await ref - .watch(albumViewerProvider.notifier) - .changeAlbumTitle(album, newAlbumTitle); + bool isSuccess = await ref.watch(albumViewerProvider.notifier).changeAlbumTitle(album, newAlbumTitle); if (!isSuccess) { ImmichToast.show( context: context, @@ -330,14 +292,9 @@ class AlbumViewerAppbar extends HookConsumerWidget leading: buildLeadingButton(), centerTitle: false, actions: [ - if (album.shared && (album.activityEnabled || comments != 0)) - buildActivitiesButton(), + if (album.shared && (album.activityEnabled || comments != 0)) buildActivitiesButton(), if (album.isRemote) ...[ - IconButton( - splashRadius: 25, - onPressed: buildBottomSheet, - icon: const Icon(Icons.more_horiz_rounded), - ), + IconButton(splashRadius: 25, onPressed: buildBottomSheet, icon: const Icon(Icons.more_horiz_rounded)), ], ], ); diff --git a/mobile/lib/widgets/album/album_viewer_editable_description.dart b/mobile/lib/widgets/album/album_viewer_editable_description.dart index b82e7f3d83..decd268ff3 100644 --- a/mobile/lib/widgets/album/album_viewer_editable_description.dart +++ b/mobile/lib/widgets/album/album_viewer_editable_description.dart @@ -8,40 +8,31 @@ import 'package:immich_mobile/providers/album/album_viewer.provider.dart'; class AlbumViewerEditableDescription extends HookConsumerWidget { final String albumDescription; final FocusNode descriptionFocusNode; - const AlbumViewerEditableDescription({ - super.key, - required this.albumDescription, - required this.descriptionFocusNode, - }); + const AlbumViewerEditableDescription({super.key, required this.albumDescription, required this.descriptionFocusNode}); @override Widget build(BuildContext context, WidgetRef ref) { final albumViewerState = ref.watch(albumViewerProvider); final descriptionTextEditController = useTextEditingController( - text: albumViewerState.isEditAlbum && - albumViewerState.editDescriptionText.isNotEmpty + text: albumViewerState.isEditAlbum && albumViewerState.editDescriptionText.isNotEmpty ? albumViewerState.editDescriptionText : albumDescription, ); void onFocusModeChange() { - if (!descriptionFocusNode.hasFocus && - descriptionTextEditController.text.isEmpty) { + if (!descriptionFocusNode.hasFocus && descriptionTextEditController.text.isEmpty) { ref.watch(albumViewerProvider.notifier).setEditDescriptionText(""); descriptionTextEditController.text = ""; } } - useEffect( - () { - descriptionFocusNode.addListener(onFocusModeChange); - return () { - descriptionFocusNode.removeListener(onFocusModeChange); - }; - }, - [], - ); + useEffect(() { + descriptionFocusNode.addListener(onFocusModeChange); + return () { + descriptionFocusNode.removeListener(onFocusModeChange); + }; + }, []); return Material( color: Colors.transparent, @@ -49,9 +40,7 @@ class AlbumViewerEditableDescription extends HookConsumerWidget { onChanged: (value) { if (value.isEmpty) { } else { - ref - .watch(albumViewerProvider.notifier) - .setEditDescriptionText(value); + ref.watch(albumViewerProvider.notifier).setEditDescriptionText(value); } }, focusNode: descriptionFocusNode, @@ -62,9 +51,7 @@ class AlbumViewerEditableDescription extends HookConsumerWidget { onTap: () { context.focusScope.requestFocus(descriptionFocusNode); - ref - .watch(albumViewerProvider.notifier) - .setEditDescriptionText(albumDescription); + ref.watch(albumViewerProvider.notifier).setEditDescriptionText(albumDescription); ref.watch(albumViewerProvider.notifier).enableEditAlbum(); if (descriptionTextEditController.text == '') { @@ -78,19 +65,12 @@ class AlbumViewerEditableDescription extends HookConsumerWidget { onPressed: () { descriptionTextEditController.clear(); }, - icon: Icon( - Icons.cancel_rounded, - color: context.primaryColor, - ), + icon: Icon(Icons.cancel_rounded, color: context.primaryColor), splashRadius: 10, ) : null, - enabledBorder: const OutlineInputBorder( - borderSide: BorderSide(color: Colors.transparent), - ), - focusedBorder: const OutlineInputBorder( - borderSide: BorderSide(color: Colors.transparent), - ), + enabledBorder: const OutlineInputBorder(borderSide: BorderSide(color: Colors.transparent)), + focusedBorder: const OutlineInputBorder(borderSide: BorderSide(color: Colors.transparent)), focusColor: Colors.grey[300], fillColor: context.scaffoldBackgroundColor, filled: descriptionFocusNode.hasFocus, diff --git a/mobile/lib/widgets/album/album_viewer_editable_title.dart b/mobile/lib/widgets/album/album_viewer_editable_title.dart index 038c9a13d8..c84e613017 100644 --- a/mobile/lib/widgets/album/album_viewer_editable_title.dart +++ b/mobile/lib/widgets/album/album_viewer_editable_title.dart @@ -8,19 +8,14 @@ import 'package:immich_mobile/providers/album/album_viewer.provider.dart'; class AlbumViewerEditableTitle extends HookConsumerWidget { final String albumName; final FocusNode titleFocusNode; - const AlbumViewerEditableTitle({ - super.key, - required this.albumName, - required this.titleFocusNode, - }); + const AlbumViewerEditableTitle({super.key, required this.albumName, required this.titleFocusNode}); @override Widget build(BuildContext context, WidgetRef ref) { final albumViewerState = ref.watch(albumViewerProvider); final titleTextEditController = useTextEditingController( - text: albumViewerState.isEditAlbum && - albumViewerState.editTitleText.isNotEmpty + text: albumViewerState.isEditAlbum && albumViewerState.editTitleText.isNotEmpty ? albumViewerState.editTitleText : albumName, ); @@ -32,15 +27,12 @@ class AlbumViewerEditableTitle extends HookConsumerWidget { } } - useEffect( - () { - titleFocusNode.addListener(onFocusModeChange); - return () { - titleFocusNode.removeListener(onFocusModeChange); - }; - }, - [], - ); + useEffect(() { + titleFocusNode.addListener(onFocusModeChange); + return () { + titleFocusNode.removeListener(onFocusModeChange); + }; + }, []); return Material( color: Colors.transparent, @@ -52,9 +44,7 @@ class AlbumViewerEditableTitle extends HookConsumerWidget { } }, focusNode: titleFocusNode, - style: context.textTheme.headlineLarge?.copyWith( - fontWeight: FontWeight.w700, - ), + style: context.textTheme.headlineLarge?.copyWith(fontWeight: FontWeight.w700), controller: titleTextEditController, onTap: () { context.focusScope.requestFocus(titleFocusNode); @@ -67,35 +57,23 @@ class AlbumViewerEditableTitle extends HookConsumerWidget { } }, decoration: InputDecoration( - contentPadding: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 0, - ), + contentPadding: const EdgeInsets.symmetric(horizontal: 8, vertical: 0), suffixIcon: titleFocusNode.hasFocus ? IconButton( onPressed: () { titleTextEditController.clear(); }, - icon: Icon( - Icons.cancel_rounded, - color: context.primaryColor, - ), + icon: Icon(Icons.cancel_rounded, color: context.primaryColor), splashRadius: 10, ) : null, - enabledBorder: const OutlineInputBorder( - borderSide: BorderSide(color: Colors.transparent), - ), - focusedBorder: const OutlineInputBorder( - borderSide: BorderSide(color: Colors.transparent), - ), + enabledBorder: const OutlineInputBorder(borderSide: BorderSide(color: Colors.transparent)), + focusedBorder: const OutlineInputBorder(borderSide: BorderSide(color: Colors.transparent)), focusColor: Colors.grey[300], fillColor: context.scaffoldBackgroundColor, filled: titleFocusNode.hasFocus, hintText: 'add_a_title'.tr(), - hintStyle: context.themeData.inputDecorationTheme.hintStyle?.copyWith( - fontSize: 28, - ), + hintStyle: context.themeData.inputDecorationTheme.hintStyle?.copyWith(fontSize: 28), ), ), ); diff --git a/mobile/lib/widgets/album/remote_album_shared_user_icons.dart b/mobile/lib/widgets/album/remote_album_shared_user_icons.dart index dd1a64abe0..7be5db1798 100644 --- a/mobile/lib/widgets/album/remote_album_shared_user_icons.dart +++ b/mobile/lib/widgets/album/remote_album_shared_user_icons.dart @@ -5,9 +5,7 @@ import 'package:immich_mobile/providers/infrastructure/remote_album.provider.dar import 'package:immich_mobile/widgets/common/user_circle_avatar.dart'; class RemoteAlbumSharedUserIcons extends ConsumerWidget { - const RemoteAlbumSharedUserIcons({ - super.key, - }); + const RemoteAlbumSharedUserIcons({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -16,8 +14,7 @@ class RemoteAlbumSharedUserIcons extends ConsumerWidget { return const SizedBox(); } - final sharedUsersAsync = - ref.watch(remoteAlbumSharedUsersProvider(currentAlbum.id)); + final sharedUsersAsync = ref.watch(remoteAlbumSharedUsersProvider(currentAlbum.id)); return sharedUsersAsync.maybeWhen( data: (sharedUsers) { @@ -32,12 +29,7 @@ class RemoteAlbumSharedUserIcons extends ConsumerWidget { itemBuilder: ((context, index) { return Padding( padding: const EdgeInsets.only(right: 4.0), - child: UserCircleAvatar( - user: sharedUsers[index], - radius: 18, - size: 36, - hasBorder: true, - ), + child: UserCircleAvatar(user: sharedUsers[index], radius: 18, size: 36, hasBorder: true), ); }), itemCount: sharedUsers.length, diff --git a/mobile/lib/widgets/album/shared_album_thumbnail_image.dart b/mobile/lib/widgets/album/shared_album_thumbnail_image.dart index e485763114..b21e86d145 100644 --- a/mobile/lib/widgets/album/shared_album_thumbnail_image.dart +++ b/mobile/lib/widgets/album/shared_album_thumbnail_image.dart @@ -14,15 +14,7 @@ class SharedAlbumThumbnailImage extends HookConsumerWidget { onTap: () { // debugPrint("View ${asset.id}"); }, - child: Stack( - children: [ - ImmichThumbnail( - asset: asset, - width: 500, - height: 500, - ), - ], - ), + child: Stack(children: [ImmichThumbnail(asset: asset, width: 500, height: 500)]), ); } } diff --git a/mobile/lib/widgets/asset_grid/asset_drag_region.dart b/mobile/lib/widgets/asset_grid/asset_drag_region.dart index 6335a1d64d..71e55acbd6 100644 --- a/mobile/lib/widgets/asset_grid/asset_drag_region.dart +++ b/mobile/lib/widgets/asset_grid/asset_drag_region.dart @@ -65,8 +65,7 @@ class _AssetDragRegionState extends State { Widget build(BuildContext context) { return RawGestureDetector( gestures: { - _CustomLongPressGestureRecognizer: GestureRecognizerFactoryWithHandlers< - _CustomLongPressGestureRecognizer>( + _CustomLongPressGestureRecognizer: GestureRecognizerFactoryWithHandlers<_CustomLongPressGestureRecognizer>( () => _CustomLongPressGestureRecognizer(), _registerCallbacks, ), @@ -89,9 +88,7 @@ class _AssetDragRegionState extends State { final local = box.globalToLocal(position); if (!box.hitTest(hitTestResult, position: local)) return null; - return (hitTestResult.path - .firstWhereOrNull((hit) => hit.target is _AssetIndexProxy) - ?.target as _AssetIndexProxy?) + return (hitTestResult.path.firstWhereOrNull((hit) => hit.target is _AssetIndexProxy)?.target as _AssetIndexProxy?) ?.index; } @@ -99,8 +96,7 @@ class _AssetDragRegionState extends State { /// Calculate widget height and scroll offset when long press starting instead of in [initState] /// or [didChangeDependencies] as the grid might still be rendering into view to get the actual size final height = context.size?.height; - if (height != null && - (topScrollOffset == null || bottomScrollOffset == null)) { + if (height != null && (topScrollOffset == null || bottomScrollOffset == null)) { topScrollOffset = height * scrollOffset; bottomScrollOffset = height - topScrollOffset!; } @@ -167,12 +163,7 @@ class AssetIndexWrapper extends SingleChildRenderObjectWidget { final int rowIndex; final int sectionIndex; - const AssetIndexWrapper({ - required Widget super.child, - required this.rowIndex, - required this.sectionIndex, - super.key, - }); + const AssetIndexWrapper({required Widget super.child, required this.rowIndex, required this.sectionIndex, super.key}); @override // ignore: library_private_types_in_public_api @@ -188,27 +179,21 @@ class AssetIndexWrapper extends SingleChildRenderObjectWidget { // ignore: library_private_types_in_public_api _AssetIndexProxy renderObject, ) { - renderObject.index = - AssetIndex(rowIndex: rowIndex, sectionIndex: sectionIndex); + renderObject.index = AssetIndex(rowIndex: rowIndex, sectionIndex: sectionIndex); } } class _AssetIndexProxy extends RenderProxyBox { AssetIndex index; - _AssetIndexProxy({ - required this.index, - }); + _AssetIndexProxy({required this.index}); } class AssetIndex { final int rowIndex; final int sectionIndex; - const AssetIndex({ - required this.rowIndex, - required this.sectionIndex, - }); + const AssetIndex({required this.rowIndex, required this.sectionIndex}); @override bool operator ==(covariant AssetIndex other) { diff --git a/mobile/lib/widgets/asset_grid/asset_grid_data_structure.dart b/mobile/lib/widgets/asset_grid/asset_grid_data_structure.dart index 6b94c04c4d..d95d6efe2e 100644 --- a/mobile/lib/widgets/asset_grid/asset_grid_data_structure.dart +++ b/mobile/lib/widgets/asset_grid/asset_grid_data_structure.dart @@ -8,12 +8,7 @@ import 'package:logging/logging.dart'; final log = Logger('AssetGridDataStructure'); -enum RenderAssetGridElementType { - assets, - assetRow, - groupDividerTitle, - monthTitle; -} +enum RenderAssetGridElementType { assets, assetRow, groupDividerTitle, monthTitle } class RenderAssetGridElement { final RenderAssetGridElementType type; @@ -33,13 +28,7 @@ class RenderAssetGridElement { }); } -enum GroupAssetsBy { - day, - month, - auto, - none, - ; -} +enum GroupAssetsBy { day, month, auto, none } class RenderList { final List elements; @@ -53,8 +42,7 @@ class RenderList { /// global offset of assets in [_buf] int _bufOffset = 0; - RenderList(this.elements, this.query, this.allAssets) - : totalAssets = allAssets?.length ?? query!.countSync(); + RenderList(this.elements, this.query, this.allAssets) : totalAssets = allAssets?.length ?? query!.countSync(); bool get isEmpty => totalAssets == 0; @@ -88,12 +76,7 @@ class RenderList { // when scrolling backward, end shortly after the requested offset... // ... to guard against the user scrolling in the other direction // a tiny bit resulting in a another required load from the DB - final start = max( - 0, - forward - ? offset - oppositeSize - : (len > batchSize ? offset : offset + count - len), - ); + final start = max(0, forward ? offset - oppositeSize : (len > batchSize ? offset : offset + count - len)); // load the calculated batch (start:start+len) from the DB and put it into the buffer _buf = query!.offset(start).limit(len).findAllSync(); _bufOffset = start; @@ -120,19 +103,14 @@ class RenderList { // request the asset from the database (not changing the buffer!) final asset = query!.offset(index).findFirstSync(); if (asset == null) { - throw Exception( - "Asset at index $index does no longer exist in database", - ); + throw Exception("Asset at index $index does no longer exist in database"); } return asset; } throw Exception("RenderList has neither assets nor query"); } - static Future fromQuery( - QueryBuilder query, - GroupAssetsBy groupBy, - ) => + static Future fromQuery(QueryBuilder query, GroupAssetsBy groupBy) => _buildRenderList(null, query, groupBy); static Future _buildRenderList( @@ -148,17 +126,10 @@ class RenderList { if (groupBy == GroupAssetsBy.none) { final int total = assets?.length ?? query!.countSync(); - final dateLoader = query != null - ? DateBatchLoader( - query: query, - batchSize: 1000 * sectionSize, - ) - : null; + final dateLoader = query != null ? DateBatchLoader(query: query, batchSize: 1000 * sectionSize) : null; for (int i = 0; i < total; i += sectionSize) { - final date = assets != null - ? assets[i].fileCreatedAt - : await dateLoader?.getDate(i); + final date = assets != null ? assets[i].fileCreatedAt : await dateLoader?.getDate(i); final int count = i + sectionSize > total ? total - i : sectionSize; if (date == null) break; @@ -175,11 +146,8 @@ class RenderList { return RenderList(elements, query, assets); } - final formatSameYear = - groupBy == GroupAssetsBy.month ? DateFormat.MMMM() : DateFormat.MMMEd(); - final formatOtherYear = groupBy == GroupAssetsBy.month - ? DateFormat.yMMMM() - : DateFormat.yMMMEd(); + final formatSameYear = groupBy == GroupAssetsBy.month ? DateFormat.MMMM() : DateFormat.MMMEd(); + final formatOtherYear = groupBy == GroupAssetsBy.month ? DateFormat.yMMMM() : DateFormat.yMMMEd(); final currentYear = DateTime.now().year; final formatMergedSameYear = DateFormat.MMMd(); final formatMergedOtherYear = DateFormat.yMMMd(); @@ -193,16 +161,9 @@ class RenderList { int lastMonthIndex = 0; String formatDateRange(DateTime from, DateTime to) { - final startDate = (from.year == currentYear - ? formatMergedSameYear - : formatMergedOtherYear) - .format(from); - final endDate = (to.year == currentYear - ? formatMergedSameYear - : formatMergedOtherYear) - .format(to); - if (DateTime(from.year, from.month, from.day) == - DateTime(to.year, to.month, to.day)) { + final startDate = (from.year == currentYear ? formatMergedSameYear : formatMergedOtherYear).format(from); + final endDate = (to.year == currentYear ? formatMergedSameYear : formatMergedOtherYear).format(to); + if (DateTime(from.year, from.month, from.day) == DateTime(to.year, to.month, to.day)) { // format range with time when both dates are on the same day final startTime = DateFormat.Hm().format(from); final endTime = DateFormat.Hm().format(to); @@ -212,10 +173,7 @@ class RenderList { } void mergeMonth() { - if (last != null && - groupBy == GroupAssetsBy.auto && - monthCount <= 30 && - elements.length > lastMonthIndex + 1) { + if (last != null && groupBy == GroupAssetsBy.auto && monthCount <= 30 && elements.length > lastMonthIndex + 1) { // merge all days into a single section assert(elements[lastMonthIndex].date.month == last.month); final e = elements[lastMonthIndex]; @@ -233,8 +191,7 @@ class RenderList { } void addElems(DateTime d, DateTime? prevDate) { - final bool newMonth = - last == null || last.year != d.year || last.month != d.month; + final bool newMonth = last == null || last.year != d.year || last.month != d.month; if (newMonth) { mergeMonth(); lastMonthIndex = elements.length; @@ -243,11 +200,11 @@ class RenderList { for (int j = 0; j < count; j += sectionSize) { final type = j == 0 ? (groupBy != GroupAssetsBy.month && newMonth - ? RenderAssetGridElementType.monthTitle - : RenderAssetGridElementType.groupDividerTitle) + ? RenderAssetGridElementType.monthTitle + : RenderAssetGridElementType.groupDividerTitle) : (groupBy == GroupAssetsBy.auto - ? RenderAssetGridElementType.groupDividerTitle - : RenderAssetGridElementType.assets); + ? RenderAssetGridElementType.groupDividerTitle + : RenderAssetGridElementType.assets); final sectionCount = j + sectionSize > count ? count - j : sectionSize; assert(sectionCount > 0 && sectionCount <= sectionSize); elements.add( @@ -258,12 +215,8 @@ class RenderList { totalCount: groupBy == GroupAssetsBy.auto ? sectionCount : count, offset: lastOffset + j, title: j == 0 - ? (d.year == currentYear - ? formatSameYear.format(d) - : formatOtherYear.format(d)) - : (groupBy == GroupAssetsBy.auto - ? formatDateRange(d, prevDate ?? d) - : null), + ? (d.year == currentYear ? formatSameYear.format(d) : formatOtherYear.format(d)) + : (groupBy == GroupAssetsBy.auto ? formatDateRange(d, prevDate ?? d) : null), ), ); } @@ -277,18 +230,10 @@ class RenderList { // TODO replace with groupBy once Isar supports such queries final dates = assets != null ? assets.map((a) => a.fileCreatedAt) - : await query! - .offset(offset) - .limit(pageSize) - .fileCreatedAtProperty() - .findAll(); + : await query!.offset(offset).limit(pageSize).fileCreatedAtProperty().findAll(); int i = 0; for (final date in dates) { - final d = DateTime( - date.year, - date.month, - groupBy == GroupAssetsBy.month ? 1 : date.day, - ); + final d = DateTime(date.year, date.month, groupBy == GroupAssetsBy.month ? 1 : date.day); current ??= d; if (current != d) { addElems(current, prevDate); @@ -315,10 +260,7 @@ class RenderList { static RenderList empty() => RenderList([], null, []); - static Future fromAssets( - List assets, - GroupAssetsBy groupBy, - ) => + static Future fromAssets(List assets, GroupAssetsBy groupBy) => _buildRenderList(assets, null, groupBy); /// Deletes an asset from the render list and clears the buffer @@ -337,10 +279,7 @@ class DateBatchLoader { List _buffer = []; int _bufferStart = 0; - DateBatchLoader({ - required this.query, - required this.batchSize, - }); + DateBatchLoader({required this.query, required this.batchSize}); Future getDate(int index) async { if (!_isIndexInBuffer(index)) { @@ -357,11 +296,7 @@ class DateBatchLoader { Future _loadBatch(int targetIndex) async { final batchStart = (targetIndex ~/ batchSize) * batchSize; - _buffer = await query - .offset(batchStart) - .limit(batchSize) - .fileCreatedAtProperty() - .findAll(); + _buffer = await query.offset(batchStart).limit(batchSize).fileCreatedAtProperty().findAll(); _bufferStart = batchStart; } diff --git a/mobile/lib/widgets/asset_grid/control_bottom_app_bar.dart b/mobile/lib/widgets/asset_grid/control_bottom_app_bar.dart index 3283b90b21..b1e54af62a 100644 --- a/mobile/lib/widgets/asset_grid/control_bottom_app_bar.dart +++ b/mobile/lib/widgets/asset_grid/control_bottom_app_bar.dart @@ -71,57 +71,36 @@ class ControlBottomAppBar extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final hasRemote = - selectionAssetState.hasRemote || selectionAssetState.hasMerged; - final hasLocal = - selectionAssetState.hasLocal || selectionAssetState.hasMerged; - final trashEnabled = - ref.watch(serverInfoProvider.select((v) => v.serverFeatures.trash)); + final hasRemote = selectionAssetState.hasRemote || selectionAssetState.hasMerged; + final hasLocal = selectionAssetState.hasLocal || selectionAssetState.hasMerged; + final trashEnabled = ref.watch(serverInfoProvider.select((v) => v.serverFeatures.trash)); final albums = ref.watch(albumProvider).where((a) => a.isRemote).toList(); - final sharedAlbums = - ref.watch(albumProvider).where((a) => a.shared).toList(); + final sharedAlbums = ref.watch(albumProvider).where((a) => a.shared).toList(); const bottomPadding = 0.24; final scrollController = useDraggableScrollController(); final isInLockedView = ref.watch(inLockedViewProvider); void minimize() { - scrollController.animateTo( - bottomPadding, - duration: const Duration(milliseconds: 300), - curve: Curves.easeOut, - ); + scrollController.animateTo(bottomPadding, duration: const Duration(milliseconds: 300), curve: Curves.easeOut); } - useEffect( - () { - controlBottomAppBarNotifier.addListener(minimize); - return () { - controlBottomAppBarNotifier.removeListener(minimize); - }; - }, - [], - ); + useEffect(() { + controlBottomAppBarNotifier.addListener(minimize); + return () { + controlBottomAppBarNotifier.removeListener(minimize); + }; + }, []); - void showForceDeleteDialog( - Function(bool) deleteCb, { - String? alertMsg, - }) { + void showForceDeleteDialog(Function(bool) deleteCb, {String? alertMsg}) { showDialog( context: context, builder: (BuildContext context) { - return DeleteDialog( - alert: alertMsg, - onDelete: () => deleteCb(true), - ); + return DeleteDialog(alert: alertMsg, onDelete: () => deleteCb(true)); }, ); } - void handleRemoteDelete( - bool force, - Function(bool) deleteCb, { - String? alertMsg, - }) { + void handleRemoteDelete(bool force, Function(bool) deleteCb, {String? alertMsg}) { if (!force) { deleteCb(force); return; @@ -132,9 +111,7 @@ class ControlBottomAppBar extends HookConsumerWidget { List renderActionButtons() { return [ ControlBoxButton( - iconData: Platform.isAndroid - ? Icons.share_rounded - : Icons.ios_share_rounded, + iconData: Platform.isAndroid ? Icons.share_rounded : Icons.ios_share_rounded, label: "share".tr(), onPressed: enabled ? () => onShare(true) : null, ), @@ -146,27 +123,20 @@ class ControlBottomAppBar extends HookConsumerWidget { ), if (hasRemote && onArchive != null) ControlBoxButton( - iconData: - unarchive ? Icons.unarchive_outlined : Icons.archive_outlined, + iconData: unarchive ? Icons.unarchive_outlined : Icons.archive_outlined, label: (unarchive ? "unarchive" : "archive").tr(), onPressed: enabled ? onArchive : null, ), if (hasRemote && onFavorite != null) ControlBoxButton( - iconData: unfavorite - ? Icons.favorite_border_rounded - : Icons.favorite_rounded, + iconData: unfavorite ? Icons.favorite_border_rounded : Icons.favorite_rounded, label: (unfavorite ? "unfavorite" : "favorite").tr(), onPressed: enabled ? onFavorite : null, ), if (hasRemote && onDownload != null) ConstrainedBox( constraints: const BoxConstraints(maxWidth: 90), - child: ControlBoxButton( - iconData: Icons.download, - label: "download".tr(), - onPressed: onDownload, - ), + child: ControlBoxButton(iconData: Icons.download, label: "download".tr(), onPressed: onDownload), ), if (hasLocal && hasRemote && onDelete != null && !isInLockedView) ConstrainedBox( @@ -174,11 +144,8 @@ class ControlBottomAppBar extends HookConsumerWidget { child: ControlBoxButton( iconData: Icons.delete_sweep_outlined, label: "delete".tr(), - onPressed: enabled - ? () => handleRemoteDelete(!trashEnabled, onDelete!) - : null, - onLongPressed: - enabled ? () => showForceDeleteDialog(onDelete!) : null, + onPressed: enabled ? () => handleRemoteDelete(!trashEnabled, onDelete!) : null, + onLongPressed: enabled ? () => showForceDeleteDialog(onDelete!) : null, ), ), if (hasRemote && onDeleteServer != null && !isInLockedView) @@ -190,17 +157,10 @@ class ControlBottomAppBar extends HookConsumerWidget { ? "control_bottom_app_bar_trash_from_immich".tr() : "control_bottom_app_bar_delete_from_immich".tr(), onPressed: enabled - ? () => handleRemoteDelete( - !trashEnabled, - onDeleteServer!, - alertMsg: "delete_dialog_alert_remote", - ) + ? () => handleRemoteDelete(!trashEnabled, onDeleteServer!, alertMsg: "delete_dialog_alert_remote") : null, onLongPressed: enabled - ? () => showForceDeleteDialog( - onDeleteServer!, - alertMsg: "delete_dialog_alert_remote", - ) + ? () => showForceDeleteDialog(onDeleteServer!, alertMsg: "delete_dialog_alert_remote") : null, ), ), @@ -211,10 +171,7 @@ class ControlBottomAppBar extends HookConsumerWidget { iconData: Icons.delete_forever, label: "delete_dialog_title".tr(), onPressed: enabled - ? () => showForceDeleteDialog( - onDeleteServer!, - alertMsg: "delete_dialog_alert_remote", - ) + ? () => showForceDeleteDialog(onDeleteServer!, alertMsg: "delete_dialog_alert_remote") : null, ), ), @@ -233,9 +190,7 @@ class ControlBottomAppBar extends HookConsumerWidget { showDialog( context: context, builder: (BuildContext context) { - return DeleteLocalOnlyDialog( - onDeleteLocal: onDeleteLocal!, - ); + return DeleteLocalOnlyDialog(onDeleteLocal: onDeleteLocal!); }, ); } @@ -264,18 +219,12 @@ class ControlBottomAppBar extends HookConsumerWidget { ConstrainedBox( constraints: const BoxConstraints(maxWidth: 100), child: ControlBoxButton( - iconData: isInLockedView - ? Icons.lock_open_rounded - : Icons.lock_outline_rounded, - label: isInLockedView - ? "remove_from_locked_folder".tr() - : "move_to_locked_folder".tr(), + iconData: isInLockedView ? Icons.lock_open_rounded : Icons.lock_outline_rounded, + label: isInLockedView ? "remove_from_locked_folder".tr() : "move_to_locked_folder".tr(), onPressed: enabled ? onToggleLocked : null, ), ), - if (!selectionAssetState.hasLocal && - selectionAssetState.selectedCount > 1 && - onStack != null) + if (!selectionAssetState.hasLocal && selectionAssetState.selectedCount > 1 && onStack != null) ConstrainedBox( constraints: const BoxConstraints(maxWidth: 90), child: ControlBoxButton( @@ -299,13 +248,11 @@ class ControlBottomAppBar extends HookConsumerWidget { label: "upload".tr(), onPressed: enabled ? () => showDialog( - context: context, - builder: (BuildContext context) { - return UploadDialog( - onUpload: onUpload, - ); - }, - ) + context: context, + builder: (BuildContext context) { + return UploadDialog(onUpload: onUpload); + }, + ) : null, ), ]; @@ -343,10 +290,7 @@ class ControlBottomAppBar extends HookConsumerWidget { surfaceTintColor: context.colorScheme.surfaceContainerHigh, elevation: 6.0, shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(12), - topRight: Radius.circular(12), - ), + borderRadius: BorderRadius.only(topLeft: Radius.circular(12), topRight: Radius.circular(12)), ), margin: const EdgeInsets.all(0), child: CustomScrollView( @@ -367,14 +311,8 @@ class ControlBottomAppBar extends HookConsumerWidget { ), ), if (hasRemote && !isInLockedView) ...[ - const Divider( - indent: 16, - endIndent: 16, - thickness: 1, - ), - _AddToAlbumTitleRow( - onCreateNewAlbum: enabled ? onCreateNewAlbum : null, - ), + const Divider(indent: 16, endIndent: 16, thickness: 1), + _AddToAlbumTitleRow(onCreateNewAlbum: enabled ? onCreateNewAlbum : null), ], ], ), @@ -398,9 +336,7 @@ class ControlBottomAppBar extends HookConsumerWidget { } class _AddToAlbumTitleRow extends StatelessWidget { - const _AddToAlbumTitleRow({ - required this.onCreateNewAlbum, - }); + const _AddToAlbumTitleRow({required this.onCreateNewAlbum}); final VoidCallback? onCreateNewAlbum; @@ -411,23 +347,13 @@ class _AddToAlbumTitleRow extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - "add_to_album", - style: context.textTheme.titleSmall, - ).tr(), + Text("add_to_album", style: context.textTheme.titleSmall).tr(), TextButton.icon( onPressed: onCreateNewAlbum, - icon: Icon( - Icons.add, - color: context.primaryColor, - ), + icon: Icon(Icons.add, color: context.primaryColor), label: Text( "common_create_new_album", - style: TextStyle( - color: context.primaryColor, - fontWeight: FontWeight.bold, - fontSize: 14, - ), + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold, fontSize: 14), ).tr(), ), ], diff --git a/mobile/lib/widgets/asset_grid/delete_dialog.dart b/mobile/lib/widgets/asset_grid/delete_dialog.dart index ecfb4130dc..e7c7775e54 100644 --- a/mobile/lib/widgets/asset_grid/delete_dialog.dart +++ b/mobile/lib/widgets/asset_grid/delete_dialog.dart @@ -5,22 +5,19 @@ import 'package:immich_mobile/widgets/common/confirm_dialog.dart'; class DeleteDialog extends ConfirmDialog { const DeleteDialog({super.key, String? alert, required Function onDelete}) - : super( - title: "delete_dialog_title", - content: alert ?? "delete_dialog_alert", - cancel: "cancel", - ok: "delete", - onOk: onDelete, - ); + : super( + title: "delete_dialog_title", + content: alert ?? "delete_dialog_alert", + cancel: "cancel", + ok: "delete", + onOk: onDelete, + ); } class DeleteLocalOnlyDialog extends StatelessWidget { final void Function(bool onlyMerged) onDeleteLocal; - const DeleteLocalOnlyDialog({ - super.key, - required this.onDeleteLocal, - }); + const DeleteLocalOnlyDialog({super.key, required this.onDeleteLocal}); @override Widget build(BuildContext context) { @@ -35,9 +32,7 @@ class DeleteLocalOnlyDialog extends StatelessWidget { } return AlertDialog( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))), title: const Text("delete_dialog_title").tr(), content: const Text("delete_dialog_alert_local_non_backed_up").tr(), actions: [ @@ -45,30 +40,21 @@ class DeleteLocalOnlyDialog extends StatelessWidget { onPressed: () => context.pop(), child: Text( "cancel", - style: TextStyle( - color: context.primaryColor, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold), ).tr(), ), TextButton( onPressed: onDeleteBackedUpOnly, child: Text( "delete_local_dialog_ok_backed_up_only", - style: TextStyle( - color: context.colorScheme.tertiary, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: context.colorScheme.tertiary, fontWeight: FontWeight.bold), ).tr(), ), TextButton( onPressed: onForceDelete, child: Text( "delete_local_dialog_ok_force", - style: TextStyle( - color: Colors.red[400], - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: Colors.red[400], fontWeight: FontWeight.bold), ).tr(), ), ], diff --git a/mobile/lib/widgets/asset_grid/disable_multi_select_button.dart b/mobile/lib/widgets/asset_grid/disable_multi_select_button.dart index 50b38c2a4a..93a1d53f4e 100644 --- a/mobile/lib/widgets/asset_grid/disable_multi_select_button.dart +++ b/mobile/lib/widgets/asset_grid/disable_multi_select_button.dart @@ -3,11 +3,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; class DisableMultiSelectButton extends ConsumerWidget { - const DisableMultiSelectButton({ - super.key, - required this.onPressed, - required this.selectedItemCount, - }); + const DisableMultiSelectButton({super.key, required this.onPressed, required this.selectedItemCount}); final Function onPressed; final int selectedItemCount; @@ -22,16 +18,10 @@ class DisableMultiSelectButton extends ConsumerWidget { padding: const EdgeInsets.symmetric(horizontal: 4.0), child: ElevatedButton.icon( onPressed: () => onPressed(), - icon: Icon( - Icons.close_rounded, - color: context.colorScheme.onPrimary, - ), + icon: Icon(Icons.close_rounded, color: context.colorScheme.onPrimary), label: Text( '$selectedItemCount', - style: context.textTheme.titleMedium?.copyWith( - height: 2.5, - color: context.colorScheme.onPrimary, - ), + style: context.textTheme.titleMedium?.copyWith(height: 2.5, color: context.colorScheme.onPrimary), ), ), ), diff --git a/mobile/lib/widgets/asset_grid/draggable_scrollbar.dart b/mobile/lib/widgets/asset_grid/draggable_scrollbar.dart index 779da8add7..3de52c2816 100644 --- a/mobile/lib/widgets/asset_grid/draggable_scrollbar.dart +++ b/mobile/lib/widgets/asset_grid/draggable_scrollbar.dart @@ -3,14 +3,15 @@ import 'dart:async'; import 'package:flutter/material.dart'; /// Build the Scroll Thumb and label using the current configuration -typedef ScrollThumbBuilder = Widget Function( - Color backgroundColor, - Animation thumbAnimation, - Animation labelAnimation, - double height, { - Text? labelText, - BoxConstraints? labelConstraints, -}); +typedef ScrollThumbBuilder = + Widget Function( + Color backgroundColor, + Animation thumbAnimation, + Animation labelAnimation, + double height, { + Text? labelText, + BoxConstraints? labelConstraints, + }); /// Build a Text widget using the current scroll offset typedef LabelTextBuilder = Text Function(double offsetY); @@ -79,8 +80,8 @@ class DraggableScrollbar extends StatefulWidget { this.scrollbarTimeToFade = const Duration(milliseconds: 600), this.labelTextBuilder, this.labelConstraints, - }) : assert(child.scrollDirection == Axis.vertical), - scrollThumbBuilder = _thumbRRectBuilder(alwaysVisibleScrollThumb); + }) : assert(child.scrollDirection == Axis.vertical), + scrollThumbBuilder = _thumbRRectBuilder(alwaysVisibleScrollThumb); DraggableScrollbar.arrows({ super.key, @@ -95,8 +96,8 @@ class DraggableScrollbar extends StatefulWidget { this.scrollbarTimeToFade = const Duration(milliseconds: 600), this.labelTextBuilder, this.labelConstraints, - }) : assert(child.scrollDirection == Axis.vertical), - scrollThumbBuilder = _thumbArrowBuilder(alwaysVisibleScrollThumb); + }) : assert(child.scrollDirection == Axis.vertical), + scrollThumbBuilder = _thumbArrowBuilder(alwaysVisibleScrollThumb); DraggableScrollbar.semicircle({ super.key, @@ -111,12 +112,8 @@ class DraggableScrollbar extends StatefulWidget { this.scrollbarTimeToFade = const Duration(milliseconds: 600), this.labelTextBuilder, this.labelConstraints, - }) : assert(child.scrollDirection == Axis.vertical), - scrollThumbBuilder = _thumbSemicircleBuilder( - heightScrollThumb * 0.6, - scrollThumbKey, - alwaysVisibleScrollThumb, - ); + }) : assert(child.scrollDirection == Axis.vertical), + scrollThumbBuilder = _thumbSemicircleBuilder(heightScrollThumb * 0.6, scrollThumbKey, alwaysVisibleScrollThumb); @override DraggableScrollbarState createState() => DraggableScrollbarState(); @@ -149,17 +146,10 @@ class DraggableScrollbar extends StatefulWidget { if (alwaysVisibleScrollThumb) { return scrollThumbAndLabel; } - return SlideFadeTransition( - animation: thumbAnimation!, - child: scrollThumbAndLabel, - ); + return SlideFadeTransition(animation: thumbAnimation!, child: scrollThumbAndLabel); } - static ScrollThumbBuilder _thumbSemicircleBuilder( - double width, - Key? scrollThumbKey, - bool alwaysVisibleScrollThumb, - ) { + static ScrollThumbBuilder _thumbSemicircleBuilder(double width, Key? scrollThumbKey, bool alwaysVisibleScrollThumb) { return ( Color backgroundColor, Animation thumbAnimation, @@ -180,9 +170,7 @@ class DraggableScrollbar extends StatefulWidget { topRight: const Radius.circular(4.0), bottomRight: const Radius.circular(4.0), ), - child: Container( - constraints: BoxConstraints.tight(Size(width, height)), - ), + child: Container(constraints: BoxConstraints.tight(Size(width, height))), ), ); @@ -198,9 +186,7 @@ class DraggableScrollbar extends StatefulWidget { }; } - static ScrollThumbBuilder _thumbArrowBuilder( - bool alwaysVisibleScrollThumb, - ) { + static ScrollThumbBuilder _thumbArrowBuilder(bool alwaysVisibleScrollThumb) { return ( Color backgroundColor, Animation thumbAnimation, @@ -216,9 +202,7 @@ class DraggableScrollbar extends StatefulWidget { width: 20.0, decoration: BoxDecoration( color: backgroundColor, - borderRadius: const BorderRadius.all( - Radius.circular(12.0), - ), + borderRadius: const BorderRadius.all(Radius.circular(12.0)), ), ), ); @@ -235,9 +219,7 @@ class DraggableScrollbar extends StatefulWidget { }; } - static ScrollThumbBuilder _thumbRRectBuilder( - bool alwaysVisibleScrollThumb, - ) { + static ScrollThumbBuilder _thumbRRectBuilder(bool alwaysVisibleScrollThumb) { return ( Color backgroundColor, Animation thumbAnimation, @@ -250,11 +232,7 @@ class DraggableScrollbar extends StatefulWidget { elevation: 4.0, color: backgroundColor, borderRadius: const BorderRadius.all(Radius.circular(7.0)), - child: Container( - constraints: BoxConstraints.tight( - Size(16.0, height), - ), - ), + child: Container(constraints: BoxConstraints.tight(Size(16.0, height))), ); return buildScrollThumbAndLabel( @@ -276,8 +254,7 @@ class ScrollLabel extends StatelessWidget { final Text child; final BoxConstraints? constraints; - static const BoxConstraints _defaultConstraints = - BoxConstraints.tightFor(width: 72.0, height: 28.0); + static const BoxConstraints _defaultConstraints = BoxConstraints.tightFor(width: 72.0, height: 28.0); const ScrollLabel({ super.key, @@ -297,19 +274,14 @@ class ScrollLabel extends StatelessWidget { elevation: 4.0, color: backgroundColor, borderRadius: const BorderRadius.all(Radius.circular(16.0)), - child: Container( - constraints: constraints ?? _defaultConstraints, - alignment: Alignment.center, - child: child, - ), + child: Container(constraints: constraints ?? _defaultConstraints, alignment: Alignment.center, child: child), ), ), ); } } -class DraggableScrollbarState extends State - with TickerProviderStateMixin { +class DraggableScrollbarState extends State with TickerProviderStateMixin { late double _barOffset; late double _viewOffset; late bool _isDragInProcess; @@ -327,25 +299,13 @@ class DraggableScrollbarState extends State _viewOffset = 0.0; _isDragInProcess = false; - _thumbAnimationController = AnimationController( - vsync: this, - duration: widget.scrollbarAnimationDuration, - ); + _thumbAnimationController = AnimationController(vsync: this, duration: widget.scrollbarAnimationDuration); - _thumbAnimation = CurvedAnimation( - parent: _thumbAnimationController, - curve: Curves.fastOutSlowIn, - ); + _thumbAnimation = CurvedAnimation(parent: _thumbAnimationController, curve: Curves.fastOutSlowIn); - _labelAnimationController = AnimationController( - vsync: this, - duration: widget.scrollbarAnimationDuration, - ); + _labelAnimationController = AnimationController(vsync: this, duration: widget.scrollbarAnimationDuration); - _labelAnimation = CurvedAnimation( - parent: _labelAnimationController, - curve: Curves.fastOutSlowIn, - ); + _labelAnimation = CurvedAnimation(parent: _labelAnimationController, curve: Curves.fastOutSlowIn); } @override @@ -356,8 +316,7 @@ class DraggableScrollbarState extends State super.dispose(); } - double get barMaxScrollExtent => - context.size!.height - widget.heightScrollThumb; + double get barMaxScrollExtent => context.size!.height - widget.heightScrollThumb; double get barMinScrollExtent => 0; @@ -369,9 +328,7 @@ class DraggableScrollbarState extends State Widget build(BuildContext context) { Text? labelText; if (widget.labelTextBuilder != null && _isDragInProcess) { - labelText = widget.labelTextBuilder!( - _viewOffset + _barOffset + widget.heightScrollThumb / 2, - ); + labelText = widget.labelTextBuilder!(_viewOffset + _barOffset + widget.heightScrollThumb / 2); } return LayoutBuilder( @@ -385,9 +342,7 @@ class DraggableScrollbarState extends State }, child: Stack( children: [ - RepaintBoundary( - child: widget.child, - ), + RepaintBoundary(child: widget.child), RepaintBoundary( child: GestureDetector( onVerticalDragStart: _onVerticalDragStart, @@ -425,11 +380,7 @@ class DraggableScrollbarState extends State setState(() { if (notification is ScrollUpdateNotification) { - _barOffset += getBarDelta( - notification.scrollDelta!, - barMaxScrollExtent, - viewMaxScrollExtent, - ); + _barOffset += getBarDelta(notification.scrollDelta!, barMaxScrollExtent, viewMaxScrollExtent); if (_barOffset < barMinScrollExtent) { _barOffset = barMinScrollExtent; @@ -447,8 +398,7 @@ class DraggableScrollbarState extends State } } - if (notification is ScrollUpdateNotification || - notification is OverscrollNotification) { + if (notification is ScrollUpdateNotification || notification is OverscrollNotification) { if (_thumbAnimationController.status != AnimationStatus.forward) { _thumbAnimationController.forward(); } @@ -463,19 +413,11 @@ class DraggableScrollbarState extends State }); } - double getBarDelta( - double scrollViewDelta, - double barMaxScrollExtent, - double viewMaxScrollExtent, - ) { + double getBarDelta(double scrollViewDelta, double barMaxScrollExtent, double viewMaxScrollExtent) { return scrollViewDelta * barMaxScrollExtent / viewMaxScrollExtent; } - double getScrollViewDelta( - double barDelta, - double barMaxScrollExtent, - double viewMaxScrollExtent, - ) { + double getScrollViewDelta(double barDelta, double barMaxScrollExtent, double viewMaxScrollExtent) { return barDelta * viewMaxScrollExtent / barMaxScrollExtent; } @@ -502,11 +444,7 @@ class DraggableScrollbarState extends State _barOffset = barMaxScrollExtent; } - double viewDelta = getScrollViewDelta( - details.delta.dy, - barMaxScrollExtent, - viewMaxScrollExtent, - ); + double viewDelta = getScrollViewDelta(details.delta.dy, barMaxScrollExtent, viewMaxScrollExtent); _viewOffset = widget.controller.position.pixels + viewDelta; if (_viewOffset < widget.controller.position.minScrollExtent) { @@ -549,14 +487,8 @@ class ArrowCustomPainter extends CustomPainter { final baseX = size.width / 2; final baseY = size.height / 2; - canvas.drawPath( - _trianglePath(Offset(baseX, baseY - 2.0), width, height, true), - paint, - ); - canvas.drawPath( - _trianglePath(Offset(baseX, baseY + 2.0), width, height, false), - paint, - ); + canvas.drawPath(_trianglePath(Offset(baseX, baseY - 2.0), width, height, true), paint); + canvas.drawPath(_trianglePath(Offset(baseX, baseY + 2.0), width, height, false), paint); } static Path _trianglePath(Offset o, double width, double height, bool isUp) { @@ -587,10 +519,7 @@ class ArrowClipper extends CustomClipper { path.lineTo(startPointX + arrowWidth / 2, startPointY - arrowWidth / 2); path.lineTo(startPointX + arrowWidth, startPointY); path.lineTo(startPointX + arrowWidth, startPointY + 1.0); - path.lineTo( - startPointX + arrowWidth / 2, - startPointY - arrowWidth / 2 + 1.0, - ); + path.lineTo(startPointX + arrowWidth / 2, startPointY - arrowWidth / 2 + 1.0); path.lineTo(startPointX, startPointY + 1.0); path.close(); @@ -599,10 +528,7 @@ class ArrowClipper extends CustomClipper { path.lineTo(startPointX + arrowWidth / 2, startPointY + arrowWidth / 2); path.lineTo(startPointX, startPointY); path.lineTo(startPointX, startPointY - 1.0); - path.lineTo( - startPointX + arrowWidth / 2, - startPointY + arrowWidth / 2 - 1.0, - ); + path.lineTo(startPointX + arrowWidth / 2, startPointY + arrowWidth / 2 - 1.0); path.lineTo(startPointX + arrowWidth, startPointY - 1.0); path.close(); @@ -617,27 +543,16 @@ class SlideFadeTransition extends StatelessWidget { final Animation animation; final Widget child; - const SlideFadeTransition({ - super.key, - required this.animation, - required this.child, - }); + const SlideFadeTransition({super.key, required this.animation, required this.child}); @override Widget build(BuildContext context) { return AnimatedBuilder( animation: animation, - builder: (context, child) => - animation.value == 0.0 ? const SizedBox() : child!, + builder: (context, child) => animation.value == 0.0 ? const SizedBox() : child!, child: SlideTransition( - position: Tween( - begin: const Offset(0.3, 0.0), - end: const Offset(0.0, 0.0), - ).animate(animation), - child: FadeTransition( - opacity: animation, - child: child, - ), + position: Tween(begin: const Offset(0.3, 0.0), end: const Offset(0.0, 0.0)).animate(animation), + child: FadeTransition(opacity: animation, child: child), ), ); } diff --git a/mobile/lib/widgets/asset_grid/draggable_scrollbar_custom.dart b/mobile/lib/widgets/asset_grid/draggable_scrollbar_custom.dart index 06954f4ff4..17f35311f0 100644 --- a/mobile/lib/widgets/asset_grid/draggable_scrollbar_custom.dart +++ b/mobile/lib/widgets/asset_grid/draggable_scrollbar_custom.dart @@ -4,14 +4,15 @@ import 'package:flutter/material.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; /// Build the Scroll Thumb and label using the current configuration -typedef ScrollThumbBuilder = Widget Function( - Color backgroundColor, - Animation thumbAnimation, - Animation labelAnimation, - double height, { - Text? labelText, - BoxConstraints? labelConstraints, -}); +typedef ScrollThumbBuilder = + Widget Function( + Color backgroundColor, + Animation thumbAnimation, + Animation labelAnimation, + double height, { + Text? labelText, + BoxConstraints? labelConstraints, + }); /// Build a Text widget using the current scroll offset typedef LabelTextBuilder = Text Function(int item); @@ -75,12 +76,8 @@ class DraggableScrollbar extends StatefulWidget { this.scrollbarTimeToFade = const Duration(milliseconds: 600), this.labelTextBuilder, this.labelConstraints, - }) : assert(child.scrollDirection == Axis.vertical), - scrollThumbBuilder = _thumbSemicircleBuilder( - heightScrollThumb * 0.6, - scrollThumbKey, - alwaysVisibleScrollThumb, - ); + }) : assert(child.scrollDirection == Axis.vertical), + scrollThumbBuilder = _thumbSemicircleBuilder(heightScrollThumb * 0.6, scrollThumbKey, alwaysVisibleScrollThumb); @override DraggableScrollbarState createState() => DraggableScrollbarState(); @@ -113,17 +110,10 @@ class DraggableScrollbar extends StatefulWidget { if (alwaysVisibleScrollThumb) { return scrollThumbAndLabel; } - return SlideFadeTransition( - animation: thumbAnimation!, - child: scrollThumbAndLabel, - ); + return SlideFadeTransition(animation: thumbAnimation!, child: scrollThumbAndLabel); } - static ScrollThumbBuilder _thumbSemicircleBuilder( - double width, - Key? scrollThumbKey, - bool alwaysVisibleScrollThumb, - ) { + static ScrollThumbBuilder _thumbSemicircleBuilder(double width, Key? scrollThumbKey, bool alwaysVisibleScrollThumb) { return ( Color backgroundColor, Animation thumbAnimation, @@ -144,9 +134,7 @@ class DraggableScrollbar extends StatefulWidget { topRight: const Radius.circular(4.0), bottomRight: const Radius.circular(4.0), ), - child: Container( - constraints: BoxConstraints.tight(Size(width, height)), - ), + child: Container(constraints: BoxConstraints.tight(Size(width, height))), ), ); @@ -169,8 +157,7 @@ class ScrollLabel extends StatelessWidget { final Text child; final BoxConstraints? constraints; - static const BoxConstraints _defaultConstraints = - BoxConstraints.tightFor(width: 72.0, height: 28.0); + static const BoxConstraints _defaultConstraints = BoxConstraints.tightFor(width: 72.0, height: 28.0); const ScrollLabel({ super.key, @@ -202,8 +189,7 @@ class ScrollLabel extends StatelessWidget { } } -class DraggableScrollbarState extends State - with TickerProviderStateMixin { +class DraggableScrollbarState extends State with TickerProviderStateMixin { late double _barOffset; late bool _isDragInProcess; late int _currentItem; @@ -221,25 +207,13 @@ class DraggableScrollbarState extends State _isDragInProcess = false; _currentItem = 0; - _thumbAnimationController = AnimationController( - vsync: this, - duration: widget.scrollbarAnimationDuration, - ); + _thumbAnimationController = AnimationController(vsync: this, duration: widget.scrollbarAnimationDuration); - _thumbAnimation = CurvedAnimation( - parent: _thumbAnimationController, - curve: Curves.fastOutSlowIn, - ); + _thumbAnimation = CurvedAnimation(parent: _thumbAnimationController, curve: Curves.fastOutSlowIn); - _labelAnimationController = AnimationController( - vsync: this, - duration: widget.scrollbarAnimationDuration, - ); + _labelAnimationController = AnimationController(vsync: this, duration: widget.scrollbarAnimationDuration); - _labelAnimation = CurvedAnimation( - parent: _labelAnimationController, - curve: Curves.fastOutSlowIn, - ); + _labelAnimation = CurvedAnimation(parent: _labelAnimationController, curve: Curves.fastOutSlowIn); } @override @@ -250,10 +224,7 @@ class DraggableScrollbarState extends State super.dispose(); } - double get barMaxScrollExtent => - (context.size?.height ?? 0) - - widget.heightScrollThumb - - (widget.heightOffset ?? 0); + double get barMaxScrollExtent => (context.size?.height ?? 0) - widget.heightScrollThumb - (widget.heightOffset ?? 0); double get barMinScrollExtent => 0; @@ -277,9 +248,7 @@ class DraggableScrollbarState extends State }, child: Stack( children: [ - RepaintBoundary( - child: widget.child, - ), + RepaintBoundary(child: widget.child), RepaintBoundary( child: GestureDetector( onVerticalDragStart: _onVerticalDragStart, @@ -317,8 +286,7 @@ class DraggableScrollbarState extends State setState(() { try { - int firstItemIndex = - widget.itemPositionsListener.itemPositions.value.first.index; + int firstItemIndex = widget.itemPositionsListener.itemPositions.value.first.index; if (notification is ScrollUpdateNotification) { _barOffset = (firstItemIndex / maxItemCount) * barMaxScrollExtent; @@ -331,8 +299,7 @@ class DraggableScrollbarState extends State } } - if (notification is ScrollUpdateNotification || - notification is OverscrollNotification) { + if (notification is ScrollUpdateNotification || notification is OverscrollNotification) { if (_thumbAnimationController.status != AnimationStatus.forward) { _thumbAnimationController.forward(); } @@ -377,16 +344,12 @@ class DraggableScrollbarState extends State /// If the bar is at the bottom but the item position is still smaller than the max item count (due to rounding error) /// jump to the end of the list if (barMaxScrollExtent - _barOffset < 10 && itemPosition < maxItemCount) { - widget.controller.jumpTo( - index: maxItemCount, - ); + widget.controller.jumpTo(index: maxItemCount); return; } - widget.controller.jumpTo( - index: itemPosition, - ); + widget.controller.jumpTo(index: itemPosition); } Timer? dragHaltTimer; @@ -412,12 +375,9 @@ class DraggableScrollbarState extends State dragHaltTimer?.cancel(); widget.scrollStateListener(true); - dragHaltTimer = Timer( - const Duration(milliseconds: 500), - () { - widget.scrollStateListener(false); - }, - ); + dragHaltTimer = Timer(const Duration(milliseconds: 500), () { + widget.scrollStateListener(false); + }); } _jumpToBarPosition(); @@ -458,14 +418,8 @@ class ArrowCustomPainter extends CustomPainter { final baseX = size.width / 2; final baseY = size.height / 2; - canvas.drawPath( - _trianglePath(Offset(baseX, baseY - 2.0), width, height, true), - paint, - ); - canvas.drawPath( - _trianglePath(Offset(baseX, baseY + 2.0), width, height, false), - paint, - ); + canvas.drawPath(_trianglePath(Offset(baseX, baseY - 2.0), width, height, true), paint); + canvas.drawPath(_trianglePath(Offset(baseX, baseY + 2.0), width, height, false), paint); } static Path _trianglePath(Offset o, double width, double height, bool isUp) { @@ -496,10 +450,7 @@ class ArrowClipper extends CustomClipper { path.lineTo(startPointX + arrowWidth / 2, startPointY - arrowWidth / 2); path.lineTo(startPointX + arrowWidth, startPointY); path.lineTo(startPointX + arrowWidth, startPointY + 1.0); - path.lineTo( - startPointX + arrowWidth / 2, - startPointY - arrowWidth / 2 + 1.0, - ); + path.lineTo(startPointX + arrowWidth / 2, startPointY - arrowWidth / 2 + 1.0); path.lineTo(startPointX, startPointY + 1.0); path.close(); @@ -508,10 +459,7 @@ class ArrowClipper extends CustomClipper { path.lineTo(startPointX + arrowWidth / 2, startPointY + arrowWidth / 2); path.lineTo(startPointX, startPointY); path.lineTo(startPointX, startPointY - 1.0); - path.lineTo( - startPointX + arrowWidth / 2, - startPointY + arrowWidth / 2 - 1.0, - ); + path.lineTo(startPointX + arrowWidth / 2, startPointY + arrowWidth / 2 - 1.0); path.lineTo(startPointX + arrowWidth, startPointY - 1.0); path.close(); @@ -526,27 +474,16 @@ class SlideFadeTransition extends StatelessWidget { final Animation animation; final Widget child; - const SlideFadeTransition({ - super.key, - required this.animation, - required this.child, - }); + const SlideFadeTransition({super.key, required this.animation, required this.child}); @override Widget build(BuildContext context) { return AnimatedBuilder( animation: animation, - builder: (context, child) => - animation.value == 0.0 ? const SizedBox() : child!, + builder: (context, child) => animation.value == 0.0 ? const SizedBox() : child!, child: SlideTransition( - position: Tween( - begin: const Offset(0.3, 0.0), - end: const Offset(0.0, 0.0), - ).animate(animation), - child: FadeTransition( - opacity: animation, - child: child, - ), + position: Tween(begin: const Offset(0.3, 0.0), end: const Offset(0.0, 0.0)).animate(animation), + child: FadeTransition(opacity: animation, child: child), ), ); } diff --git a/mobile/lib/widgets/asset_grid/group_divider_title.dart b/mobile/lib/widgets/asset_grid/group_divider_title.dart index b9fe8e3c1d..1464c941f0 100644 --- a/mobile/lib/widgets/asset_grid/group_divider_title.dart +++ b/mobile/lib/widgets/asset_grid/group_divider_title.dart @@ -30,14 +30,10 @@ class GroupDividerTitle extends HookConsumerWidget { final appSettingService = ref.watch(appSettingsServiceProvider); final groupBy = useState(GroupAssetsBy.day); - useEffect( - () { - groupBy.value = GroupAssetsBy.values[ - appSettingService.getSetting(AppSettingsEnum.groupAssetsBy)]; - return null; - }, - [], - ); + useEffect(() { + groupBy.value = GroupAssetsBy.values[appSettingService.getSetting(AppSettingsEnum.groupAssetsBy)]; + return null; + }, []); void handleTitleIconClick() { ref.read(hapticFeedbackProvider.notifier).heavyImpact(); @@ -60,9 +56,7 @@ class GroupDividerTitle extends HookConsumerWidget { Text( text, style: groupBy.value == GroupAssetsBy.month - ? context.textTheme.bodyLarge?.copyWith( - fontSize: 24.0, - ) + ? context.textTheme.bodyLarge?.copyWith(fontSize: 24.0) : context.textTheme.labelLarge?.copyWith( color: context.textTheme.labelLarge?.color?.withAlpha(250), fontWeight: FontWeight.w500, @@ -75,14 +69,12 @@ class GroupDividerTitle extends HookConsumerWidget { ? Icon( Icons.check_circle_rounded, color: context.primaryColor, - semanticLabel: - "unselect_all_in".tr(namedArgs: {"group": text}), + semanticLabel: "unselect_all_in".tr(namedArgs: {"group": text}), ) : Icon( Icons.check_circle_outline_rounded, color: context.colorScheme.onSurfaceSecondary, - semanticLabel: - "select_all_in".tr(namedArgs: {"group": text}), + semanticLabel: "select_all_in".tr(namedArgs: {"group": text}), ), ), ], diff --git a/mobile/lib/widgets/asset_grid/immich_asset_grid.dart b/mobile/lib/widgets/asset_grid/immich_asset_grid.dart index da4c47e466..ab6b350a7b 100644 --- a/mobile/lib/widgets/asset_grid/immich_asset_grid.dart +++ b/mobile/lib/widgets/asset_grid/immich_asset_grid.dart @@ -27,8 +27,7 @@ class ImmichAssetGrid extends HookConsumerWidget { final bool canDeselect; final bool? dynamicLayout; final bool showMultiSelectIndicator; - final void Function(Iterable itemPositions)? - visibleItemsListener; + final void Function(Iterable itemPositions)? visibleItemsListener; final Widget? topWidget; final bool shrinkWrap; final bool showDragScroll; @@ -61,9 +60,7 @@ class ImmichAssetGrid extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { var settings = ref.watch(appSettingsServiceProvider); - final perRow = useState( - assetsPerRow ?? settings.getSetting(AppSettingsEnum.tilesPerRow)!, - ); + final perRow = useState(assetsPerRow ?? settings.getSetting(AppSettingsEnum.tilesPerRow)!); final scaleFactor = useState(7.0 - perRow.value); final baseScaleFactor = useState(7.0 - perRow.value); @@ -82,39 +79,34 @@ class ImmichAssetGrid extends HookConsumerWidget { Widget buildAssetGridView(RenderList renderList) { return RawGestureDetector( gestures: { - CustomScaleGestureRecognizer: GestureRecognizerFactoryWithHandlers< - CustomScaleGestureRecognizer>( - () => CustomScaleGestureRecognizer(), - (CustomScaleGestureRecognizer scale) { - scale.onStart = (details) { - baseScaleFactor.value = scaleFactor.value; - }; + CustomScaleGestureRecognizer: GestureRecognizerFactoryWithHandlers( + () => CustomScaleGestureRecognizer(), + (CustomScaleGestureRecognizer scale) { + scale.onStart = (details) { + baseScaleFactor.value = scaleFactor.value; + }; - scale.onUpdate = (details) { - scaleFactor.value = max( - min(5.0, baseScaleFactor.value * details.scale), - 1.0, - ); - if (7 - scaleFactor.value.toInt() != perRow.value) { - perRow.value = 7 - scaleFactor.value.toInt(); - settings.setSetting(AppSettingsEnum.tilesPerRow, perRow.value); - } - }; - }), + scale.onUpdate = (details) { + scaleFactor.value = max(min(5.0, baseScaleFactor.value * details.scale), 1.0); + if (7 - scaleFactor.value.toInt() != perRow.value) { + perRow.value = 7 - scaleFactor.value.toInt(); + settings.setSetting(AppSettingsEnum.tilesPerRow, perRow.value); + } + }; + }, + ), }, child: ImmichAssetGridView( onRefresh: onRefresh, assetsPerRow: perRow.value, listener: listener, - showStorageIndicator: showStorageIndicator ?? - settings.getSetting(AppSettingsEnum.storageIndicator), + showStorageIndicator: showStorageIndicator ?? settings.getSetting(AppSettingsEnum.storageIndicator), renderList: renderList, margin: margin, selectionActive: selectionActive, preselectedAssets: preselectedAssets, canDeselect: canDeselect, - dynamicLayout: dynamicLayout ?? - settings.getSetting(AppSettingsEnum.dynamicLayout), + dynamicLayout: dynamicLayout ?? settings.getSetting(AppSettingsEnum.dynamicLayout), showMultiSelectIndicator: showMultiSelectIndicator, visibleItemsListener: visibleItemsListener, topWidget: topWidget, @@ -130,9 +122,7 @@ class ImmichAssetGrid extends HookConsumerWidget { if (renderList != null) return buildAssetGridView(renderList!); final renderListFuture = ref.watch(assetsTimelineProvider(assets!)); - return renderListFuture.widgetWhen( - onData: (renderList) => buildAssetGridView(renderList), - ); + return renderListFuture.widgetWhen(onData: (renderList) => buildAssetGridView(renderList)); } } diff --git a/mobile/lib/widgets/asset_grid/immich_asset_grid_view.dart b/mobile/lib/widgets/asset_grid/immich_asset_grid_view.dart index 060898e270..7db03a33aa 100644 --- a/mobile/lib/widgets/asset_grid/immich_asset_grid_view.dart +++ b/mobile/lib/widgets/asset_grid/immich_asset_grid_view.dart @@ -34,10 +34,7 @@ import 'disable_multi_select_button.dart'; import 'draggable_scrollbar_custom.dart'; import 'group_divider_title.dart'; -typedef ImmichAssetGridSelectionListener = void Function( - bool, - Set, -); +typedef ImmichAssetGridSelectionListener = void Function(bool, Set); class ImmichAssetGridView extends ConsumerStatefulWidget { final RenderList renderList; @@ -51,8 +48,7 @@ class ImmichAssetGridView extends ConsumerStatefulWidget { final bool canDeselect; final bool dynamicLayout; final bool showMultiSelectIndicator; - final void Function(Iterable itemPositions)? - visibleItemsListener; + final void Function(Iterable itemPositions)? visibleItemsListener; final Widget? topWidget; final int heroOffset; final bool shrinkWrap; @@ -90,24 +86,20 @@ class ImmichAssetGridView extends ConsumerStatefulWidget { class ImmichAssetGridViewState extends ConsumerState { final ItemScrollController _itemScrollController = ItemScrollController(); - final ScrollOffsetController _scrollOffsetController = - ScrollOffsetController(); - final ItemPositionsListener _itemPositionsListener = - ItemPositionsListener.create(); + final ScrollOffsetController _scrollOffsetController = ScrollOffsetController(); + final ItemPositionsListener _itemPositionsListener = ItemPositionsListener.create(); late final KeepAliveLink currentAssetLink; /// The timestamp when the haptic feedback was last invoked int _hapticFeedbackTS = 0; DateTime? _prevItemTime; bool _scrolling = false; - final Set _selectedAssets = - LinkedHashSet(equals: (a, b) => a.id == b.id, hashCode: (a) => a.id); + final Set _selectedAssets = LinkedHashSet(equals: (a, b) => a.id == b.id, hashCode: (a) => a.id); bool _dragging = false; int? _dragAnchorAssetIndex; int? _dragAnchorSectionIndex; - final Set _draggedAssets = - HashSet(equals: (a, b) => a.id == b.id, hashCode: (a) => a.id); + final Set _draggedAssets = HashSet(equals: (a, b) => a.id == b.id, hashCode: (a) => a.id); ScrollPhysics? _scrollPhysics; @@ -131,9 +123,7 @@ class ImmichAssetGridViewState extends ConsumerState { void _deselectAssets(List assets) { final assetsToDeselect = assets.where( - (a) => - widget.canDeselect || - !(widget.preselectedAssets?.contains(a) ?? false), + (a) => widget.canDeselect || !(widget.preselectedAssets?.contains(a) ?? false), ); setState(() { @@ -152,9 +142,7 @@ class ImmichAssetGridViewState extends ConsumerState { _dragAnchorSectionIndex = null; _draggedAssets.clear(); _dragging = false; - if (!widget.canDeselect && - widget.preselectedAssets != null && - widget.preselectedAssets!.isNotEmpty) { + if (!widget.canDeselect && widget.preselectedAssets != null && widget.preselectedAssets!.isNotEmpty) { _selectedAssets.addAll(widget.preselectedAssets!); } _callSelectionListener(false); @@ -162,8 +150,7 @@ class ImmichAssetGridViewState extends ConsumerState { } bool _allAssetsSelected(List assets) { - return widget.selectionActive && - assets.firstWhereOrNull((e) => !_selectedAssets.contains(e)) == null; + return widget.selectionActive && assets.firstWhereOrNull((e) => !_selectedAssets.contains(e)) == null; } Future _scrollToIndex(int index) async { @@ -171,16 +158,10 @@ class ImmichAssetGridViewState extends ConsumerState { // the scroll_position widget crashes. This is a workaround to prevent this. // If the index is within the last 10 elements, we jump instead of scrolling. if (widget.renderList.elements.length <= index + 10) { - _itemScrollController.jumpTo( - index: index, - ); + _itemScrollController.jumpTo(index: index); return; } - await _itemScrollController.scrollTo( - index: index, - alignment: 0, - duration: const Duration(milliseconds: 500), - ); + await _itemScrollController.scrollTo(index: index, alignment: 0, duration: const Duration(milliseconds: 500)); } Widget _itemBuilder(BuildContext c, int position) { @@ -229,23 +210,16 @@ class ImmichAssetGridViewState extends ConsumerState { return Text( DateFormat.yMMMM().format(date), - style: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold), ); } Widget _buildMultiSelectIndicator() { - return DisableMultiSelectButton( - onPressed: () => _deselectAll(), - selectedItemCount: _selectedAssets.length, - ); + return DisableMultiSelectButton(onPressed: () => _deselectAll(), selectedItemCount: _selectedAssets.length); } Widget _buildAssetGrid() { - final useDragScrolling = - widget.showDragScroll && widget.renderList.totalAssets >= 20; + final useDragScrolling = widget.showDragScroll && widget.renderList.totalAssets >= 20; void dragScrolling(bool active) { if (active != _scrolling) { @@ -256,24 +230,18 @@ class ImmichAssetGridViewState extends ConsumerState { } bool appBarOffset() { - return (ref.watch(tabProvider).index == 0 && - ModalRoute.of(context)?.settings.name == - TabControllerRoute.name) || + return (ref.watch(tabProvider).index == 0 && ModalRoute.of(context)?.settings.name == TabControllerRoute.name) || (ModalRoute.of(context)?.settings.name == AlbumViewerRoute.name); } final listWidget = ScrollablePositionedList.builder( - padding: EdgeInsets.only( - top: appBarOffset() ? 60 : 0, - bottom: 220, - ), + padding: EdgeInsets.only(top: appBarOffset() ? 60 : 0, bottom: 220), itemBuilder: _itemBuilder, itemPositionsListener: _itemPositionsListener, physics: _scrollPhysics, itemScrollController: _itemScrollController, scrollOffsetController: _scrollOffsetController, - itemCount: widget.renderList.elements.length + - (widget.topWidget != null ? 1 : 0), + itemCount: widget.renderList.elements.length + (widget.topWidget != null ? 1 : 0), addRepaintBoundaries: true, shrinkWrap: widget.shrinkWrap, ); @@ -287,9 +255,7 @@ class ImmichAssetGridViewState extends ConsumerState { ? context.colorScheme.primary.darken(amount: .5) : context.colorScheme.primary, labelTextBuilder: widget.showLabel ? _labelBuilder : null, - padding: appBarOffset() - ? const EdgeInsets.only(top: 60) - : const EdgeInsets.only(), + padding: appBarOffset() ? const EdgeInsets.only(top: 60) : const EdgeInsets.only(), heightOffset: appBarOffset() ? 60 : 0, labelConstraints: const BoxConstraints(maxHeight: 28), scrollbarAnimationDuration: const Duration(milliseconds: 300), @@ -301,12 +267,8 @@ class ImmichAssetGridViewState extends ConsumerState { return widget.onRefresh == null ? child : appBarOffset() - ? RefreshIndicator( - onRefresh: widget.onRefresh!, - edgeOffset: 30, - child: child, - ) - : RefreshIndicator(onRefresh: widget.onRefresh!, child: child); + ? RefreshIndicator(onRefresh: widget.onRefresh!, edgeOffset: 30, child: child) + : RefreshIndicator(onRefresh: widget.onRefresh!, child: child); } void _scrollToDate() { @@ -323,18 +285,13 @@ class ImmichAssetGridViewState extends ConsumerState { // Search for the index of the exact date in the list var index = widget.renderList.elements.indexWhere( - (e) => - e.date.year == date.year && - e.date.month == date.month && - e.date.day == date.day, + (e) => e.date.year == date.year && e.date.month == date.month && e.date.day == date.day, ); // If the exact date is not found, the timeline is grouped by month, // thus we search for the month if (index == -1) { - index = widget.renderList.elements.indexWhere( - (e) => e.date.year == date.year && e.date.month == date.month, - ); + index = widget.renderList.elements.indexWhere((e) => e.date.year == date.year && e.date.month == date.month); } if (index < widget.renderList.elements.length) { @@ -343,8 +300,7 @@ class ImmichAssetGridViewState extends ConsumerState { } else { ImmichToast.show( context: context, - msg: - "The date (${DateFormat.yMd().format(date)}) could not be found in the timeline.", + msg: "The date (${DateFormat.yMd().format(date)}) could not be found in the timeline.", gravity: ToastGravity.BOTTOM, toastType: ToastType.error, ); @@ -417,8 +373,7 @@ class ImmichAssetGridViewState extends ConsumerState { // on startup. if (_prevItemTime == null) { _prevItemTime = date; - } else if (_prevItemTime?.year != date.year || - _prevItemTime?.month != date.month) { + } else if (_prevItemTime?.year != date.year || _prevItemTime?.month != date.month) { _prevItemTime = date; final now = Timeline.now; @@ -433,13 +388,8 @@ class ImmichAssetGridViewState extends ConsumerState { void _scrollToTop() { // for some reason, this is necessary as well in order // to correctly reposition the drag thumb scroll bar - _itemScrollController.jumpTo( - index: 0, - ); - _itemScrollController.scrollTo( - index: 0, - duration: const Duration(milliseconds: 200), - ); + _itemScrollController.jumpTo(index: 0); + _itemScrollController.scrollTo(index: 0, duration: const Duration(milliseconds: 200)); } void _setDragStartIndex(AssetIndex index) { @@ -511,17 +461,13 @@ class ImmichAssetGridViewState extends ConsumerState { final selectedAssets = {}; var currentSectionIndex = startSectionIndex; while (currentSectionIndex < endSectionIndex) { - final section = - widget.renderList.elements.elementAtOrNull(currentSectionIndex); + final section = widget.renderList.elements.elementAtOrNull(currentSectionIndex); if (section == null) continue; - final sectionAssets = - widget.renderList.loadAssets(section.offset, section.count); + final sectionAssets = widget.renderList.loadAssets(section.offset, section.count); if (currentSectionIndex == startSectionIndex) { - selectedAssets.addAll( - sectionAssets.slice(startSectionAssetIndex, sectionAssets.length), - ); + selectedAssets.addAll(sectionAssets.slice(startSectionAssetIndex, sectionAssets.length)); } else { selectedAssets.addAll(sectionAssets); } @@ -531,16 +477,11 @@ class ImmichAssetGridViewState extends ConsumerState { final section = widget.renderList.elements.elementAtOrNull(endSectionIndex); if (section != null) { - final sectionAssets = - widget.renderList.loadAssets(section.offset, section.count); + final sectionAssets = widget.renderList.loadAssets(section.offset, section.count); if (startSectionIndex == endSectionIndex) { - selectedAssets.addAll( - sectionAssets.slice(startSectionAssetIndex, endSectionAssetIndex + 1), - ); + selectedAssets.addAll(sectionAssets.slice(startSectionAssetIndex, endSectionAssetIndex + 1)); } else { - selectedAssets.addAll( - sectionAssets.slice(0, endSectionAssetIndex + 1), - ); + selectedAssets.addAll(sectionAssets.slice(0, endSectionAssetIndex + 1)); } } @@ -562,8 +503,7 @@ class ImmichAssetGridViewState extends ConsumerState { /// "add to album" button. /// /// `_selectedAssets` includes `preselectedAssets` on initialization. - if (_selectedAssets.length > - (widget.preselectedAssets?.length ?? 0)) { + if (_selectedAssets.length > (widget.preselectedAssets?.length ?? 0)) { /// `_deselectAll` only deselects the selected assets, /// doesn't affect the preselected ones. _deselectAll(); @@ -580,13 +520,11 @@ class ImmichAssetGridViewState extends ConsumerState { onAssetEnter: _handleDragAssetEnter, onEnd: _stopDrag, onScroll: _dragDragScroll, - onScrollStart: () => WidgetsBinding.instance.addPostFrameCallback( - (_) => controlBottomAppBarNotifier.minimize(), - ), + onScrollStart: () => + WidgetsBinding.instance.addPostFrameCallback((_) => controlBottomAppBarNotifier.minimize()), child: _buildAssetGrid(), ), - if (widget.showMultiSelectIndicator && widget.selectionActive) - _buildMultiSelectIndicator(), + if (widget.showMultiSelectIndicator && widget.selectionActive) _buildMultiSelectIndicator(), ], ), ); @@ -617,10 +555,7 @@ class _PlaceholderRow extends StatelessWidget { key: ValueKey(i), width: width, height: height, - margin: EdgeInsets.only( - bottom: margin, - right: i + 1 == number ? 0.0 : margin, - ), + margin: EdgeInsets.only(bottom: margin, right: i + 1 == number ? 0.0 : margin), ), ], ); @@ -666,31 +601,23 @@ class _Section extends StatelessWidget { }); @override - Widget build( - BuildContext context, - ) { + Widget build(BuildContext context) { return LayoutBuilder( builder: (context, constraints) { - final width = constraints.maxWidth / assetsPerRow - - margin * (assetsPerRow - 1) / assetsPerRow; + final width = constraints.maxWidth / assetsPerRow - margin * (assetsPerRow - 1) / assetsPerRow; final rows = (section.count + assetsPerRow - 1) ~/ assetsPerRow; - final List assetsToRender = scrolling - ? [] - : renderList.loadAssets(section.offset, section.count); + final List assetsToRender = scrolling ? [] : renderList.loadAssets(section.offset, section.count); return Column( key: ValueKey(section.offset), crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (section.type == RenderAssetGridElementType.monthTitle) - _MonthTitle(date: section.date), + if (section.type == RenderAssetGridElementType.monthTitle) _MonthTitle(date: section.date), if (section.type == RenderAssetGridElementType.groupDividerTitle || section.type == RenderAssetGridElementType.monthTitle) _Title( selectionActive: selectionActive, title: section.title!, - assets: scrolling - ? [] - : renderList.loadAssets(section.offset, section.totalCount), + assets: scrolling ? [] : renderList.loadAssets(section.offset, section.totalCount), allAssetsSelected: allAssetsSelected, selectAssets: selectAssets, deselectAssets: deselectAssets, @@ -699,9 +626,7 @@ class _Section extends StatelessWidget { scrolling ? _PlaceholderRow( key: ValueKey(i), - number: i + 1 == rows - ? section.count - i * assetsPerRow - : assetsPerRow, + number: i + 1 == rows ? section.count - i * assetsPerRow : assetsPerRow, width: width, height: width, margin: margin, @@ -710,10 +635,7 @@ class _Section extends StatelessWidget { key: ValueKey(i), rowStartIndex: i * assetsPerRow, sectionIndex: sectionIndex, - assets: assetsToRender.nestedSlice( - i * assetsPerRow, - min((i + 1) * assetsPerRow, section.count), - ), + assets: assetsToRender.nestedSlice(i * assetsPerRow, min((i + 1) * assetsPerRow, section.count)), absoluteOffset: section.offset + i * assetsPerRow, width: width, assetsPerRow: assetsPerRow, @@ -741,25 +663,18 @@ class _Section extends StatelessWidget { class _MonthTitle extends StatelessWidget { final DateTime date; - const _MonthTitle({ - required this.date, - }); + const _MonthTitle({required this.date}); @override Widget build(BuildContext context) { - final monthFormat = DateTime.now().year == date.year - ? DateFormat.MMMM() - : DateFormat.yMMMM(); + final monthFormat = DateTime.now().year == date.year ? DateFormat.MMMM() : DateFormat.yMMMM(); final String title = monthFormat.format(date); return Padding( key: Key("month-$title"), padding: const EdgeInsets.only(left: 12.0, top: 24.0), child: Text( toBeginningOfSentenceCase(title, context.locale.languageCode), - style: const TextStyle( - fontSize: 26, - fontWeight: FontWeight.w500, - ), + style: const TextStyle(fontSize: 26, fontWeight: FontWeight.w500), ), ); } @@ -844,8 +759,7 @@ class _AssetRow extends StatelessWidget { final widthDistribution = List.filled(assets.length, 1.0); if (dynamicLayout) { - final aspectRatios = - assets.map((e) => (e.width ?? 1) / (e.height ?? 1)).toList(); + final aspectRatios = assets.map((e) => (e.width ?? 1) / (e.height ?? 1)).toList(); final meanAspectRatio = aspectRatios.sum / assets.length; // 1: mean width @@ -859,11 +773,7 @@ class _AssetRow extends StatelessWidget { // Normalize: final sum = arConfiguration.sum; - widthDistribution.setRange( - 0, - widthDistribution.length, - arConfiguration.map((e) => (e * assets.length) / sum), - ); + widthDistribution.setRange(0, widthDistribution.length, arConfiguration.map((e) => (e * assets.length) / sum)); } return Row( key: key, @@ -873,10 +783,7 @@ class _AssetRow extends StatelessWidget { return Container( width: width * widthDistribution[index], height: width, - margin: EdgeInsets.only( - bottom: margin, - right: last ? 0.0 : margin, - ), + margin: EdgeInsets.only(bottom: margin, right: last ? 0.0 : margin), child: GestureDetector( onTap: () { if (selectionActive) { diff --git a/mobile/lib/widgets/asset_grid/multiselect_grid.dart b/mobile/lib/widgets/asset_grid/multiselect_grid.dart index 98b1c6f601..c28f407e1e 100644 --- a/mobile/lib/widgets/asset_grid/multiselect_grid.dart +++ b/mobile/lib/widgets/asset_grid/multiselect_grid.dart @@ -65,11 +65,9 @@ class MultiselectGrid extends HookConsumerWidget { final bool unfavorite; final bool editEnabled; final Widget? emptyIndicator; - Widget buildDefaultLoadingIndicator() => - const Center(child: CircularProgressIndicator()); + Widget buildDefaultLoadingIndicator() => const Center(child: CircularProgressIndicator()); - Widget buildEmptyIndicator() => - emptyIndicator ?? Center(child: const Text("no_assets_to_show").tr()); + Widget buildEmptyIndicator() => emptyIndicator ?? Center(child: const Text("no_assets_to_show").tr()); @override Widget build(BuildContext context, WidgetRef ref) { @@ -81,57 +79,38 @@ class MultiselectGrid extends HookConsumerWidget { final currentUser = ref.watch(currentUserProvider); final processing = useProcessingOverlay(); - useEffect( - () { - selectionEnabledHook.addListener(() { - multiselectEnabled.state = selectionEnabledHook.value; - }); + useEffect(() { + selectionEnabledHook.addListener(() { + multiselectEnabled.state = selectionEnabledHook.value; + }); - return () { - // This does not work in tests - if (kReleaseMode) { - selectionEnabledHook.dispose(); - } - }; - }, - [], - ); + return () { + // This does not work in tests + if (kReleaseMode) { + selectionEnabledHook.dispose(); + } + }; + }, []); - void selectionListener( - bool multiselect, - Set selectedAssets, - ) { + void selectionListener(bool multiselect, Set selectedAssets) { selectionEnabledHook.value = multiselect; selection.value = selectedAssets; - selectionAssetState.value = - AssetSelectionState.fromSelection(selectedAssets); + selectionAssetState.value = AssetSelectionState.fromSelection(selectedAssets); } errorBuilder(String? msg) => msg != null && msg.isNotEmpty - ? () => ImmichToast.show( - context: context, - msg: msg, - gravity: ToastGravity.BOTTOM, - ) + ? () => ImmichToast.show(context: context, msg: msg, gravity: ToastGravity.BOTTOM) : null; - Iterable ownedRemoteSelection({ - String? localErrorMessage, - String? ownerErrorMessage, - }) { + Iterable ownedRemoteSelection({String? localErrorMessage, String? ownerErrorMessage}) { final assets = selection.value; return assets .remoteOnly(errorCallback: errorBuilder(localErrorMessage)) - .ownedOnly( - currentUser, - errorCallback: errorBuilder(ownerErrorMessage), - ); + .ownedOnly(currentUser, errorCallback: errorBuilder(ownerErrorMessage)); } Iterable remoteSelection({String? errorMessage}) => - selection.value.remoteOnly( - errorCallback: errorBuilder(errorMessage), - ); + selection.value.remoteOnly(errorCallback: errorBuilder(errorMessage)); void onShareAssets(bool shareLocal) { processing.value = true; @@ -139,9 +118,7 @@ class MultiselectGrid extends HookConsumerWidget { // Share = Download + Send to OS specific share sheet handleShareAssets(ref, context, selection.value); } else { - final ids = - remoteSelection(errorMessage: "home_page_share_err_local".tr()) - .map((e) => e.remoteId!); + final ids = remoteSelection(errorMessage: "home_page_share_err_local".tr()).map((e) => e.remoteId!); context.pushRoute(SharedLinkEditRoute(assetsList: ids.toList())); } processing.value = false; @@ -182,23 +159,16 @@ class MultiselectGrid extends HookConsumerWidget { processing.value = true; try { final toDelete = selection.value - .ownedOnly( - currentUser, - errorCallback: errorBuilder('home_page_delete_err_partner'.tr()), - ) + .ownedOnly(currentUser, errorCallback: errorBuilder('home_page_delete_err_partner'.tr())) .toList(); - final isDeleted = await ref - .read(assetProvider.notifier) - .deleteAssets(toDelete, force: force); + final isDeleted = await ref.read(assetProvider.notifier).deleteAssets(toDelete, force: force); if (isDeleted) { ImmichToast.show( context: context, msg: force - ? 'assets_deleted_permanently' - .tr(namedArgs: {'count': "${selection.value.length}"}) - : 'assets_trashed' - .tr(namedArgs: {'count': "${selection.value.length}"}), + ? 'assets_deleted_permanently'.tr(namedArgs: {'count': "${selection.value.length}"}) + : 'assets_trashed'.tr(namedArgs: {'count': "${selection.value.length}"}), gravity: ToastGravity.BOTTOM, ); selectionEnabledHook.value = false; @@ -213,26 +183,20 @@ class MultiselectGrid extends HookConsumerWidget { try { final localAssets = selection.value.where((a) => a.isLocal).toList(); - final toDelete = isMergedAsset - ? localAssets.where((e) => e.storage == AssetState.merged) - : localAssets; + final toDelete = isMergedAsset ? localAssets.where((e) => e.storage == AssetState.merged) : localAssets; if (toDelete.isEmpty) { return; } - final isDeleted = await ref - .read(assetProvider.notifier) - .deleteLocalAssets(toDelete.toList()); + final isDeleted = await ref.read(assetProvider.notifier).deleteLocalAssets(toDelete.toList()); if (isDeleted) { - final deletedCount = - localAssets.where((e) => !isMergedAsset || e.isRemote).length; + final deletedCount = localAssets.where((e) => !isMergedAsset || e.isRemote).length; ImmichToast.show( context: context, - msg: 'assets_removed_permanently_from_device' - .tr(namedArgs: {'count': "$deletedCount"}), + msg: 'assets_removed_permanently_from_device'.tr(namedArgs: {'count': "$deletedCount"}), gravity: ToastGravity.BOTTOM, ); @@ -248,34 +212,17 @@ class MultiselectGrid extends HookConsumerWidget { try { final toDownload = selection.value.toList(); - final results = await ref - .read(downloadStateProvider.notifier) - .downloadAllAsset(toDownload); + final results = await ref.read(downloadStateProvider.notifier).downloadAllAsset(toDownload); final totalCount = toDownload.length; final successCount = results.where((e) => e).length; final failedCount = totalCount - successCount; final msg = failedCount > 0 - ? 'assets_downloaded_failed'.t( - context: context, - args: { - 'count': successCount, - 'error': failedCount, - }, - ) - : 'assets_downloaded_successfully'.t( - context: context, - args: { - 'count': successCount, - }, - ); + ? 'assets_downloaded_failed'.t(context: context, args: {'count': successCount, 'error': failedCount}) + : 'assets_downloaded_successfully'.t(context: context, args: {'count': successCount}); - ImmichToast.show( - context: context, - msg: msg, - gravity: ToastGravity.BOTTOM, - ); + ImmichToast.show(context: context, msg: msg, gravity: ToastGravity.BOTTOM); } finally { processing.value = false; selectionEnabledHook.value = false; @@ -290,19 +237,15 @@ class MultiselectGrid extends HookConsumerWidget { ownerErrorMessage: 'home_page_delete_err_partner'.tr(), ).toList(); - final isDeleted = - await ref.read(assetProvider.notifier).deleteRemoteAssets( - toDelete, - shouldDeletePermanently: shouldDeletePermanently, - ); + final isDeleted = await ref + .read(assetProvider.notifier) + .deleteRemoteAssets(toDelete, shouldDeletePermanently: shouldDeletePermanently); if (isDeleted) { ImmichToast.show( context: context, msg: shouldDeletePermanently - ? 'assets_deleted_permanently_from_server' - .tr(namedArgs: {'count': "${toDelete.length}"}) - : 'assets_trashed_from_server' - .tr(namedArgs: {'count': "${toDelete.length}"}), + ? 'assets_deleted_permanently_from_server'.tr(namedArgs: {'count': "${toDelete.length}"}) + : 'assets_trashed_from_server'.tr(namedArgs: {'count': "${toDelete.length}"}), gravity: ToastGravity.BOTTOM, ); } @@ -316,10 +259,9 @@ class MultiselectGrid extends HookConsumerWidget { processing.value = true; selectionEnabledHook.value = false; try { - ref.read(manualUploadProvider.notifier).uploadAssets( - context, - selection.value.where((a) => a.storage == AssetState.local), - ); + ref + .read(manualUploadProvider.notifier) + .uploadAssets(context, selection.value.where((a) => a.storage == AssetState.local)); } finally { processing.value = false; } @@ -328,16 +270,11 @@ class MultiselectGrid extends HookConsumerWidget { void onAddToAlbum(Album album) async { processing.value = true; try { - final Iterable assets = remoteSelection( - errorMessage: "home_page_add_to_album_err_local".tr(), - ); + final Iterable assets = remoteSelection(errorMessage: "home_page_add_to_album_err_local".tr()); if (assets.isEmpty) { return; } - final result = await ref.read(albumServiceProvider).addAssets( - album, - assets, - ); + final result = await ref.read(albumServiceProvider).addAssets(album, assets); if (result != null) { if (result.alreadyInAlbum.isNotEmpty) { @@ -355,10 +292,7 @@ class MultiselectGrid extends HookConsumerWidget { ImmichToast.show( context: context, msg: "home_page_add_to_album_success".tr( - namedArgs: { - "album": album.name, - "added": result.successfullyAdded.toString(), - }, + namedArgs: {"album": album.name, "added": result.successfullyAdded.toString()}, ), toastType: ToastType.success, ); @@ -373,15 +307,11 @@ class MultiselectGrid extends HookConsumerWidget { void onCreateNewAlbum() async { processing.value = true; try { - final Iterable assets = remoteSelection( - errorMessage: "home_page_add_to_album_err_local".tr(), - ); + final Iterable assets = remoteSelection(errorMessage: "home_page_add_to_album_err_local".tr()); if (assets.isEmpty) { return; } - final result = await ref - .read(albumServiceProvider) - .createAlbumWithGeneratedName(assets); + final result = await ref.read(albumServiceProvider).createAlbumWithGeneratedName(assets); if (result != null) { ref.watch(albumProvider.notifier).refreshRemoteAlbums(); @@ -401,9 +331,7 @@ class MultiselectGrid extends HookConsumerWidget { return; } - await ref.read(stackServiceProvider).createStack( - selection.value.map((e) => e.remoteId!).toList(), - ); + await ref.read(stackServiceProvider).createStack(selection.value.map((e) => e.remoteId!).toList()); } finally { processing.value = false; selectionEnabledHook.value = false; @@ -449,16 +377,9 @@ class MultiselectGrid extends HookConsumerWidget { ); if (remoteAssets.isNotEmpty) { final isInLockedView = ref.read(inLockedViewProvider); - final visibility = isInLockedView - ? AssetVisibilityEnum.timeline - : AssetVisibilityEnum.locked; + final visibility = isInLockedView ? AssetVisibilityEnum.timeline : AssetVisibilityEnum.locked; - await handleSetAssetsVisibility( - ref, - context, - visibility, - remoteAssets.toList(), - ); + await handleSetAssetsVisibility(ref, context, visibility, remoteAssets.toList()); } } finally { processing.value = false; @@ -466,42 +387,34 @@ class MultiselectGrid extends HookConsumerWidget { } } - Future Function() wrapLongRunningFun( - Future Function() fun, { - bool showOverlay = true, - }) => - () async { - if (showOverlay) processing.value = true; - try { - final result = await fun(); - if (result.runtimeType != bool || result == true) { - selectionEnabledHook.value = false; - } - return result; - } finally { - if (showOverlay) processing.value = false; - } - }; + Future Function() wrapLongRunningFun(Future Function() fun, {bool showOverlay = true}) => () async { + if (showOverlay) processing.value = true; + try { + final result = await fun(); + if (result.runtimeType != bool || result == true) { + selectionEnabledHook.value = false; + } + return result; + } finally { + if (showOverlay) processing.value = false; + } + }; return SafeArea( top: true, bottom: false, child: Stack( children: [ - ref.watch(renderListProvider).when( - data: (data) => data.isEmpty && - (buildLoadingIndicator != null || topWidget == null) + ref + .watch(renderListProvider) + .when( + data: (data) => data.isEmpty && (buildLoadingIndicator != null || topWidget == null) ? (buildLoadingIndicator ?? buildEmptyIndicator)() : ImmichAssetGrid( renderList: data, listener: selectionListener, selectionActive: selectionEnabledHook.value, - onRefresh: onRefresh == null - ? null - : wrapLongRunningFun( - onRefresh!, - showOverlay: false, - ), + onRefresh: onRefresh == null ? null : wrapLongRunningFun(onRefresh!, showOverlay: false), topWidget: topWidget, showStack: stackEnabled, showDragScrollLabel: dragScrollLabelEnabled, @@ -534,9 +447,7 @@ class MultiselectGrid extends HookConsumerWidget { unarchive: unarchive, onToggleLocked: onToggleLockedVisibility, onRemoveFromAlbum: onRemoveFromAlbum != null - ? wrapLongRunningFun( - () => onRemoveFromAlbum!(selection.value), - ) + ? wrapLongRunningFun(() => onRemoveFromAlbum!(selection.value)) : null, ), ], diff --git a/mobile/lib/widgets/asset_grid/multiselect_grid_status_indicator.dart b/mobile/lib/widgets/asset_grid/multiselect_grid_status_indicator.dart index b17029f2af..3a1fa82a28 100644 --- a/mobile/lib/widgets/asset_grid/multiselect_grid_status_indicator.dart +++ b/mobile/lib/widgets/asset_grid/multiselect_grid_status_indicator.dart @@ -5,11 +5,7 @@ import 'package:immich_mobile/providers/asset_viewer/render_list_status_provider import 'package:immich_mobile/widgets/common/delayed_loading_indicator.dart'; class MultiselectGridStatusIndicator extends HookConsumerWidget { - const MultiselectGridStatusIndicator({ - super.key, - this.buildLoadingIndicator, - this.emptyIndicator, - }); + const MultiselectGridStatusIndicator({super.key, this.buildLoadingIndicator, this.emptyIndicator}); final Widget Function()? buildLoadingIndicator; final Widget? emptyIndicator; @@ -18,18 +14,13 @@ class MultiselectGridStatusIndicator extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final renderListStatus = ref.watch(renderListStatusProvider); return switch (renderListStatus) { - RenderListStatusEnum.loading => buildLoadingIndicator == null - ? const Center( - child: DelayedLoadingIndicator( - delay: Duration(milliseconds: 500), - ), - ) - : buildLoadingIndicator!(), - RenderListStatusEnum.empty => - emptyIndicator ?? Center(child: const Text("no_assets_to_show").tr()), - RenderListStatusEnum.error => - Center(child: const Text("error_loading_assets").tr()), - RenderListStatusEnum.complete => const SizedBox() + RenderListStatusEnum.loading => + buildLoadingIndicator == null + ? const Center(child: DelayedLoadingIndicator(delay: Duration(milliseconds: 500))) + : buildLoadingIndicator!(), + RenderListStatusEnum.empty => emptyIndicator ?? Center(child: const Text("no_assets_to_show").tr()), + RenderListStatusEnum.error => Center(child: const Text("error_loading_assets").tr()), + RenderListStatusEnum.complete => const SizedBox(), }; } } diff --git a/mobile/lib/widgets/asset_grid/thumbnail_image.dart b/mobile/lib/widgets/asset_grid/thumbnail_image.dart index 5b98c8a756..93385b88b3 100644 --- a/mobile/lib/widgets/asset_grid/thumbnail_image.dart +++ b/mobile/lib/widgets/asset_grid/thumbnail_image.dart @@ -53,16 +53,13 @@ class ThumbnailImage extends StatelessWidget { decoration: BoxDecoration( border: multiselectEnabled && isSelected ? canDeselect - ? Border.all( - color: assetContainerColor, - width: 8, - ) - : const Border( - top: BorderSide(color: Colors.grey, width: 8), - right: BorderSide(color: Colors.grey, width: 8), - bottom: BorderSide(color: Colors.grey, width: 8), - left: BorderSide(color: Colors.grey, width: 8), - ) + ? Border.all(color: assetContainerColor, width: 8) + : const Border( + top: BorderSide(color: Colors.grey, width: 8), + right: BorderSide(color: Colors.grey, width: 8), + bottom: BorderSide(color: Colors.grey, width: 8), + left: BorderSide(color: Colors.grey, width: 8), + ) : const Border(), ), child: Stack( @@ -77,21 +74,9 @@ class ThumbnailImage extends StatelessWidget { ), if (showStorageIndicator) _StorageIcon(storage: asset.storage), if (asset.isFavorite) - const Positioned( - left: 8, - bottom: 5, - child: Icon( - Icons.favorite, - color: Colors.white, - size: 16, - ), - ), + const Positioned(left: 8, bottom: 5, child: Icon(Icons.favorite, color: Colors.white, size: 16)), if (asset.isVideo) _VideoIcon(duration: asset.duration), - if (asset.stackCount > 0) - _StackIcon( - isVideo: asset.isVideo, - stackCount: asset.stackCount, - ), + if (asset.stackCount > 0) _StackIcon(isVideo: asset.isVideo, stackCount: asset.stackCount), ], ), ), @@ -99,15 +84,9 @@ class ThumbnailImage extends StatelessWidget { isSelected ? const Padding( padding: EdgeInsets.all(3.0), - child: Align( - alignment: Alignment.topLeft, - child: _SelectedIcon(), - ), + child: Align(alignment: Alignment.topLeft, child: _SelectedIcon()), ) - : const Icon( - Icons.circle_outlined, - color: Colors.white, - ), + : const Icon(Icons.circle_outlined, color: Colors.white), ], ); } @@ -123,14 +102,8 @@ class _SelectedIcon extends StatelessWidget { : context.primaryColor.lighten(amount: 0.8); return DecoratedBox( - decoration: BoxDecoration( - shape: BoxShape.circle, - color: assetContainerColor, - ), - child: Icon( - Icons.check_circle_rounded, - color: context.primaryColor, - ), + decoration: BoxDecoration(shape: BoxShape.circle, color: assetContainerColor), + child: Icon(Icons.check_circle_rounded, color: context.primaryColor), ); } } @@ -149,18 +122,10 @@ class _VideoIcon extends StatelessWidget { children: [ Text( duration.format(), - style: const TextStyle( - color: Colors.white, - fontSize: 10, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold), ), const SizedBox(width: 3), - const Icon( - Icons.play_circle_fill_rounded, - color: Colors.white, - size: 18, - ), + const Icon(Icons.play_circle_fill_rounded, color: Colors.white, size: 18), ], ), ); @@ -183,21 +148,10 @@ class _StackIcon extends StatelessWidget { if (stackCount > 1) Text( "$stackCount", - style: const TextStyle( - color: Colors.white, - fontSize: 10, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold), ), - if (stackCount > 1) - const SizedBox( - width: 3, - ), - const Icon( - Icons.burst_mode_rounded, - color: Colors.white, - size: 18, - ), + if (stackCount > 1) const SizedBox(width: 3), + const Icon(Icons.burst_mode_rounded, color: Colors.white, size: 18), ], ), ); @@ -213,53 +167,35 @@ class _StorageIcon extends StatelessWidget { Widget build(BuildContext context) { return switch (storage) { AssetState.local => const Positioned( - right: 8, - bottom: 5, - child: Icon( - Icons.cloud_off_outlined, - color: Color.fromRGBO(255, 255, 255, 0.8), - size: 16, - shadows: [ - Shadow( - blurRadius: 5.0, - color: Color.fromRGBO(0, 0, 0, 0.6), - offset: Offset(0.0, 0.0), - ), - ], - ), + right: 8, + bottom: 5, + child: Icon( + Icons.cloud_off_outlined, + color: Color.fromRGBO(255, 255, 255, 0.8), + size: 16, + shadows: [Shadow(blurRadius: 5.0, color: Color.fromRGBO(0, 0, 0, 0.6), offset: Offset(0.0, 0.0))], ), + ), AssetState.remote => const Positioned( - right: 8, - bottom: 5, - child: Icon( - Icons.cloud_outlined, - color: Color.fromRGBO(255, 255, 255, 0.8), - size: 16, - shadows: [ - Shadow( - blurRadius: 5.0, - color: Color.fromRGBO(0, 0, 0, 0.6), - offset: Offset(0.0, 0.0), - ), - ], - ), + right: 8, + bottom: 5, + child: Icon( + Icons.cloud_outlined, + color: Color.fromRGBO(255, 255, 255, 0.8), + size: 16, + shadows: [Shadow(blurRadius: 5.0, color: Color.fromRGBO(0, 0, 0, 0.6), offset: Offset(0.0, 0.0))], ), + ), AssetState.merged => const Positioned( - right: 8, - bottom: 5, - child: Icon( - Icons.cloud_done_outlined, - color: Color.fromRGBO(255, 255, 255, 0.8), - size: 16, - shadows: [ - Shadow( - blurRadius: 5.0, - color: Color.fromRGBO(0, 0, 0, 0.6), - offset: Offset(0.0, 0.0), - ), - ], - ), + right: 8, + bottom: 5, + child: Icon( + Icons.cloud_done_outlined, + color: Color.fromRGBO(255, 255, 255, 0.8), + size: 16, + shadows: [Shadow(blurRadius: 5.0, color: Color.fromRGBO(0, 0, 0, 0.6), offset: Offset(0.0, 0.0))], ), + ), }; } } @@ -290,13 +226,7 @@ class _ImageIcon extends StatelessWidget { tag: isDto ? '${asset.remoteId}-$heroOffset' : asset.id + heroOffset, child: Stack( children: [ - SizedBox.expand( - child: ImmichThumbnail( - asset: asset, - height: 250, - width: 250, - ), - ), + SizedBox.expand(child: ImmichThumbnail(asset: asset, height: 250, width: 250)), const DecoratedBox( decoration: BoxDecoration( gradient: LinearGradient( @@ -322,13 +252,8 @@ class _ImageIcon extends StatelessWidget { } return DecoratedBox( - decoration: canDeselect - ? BoxDecoration(color: assetContainerColor) - : const BoxDecoration(color: Colors.grey), - child: ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(15.0)), - child: image, - ), + decoration: canDeselect ? BoxDecoration(color: assetContainerColor) : const BoxDecoration(color: Colors.grey), + child: ClipRRect(borderRadius: const BorderRadius.all(Radius.circular(15.0)), child: image), ); } } diff --git a/mobile/lib/widgets/asset_grid/thumbnail_placeholder.dart b/mobile/lib/widgets/asset_grid/thumbnail_placeholder.dart index 5b12426a50..a84dfbae37 100644 --- a/mobile/lib/widgets/asset_grid/thumbnail_placeholder.dart +++ b/mobile/lib/widgets/asset_grid/thumbnail_placeholder.dart @@ -7,12 +7,7 @@ class ThumbnailPlaceholder extends StatelessWidget { final double width; final double height; - const ThumbnailPlaceholder({ - super.key, - this.margin = EdgeInsets.zero, - this.width = 250, - this.height = 250, - }); + const ThumbnailPlaceholder({super.key, this.margin = EdgeInsets.zero, this.width = 250, this.height = 250}); @override Widget build(BuildContext context) { @@ -26,11 +21,7 @@ class ThumbnailPlaceholder extends StatelessWidget { height: height, margin: margin, decoration: BoxDecoration( - gradient: LinearGradient( - colors: gradientColors, - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - ), + gradient: LinearGradient(colors: gradientColors, begin: Alignment.topCenter, end: Alignment.bottomCenter), ), ); } diff --git a/mobile/lib/widgets/asset_grid/upload_dialog.dart b/mobile/lib/widgets/asset_grid/upload_dialog.dart index c2a38fab8c..86e2759566 100644 --- a/mobile/lib/widgets/asset_grid/upload_dialog.dart +++ b/mobile/lib/widgets/asset_grid/upload_dialog.dart @@ -4,11 +4,11 @@ class UploadDialog extends ConfirmDialog { final Function onUpload; const UploadDialog({super.key, required this.onUpload}) - : super( - title: 'upload_dialog_title', - content: 'upload_dialog_info', - cancel: 'cancel', - ok: 'upload', - onOk: onUpload, - ); + : super( + title: 'upload_dialog_title', + content: 'upload_dialog_info', + cancel: 'cancel', + ok: 'upload', + onOk: onUpload, + ); } diff --git a/mobile/lib/widgets/asset_viewer/advanced_bottom_sheet.dart b/mobile/lib/widgets/asset_viewer/advanced_bottom_sheet.dart index f6a617bf1c..faa058ced4 100644 --- a/mobile/lib/widgets/asset_viewer/advanced_bottom_sheet.dart +++ b/mobile/lib/widgets/asset_viewer/advanced_bottom_sheet.dart @@ -8,11 +8,7 @@ class AdvancedBottomSheet extends HookConsumerWidget { final Asset assetDetail; final ScrollController? scrollController; - const AdvancedBottomSheet({ - super.key, - required this.assetDetail, - this.scrollController, - }); + const AdvancedBottomSheet({super.key, required this.assetDetail, this.scrollController}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -26,29 +22,15 @@ class AdvancedBottomSheet extends HookConsumerWidget { return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - const Align( - child: Text( - "ADVANCED INFO", - style: TextStyle(fontSize: 12.0), - ), - ), + const Align(child: Text("ADVANCED INFO", style: TextStyle(fontSize: 12.0))), const SizedBox(height: 32.0), Container( decoration: BoxDecoration( - color: context.isDarkTheme - ? Colors.grey[900] - : Colors.grey[200], - borderRadius: const BorderRadius.all( - Radius.circular(15.0), - ), + color: context.isDarkTheme ? Colors.grey[900] : Colors.grey[200], + borderRadius: const BorderRadius.all(Radius.circular(15.0)), ), child: Padding( - padding: const EdgeInsets.only( - right: 16.0, - left: 16, - top: 8, - bottom: 16, - ), + padding: const EdgeInsets.only(right: 16.0, left: 16, top: 8, bottom: 16), child: ListView( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), @@ -57,29 +39,18 @@ class AdvancedBottomSheet extends HookConsumerWidget { alignment: Alignment.centerRight, child: IconButton( onPressed: () { - Clipboard.setData( - ClipboardData( - text: assetDetail.toString(), - ), - ).then((_) { + Clipboard.setData(ClipboardData(text: assetDetail.toString())).then((_) { context.scaffoldMessenger.showSnackBar( SnackBar( content: Text( "Copied to clipboard", - style: - context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), ), ), ); }); }, - icon: Icon( - Icons.copy, - size: 16.0, - color: context.primaryColor, - ), + icon: Icon(Icons.copy, size: 16.0, color: context.primaryColor), ), ), SelectableText( diff --git a/mobile/lib/widgets/asset_viewer/animated_play_pause.dart b/mobile/lib/widgets/asset_viewer/animated_play_pause.dart index 7935567b8c..e7ceac6105 100644 --- a/mobile/lib/widgets/asset_viewer/animated_play_pause.dart +++ b/mobile/lib/widgets/asset_viewer/animated_play_pause.dart @@ -2,12 +2,7 @@ import 'package:flutter/material.dart'; /// A widget that animates implicitly between a play and a pause icon. class AnimatedPlayPause extends StatefulWidget { - const AnimatedPlayPause({ - super.key, - required this.playing, - this.size, - this.color, - }); + const AnimatedPlayPause({super.key, required this.playing, this.size, this.color}); final double? size; final bool playing; @@ -17,8 +12,7 @@ class AnimatedPlayPause extends StatefulWidget { State createState() => AnimatedPlayPauseState(); } -class AnimatedPlayPauseState extends State - with SingleTickerProviderStateMixin { +class AnimatedPlayPauseState extends State with SingleTickerProviderStateMixin { late final animationController = AnimationController( vsync: this, value: widget.playing ? 1 : 0, diff --git a/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart b/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart index 59d97bf0c7..c7125e2200 100644 --- a/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart +++ b/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart @@ -52,45 +52,34 @@ class BottomGalleryBar extends ConsumerWidget { if (asset == null) { return const SizedBox(); } - final isOwner = - asset.ownerId == fastHash(ref.watch(currentUserProvider)?.id ?? ''); + final isOwner = asset.ownerId == fastHash(ref.watch(currentUserProvider)?.id ?? ''); final showControls = ref.watch(showControlsProvider); final stackId = asset.stackId; - final stackItems = showStack && stackId != null - ? ref.watch(assetStackStateProvider(stackId)) - : []; + final stackItems = showStack && stackId != null ? ref.watch(assetStackStateProvider(stackId)) : []; bool isStackPrimaryAsset = asset.stackPrimaryAssetId == null; final navStack = AutoRouter.of(context).stackData; - final isTrashEnabled = - ref.watch(serverInfoProvider.select((v) => v.serverFeatures.trash)); - final isFromTrash = isTrashEnabled && - navStack.length > 2 && - navStack.elementAt(navStack.length - 2).name == TrashRoute.name; + final isTrashEnabled = ref.watch(serverInfoProvider.select((v) => v.serverFeatures.trash)); + final isFromTrash = + isTrashEnabled && navStack.length > 2 && navStack.elementAt(navStack.length - 2).name == TrashRoute.name; final isInAlbum = ref.watch(currentAlbumProvider)?.isRemote ?? false; void removeAssetFromStack() { if (stackIndex.value > 0 && showStack && stackId != null) { - ref - .read(assetStackStateProvider(stackId).notifier) - .removeChild(stackIndex.value - 1); + ref.read(assetStackStateProvider(stackId).notifier).removeChild(stackIndex.value - 1); } } void handleDelete() async { Future onDelete(bool force) async { - final isDeleted = await ref.read(assetProvider.notifier).deleteAssets( - {asset}, - force: force, - ); + final isDeleted = await ref.read(assetProvider.notifier).deleteAssets({asset}, force: force); if (isDeleted && isStackPrimaryAsset) { // Workaround for asset remaining in the gallery renderList.deleteAsset(asset); // `assetIndex == totalAssets.value - 1` handle the case of removing the last asset // to not throw the error when the next preCache index is called - if (totalAssets.value == 1 || - assetIndex.value == totalAssets.value - 1) { + if (totalAssets.value == 1 || assetIndex.value == totalAssets.value - 1) { // Handle only one asset context.maybePop(); } @@ -98,9 +87,7 @@ class BottomGalleryBar extends ConsumerWidget { totalAssets.value -= 1; } if (isDeleted) { - ref - .read(currentAssetProvider.notifier) - .set(renderList.loadAsset(assetIndex.value)); + ref.read(currentAssetProvider.notifier).set(renderList.loadAsset(assetIndex.value)); } return isDeleted; } @@ -111,12 +98,7 @@ class BottomGalleryBar extends ConsumerWidget { if (isDeleted) { // Can only trash assets stored in server. Local assets are always permanently removed for now if (context.mounted && asset.isRemote && isStackPrimaryAsset) { - ImmichToast.show( - durationInSecond: 1, - context: context, - msg: 'Asset trashed', - gravity: ToastGravity.BOTTOM, - ); + ImmichToast.show(durationInSecond: 1, context: context, msg: 'Asset trashed', gravity: ToastGravity.BOTTOM); } removeAssetFromStack(); } @@ -144,9 +126,7 @@ class BottomGalleryBar extends ConsumerWidget { return; } - await ref - .read(stackServiceProvider) - .deleteStack(asset.stackId!, stackItems); + await ref.read(stackServiceProvider).deleteStack(asset.stackId!, stackItems); } void showStackActionItems() { @@ -161,19 +141,13 @@ class BottomGalleryBar extends ConsumerWidget { mainAxisSize: MainAxisSize.min, children: [ ListTile( - leading: const Icon( - Icons.filter_none_outlined, - size: 18, - ), + leading: const Icon(Icons.filter_none_outlined, size: 18), onTap: () async { await unStack(); ctx.pop(); context.maybePop(); }, - title: const Text( - "viewer_unstack", - style: TextStyle(fontWeight: FontWeight.bold), - ).tr(), + title: const Text("viewer_unstack", style: TextStyle(fontWeight: FontWeight.bold)).tr(), ), ], ), @@ -201,11 +175,7 @@ class BottomGalleryBar extends ConsumerWidget { context.navigator.push( MaterialPageRoute( - builder: (context) => EditImagePage( - asset: asset, - image: image, - isEdited: false, - ), + builder: (context) => EditImagePage(asset: asset, image: image, isEdited: false), ), ); } @@ -233,15 +203,12 @@ class BottomGalleryBar extends ConsumerWidget { return; } - ref.read(downloadStateProvider.notifier).downloadAsset( - asset, - ); + ref.read(downloadStateProvider.notifier).downloadAsset(asset); } handleRemoveFromAlbum() async { final album = ref.read(currentAlbumProvider); - final bool isSuccess = album != null && - await ref.read(albumProvider.notifier).removeAsset(album, [asset]); + final bool isSuccess = album != null && await ref.read(albumProvider.notifier).removeAsset(album, [asset]); if (isSuccess) { // Workaround for asset remaining in the gallery @@ -271,12 +238,11 @@ class BottomGalleryBar extends ConsumerWidget { final List> albumActions = [ { BottomNavigationBarItem( - icon: Icon( - Platform.isAndroid ? Icons.share_rounded : Icons.ios_share_rounded, - ), + icon: Icon(Platform.isAndroid ? Icons.share_rounded : Icons.ios_share_rounded), label: 'share'.tr(), tooltip: 'share'.tr(), - ): (_) => shareAsset(), + ): (_) => + shareAsset(), }, if (asset.isImage && !isInLockedView) { @@ -284,7 +250,8 @@ class BottomGalleryBar extends ConsumerWidget { icon: const Icon(Icons.tune_outlined), label: 'edit'.tr(), tooltip: 'edit'.tr(), - ): (_) => handleEdit(), + ): (_) => + handleEdit(), }, if (isOwner && !isInLockedView) { @@ -298,7 +265,8 @@ class BottomGalleryBar extends ConsumerWidget { icon: const Icon(Icons.archive_outlined), label: 'archive'.tr(), tooltip: 'archive'.tr(), - ): (_) => handleArchive(), + ): (_) => + handleArchive(), }, if (isOwner && asset.stackCount > 0 && !isInLockedView) { @@ -306,7 +274,8 @@ class BottomGalleryBar extends ConsumerWidget { icon: const Icon(Icons.burst_mode_outlined), label: 'stack'.tr(), tooltip: 'stack'.tr(), - ): (_) => showStackActionItems(), + ): (_) => + showStackActionItems(), }, if (isOwner && !isInAlbum) { @@ -314,7 +283,8 @@ class BottomGalleryBar extends ConsumerWidget { icon: const Icon(Icons.delete_outline), label: 'delete'.tr(), tooltip: 'delete'.tr(), - ): (_) => handleDelete(), + ): (_) => + handleDelete(), }, if (!isOwner) { @@ -322,7 +292,8 @@ class BottomGalleryBar extends ConsumerWidget { icon: const Icon(Icons.download_outlined), label: 'download'.tr(), tooltip: 'download'.tr(), - ): (_) => handleDownload(), + ): (_) => + handleDownload(), }, if (isInAlbum) { @@ -330,7 +301,8 @@ class BottomGalleryBar extends ConsumerWidget { icon: const Icon(Icons.remove_circle_outline), label: 'remove_from_album'.tr(), tooltip: 'remove_from_album'.tr(), - ): (_) => handleRemoveFromAlbum(), + ): (_) => + handleRemoveFromAlbum(), }, ]; return IgnorePointer( @@ -357,25 +329,15 @@ class BottomGalleryBar extends ConsumerWidget { backgroundColor: Colors.transparent, unselectedIconTheme: const IconThemeData(color: Colors.white), selectedIconTheme: const IconThemeData(color: Colors.white), - unselectedLabelStyle: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.w500, - height: 2.3, - ), - selectedLabelStyle: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.w500, - height: 2.3, - ), + unselectedLabelStyle: const TextStyle(color: Colors.white, fontWeight: FontWeight.w500, height: 2.3), + selectedLabelStyle: const TextStyle(color: Colors.white, fontWeight: FontWeight.w500, height: 2.3), unselectedFontSize: 14, selectedFontSize: 14, selectedItemColor: Colors.white, unselectedItemColor: Colors.white, showSelectedLabels: true, showUnselectedLabels: true, - items: albumActions - .map((e) => e.keys.first) - .toList(growable: false), + items: albumActions.map((e) => e.keys.first).toList(growable: false), onTap: (index) { albumActions[index].values.first.call(index); }, diff --git a/mobile/lib/widgets/asset_viewer/cast_dialog.dart b/mobile/lib/widgets/asset_viewer/cast_dialog.dart index 9043ea4bea..4db1d9bb69 100644 --- a/mobile/lib/widgets/asset_viewer/cast_dialog.dart +++ b/mobile/lib/widgets/asset_viewer/cast_dialog.dart @@ -21,10 +21,7 @@ class CastDialog extends ConsumerWidget { } return AlertDialog( - title: const Text( - "cast", - style: TextStyle(fontWeight: FontWeight.bold), - ).tr(), + title: const Text("cast", style: TextStyle(fontWeight: FontWeight.bold)).tr(), content: SizedBox( width: 250, height: 250, @@ -32,27 +29,18 @@ class CastDialog extends ConsumerWidget { future: ref.read(castProvider.notifier).getDevices(), builder: (context, snapshot) { if (snapshot.hasError) { - return Text( - 'Error: ${snapshot.error.toString()}', - ); + return Text('Error: ${snapshot.error.toString()}'); } else if (!snapshot.hasData) { - return const SizedBox( - height: 48, - child: Center(child: CircularProgressIndicator()), - ); + return const SizedBox(height: 48, child: Center(child: CircularProgressIndicator())); } if (snapshot.data!.isEmpty) { - return const Text( - 'no_cast_devices_found', - ).tr(); + return const Text('no_cast_devices_found').tr(); } final devices = snapshot.data!; - final connected = - devices.where((d) => isCurrentDevice(d.$1)).toList(); - final others = - devices.where((d) => !isCurrentDevice(d.$1)).toList(); + final connected = devices.where((d) => isCurrentDevice(d.$1)).toList(); + final others = devices.where((d) => !isCurrentDevice(d.$1)).toList(); final List sectionedList = []; @@ -76,40 +64,25 @@ class CastDialog extends ConsumerWidget { // It's a section header return Padding( padding: const EdgeInsets.symmetric(vertical: 8.0), - child: Text( - item, - style: const TextStyle( - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ).tr(), + child: Text(item, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16)).tr(), ); } else { - final (deviceName, type, deviceObj) = - item as (String, CastDestinationType, dynamic); + final (deviceName, type, deviceObj) = item as (String, CastDestinationType, dynamic); return ListTile( title: Text( deviceName, - style: TextStyle( - color: isCurrentDevice(deviceName) - ? context.colorScheme.primary - : null, - ), + style: TextStyle(color: isCurrentDevice(deviceName) ? context.colorScheme.primary : null), ), leading: Icon( - type == CastDestinationType.googleCast - ? Icons.cast - : Icons.cast_connected, - color: isCurrentDevice(deviceName) - ? context.colorScheme.primary - : null, + type == CastDestinationType.googleCast ? Icons.cast : Icons.cast_connected, + color: isCurrentDevice(deviceName) ? context.colorScheme.primary : null, ), trailing: isCurrentDevice(deviceName) ? Icon(Icons.check, color: context.colorScheme.primary) : isDeviceConnecting(deviceName) - ? const CircularProgressIndicator() - : null, + ? const CircularProgressIndicator() + : null, onTap: () async { if (isDeviceConnecting(deviceName)) { return; @@ -120,9 +93,7 @@ class CastDialog extends ConsumerWidget { } if (!isCurrentDevice(deviceName)) { - ref - .read(castProvider.notifier) - .connect(type, deviceObj); + ref.read(castProvider.notifier).connect(type, deviceObj); } }, ); @@ -138,20 +109,14 @@ class CastDialog extends ConsumerWidget { onPressed: () => ref.read(castProvider.notifier).disconnect(), child: Text( "stop_casting", - style: TextStyle( - color: context.colorScheme.secondary, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: context.colorScheme.secondary, fontWeight: FontWeight.bold), ).tr(), ), TextButton( onPressed: () => context.pop(), child: Text( "close", - style: TextStyle( - color: context.colorScheme.primary, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: context.colorScheme.primary, fontWeight: FontWeight.bold), ).tr(), ), ], diff --git a/mobile/lib/widgets/asset_viewer/center_play_button.dart b/mobile/lib/widgets/asset_viewer/center_play_button.dart index 6d7aead9d1..26d0a41129 100644 --- a/mobile/lib/widgets/asset_viewer/center_play_button.dart +++ b/mobile/lib/widgets/asset_viewer/center_play_button.dart @@ -29,19 +29,13 @@ class CenterPlayButton extends StatelessWidget { opacity: show ? 1.0 : 0.0, duration: const Duration(milliseconds: 100), child: DecoratedBox( - decoration: BoxDecoration( - color: backgroundColor, - shape: BoxShape.circle, - ), + decoration: BoxDecoration(color: backgroundColor, shape: BoxShape.circle), child: IconButton( iconSize: 32, padding: const EdgeInsets.all(12.0), icon: isFinished ? Icon(Icons.replay, color: iconColor) - : AnimatedPlayPause( - color: iconColor, - playing: isPlaying, - ), + : AnimatedPlayPause(color: iconColor, playing: isPlaying), onPressed: onPressed, ), ), diff --git a/mobile/lib/widgets/asset_viewer/custom_video_player_controls.dart b/mobile/lib/widgets/asset_viewer/custom_video_player_controls.dart index 18565c8332..0e766c77b9 100644 --- a/mobile/lib/widgets/asset_viewer/custom_video_player_controls.dart +++ b/mobile/lib/widgets/asset_viewer/custom_video_player_controls.dart @@ -13,41 +13,29 @@ import 'package:immich_mobile/widgets/common/delayed_loading_indicator.dart'; class CustomVideoPlayerControls extends HookConsumerWidget { final Duration hideTimerDuration; - const CustomVideoPlayerControls({ - super.key, - this.hideTimerDuration = const Duration(seconds: 5), - }); + const CustomVideoPlayerControls({super.key, this.hideTimerDuration = const Duration(seconds: 5)}); @override Widget build(BuildContext context, WidgetRef ref) { - final assetIsVideo = ref.watch( - currentAssetProvider.select((asset) => asset != null && asset.isVideo), - ); + final assetIsVideo = ref.watch(currentAssetProvider.select((asset) => asset != null && asset.isVideo)); final showControls = ref.watch(showControlsProvider); - final VideoPlaybackState state = - ref.watch(videoPlaybackValueProvider.select((value) => value.state)); + final VideoPlaybackState state = ref.watch(videoPlaybackValueProvider.select((value) => value.state)); final cast = ref.watch(castProvider); // A timer to hide the controls - final hideTimer = useTimer( - hideTimerDuration, - () { - if (!context.mounted) { - return; - } - final state = ref.read(videoPlaybackValueProvider).state; + final hideTimer = useTimer(hideTimerDuration, () { + if (!context.mounted) { + return; + } + final state = ref.read(videoPlaybackValueProvider).state; - // Do not hide on paused - if (state != VideoPlaybackState.paused && - state != VideoPlaybackState.completed && - assetIsVideo) { - ref.read(showControlsProvider.notifier).show = false; - } - }, - ); - final showBuffering = - state == VideoPlaybackState.buffering && !cast.isCasting; + // Do not hide on paused + if (state != VideoPlaybackState.paused && state != VideoPlaybackState.completed && assetIsVideo) { + ref.read(showControlsProvider.notifier).show = false; + } + }); + final showBuffering = state == VideoPlaybackState.buffering && !cast.isCasting; /// Shows the controls and starts the timer to hide them void showControlsAndStartHideTimer() { @@ -56,8 +44,7 @@ class CustomVideoPlayerControls extends HookConsumerWidget { } // When we change position, show or hide timer - ref.listen(videoPlayerControlsProvider.select((v) => v.position), - (previous, next) { + ref.listen(videoPlayerControlsProvider.select((v) => v.position), (previous, next) { showControlsAndStartHideTimer(); }); @@ -98,21 +85,16 @@ class CustomVideoPlayerControls extends HookConsumerWidget { child: Stack( children: [ if (showBuffering) - const Center( - child: DelayedLoadingIndicator( - fadeInDuration: Duration(milliseconds: 400), - ), - ) + const Center(child: DelayedLoadingIndicator(fadeInDuration: Duration(milliseconds: 400))) else GestureDetector( - onTap: () => - ref.read(showControlsProvider.notifier).show = false, + onTap: () => ref.read(showControlsProvider.notifier).show = false, child: CenterPlayButton( backgroundColor: Colors.black54, iconColor: Colors.white, isFinished: state == VideoPlaybackState.completed, - isPlaying: state == VideoPlaybackState.playing || - (cast.isCasting && cast.castState == CastState.playing), + isPlaying: + state == VideoPlaybackState.playing || (cast.isCasting && cast.castState == CastState.playing), show: assetIsVideo && showControls, onPressed: togglePlay, ), diff --git a/mobile/lib/widgets/asset_viewer/description_input.dart b/mobile/lib/widgets/asset_viewer/description_input.dart index 3ac60fd613..b0cefd63fa 100644 --- a/mobile/lib/widgets/asset_viewer/description_input.dart +++ b/mobile/lib/widgets/asset_viewer/description_input.dart @@ -14,11 +14,7 @@ import 'package:immich_mobile/widgets/common/immich_toast.dart'; import 'package:logging/logging.dart'; class DescriptionInput extends HookConsumerWidget { - DescriptionInput({ - super.key, - required this.asset, - this.exifInfo, - }); + DescriptionInput({super.key, required this.asset, this.exifInfo}); final Asset asset; final ExifInfo? exifInfo; @@ -37,16 +33,13 @@ class DescriptionInput extends HookConsumerWidget { final hasDescription = useState(false); final isOwner = fastHash(owner?.id ?? '') == asset.ownerId; - useEffect( - () { - assetService.getDescription(asset).then((value) { - controller.text = value; - hasDescription.value = value.isNotEmpty; - }); - return null; - }, - [assetWithExif.value], - ); + useEffect(() { + assetService.getDescription(asset).then((value) { + controller.text = value; + hasDescription.value = value.isNotEmpty; + }); + return null; + }, [assetWithExif.value]); if (!isOwner && !hasDescription.value) { return const SizedBox.shrink(); @@ -55,19 +48,12 @@ class DescriptionInput extends HookConsumerWidget { submitDescription(String description) async { hasError.value = false; try { - await assetService.setDescription( - asset, - description, - ); + await assetService.setDescription(asset, description); controller.text = description; } catch (error, stack) { hasError.value = true; _log.severe("Error updating description", error, stack); - ImmichToast.show( - context: context, - msg: "description_input_submit_error".tr(), - toastType: ToastType.error, - ); + ImmichToast.show(context: context, msg: "description_input_submit_error".tr(), toastType: ToastType.error); } } @@ -80,10 +66,7 @@ class DescriptionInput extends HookConsumerWidget { controller.clear(); isTextEmpty.value = true; }, - icon: Icon( - Icons.cancel_rounded, - color: context.colorScheme.onSurfaceSecondary, - ), + icon: Icon(Icons.cancel_rounded, color: context.colorScheme.onSurfaceSecondary), splashRadius: 10, ); } diff --git a/mobile/lib/widgets/asset_viewer/detail_panel/asset_date_time.dart b/mobile/lib/widgets/asset_viewer/detail_panel/asset_date_time.dart index e29da52280..df8f6593df 100644 --- a/mobile/lib/widgets/asset_viewer/detail_panel/asset_date_time.dart +++ b/mobile/lib/widgets/asset_viewer/detail_panel/asset_date_time.dart @@ -36,18 +36,8 @@ class AssetDateTime extends ConsumerWidget { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - formattedDateTime, - style: context.textTheme.bodyMedium?.copyWith( - fontWeight: FontWeight.w600, - ), - ), - if (asset.isRemote) - IconButton( - onPressed: editDateTime, - icon: const Icon(Icons.edit_outlined), - iconSize: 20, - ), + Text(formattedDateTime, style: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600)), + if (asset.isRemote) IconButton(onPressed: editDateTime, icon: const Icon(Icons.edit_outlined), iconSize: 20), ], ); } diff --git a/mobile/lib/widgets/asset_viewer/detail_panel/asset_details.dart b/mobile/lib/widgets/asset_viewer/detail_panel/asset_details.dart index 59b52344e7..f0f9a2efcb 100644 --- a/mobile/lib/widgets/asset_viewer/detail_panel/asset_details.dart +++ b/mobile/lib/widgets/asset_viewer/detail_panel/asset_details.dart @@ -12,11 +12,7 @@ class AssetDetails extends ConsumerWidget { final Asset asset; final ExifInfo? exifInfo; - const AssetDetails({ - super.key, - required this.asset, - this.exifInfo, - }); + const AssetDetails({super.key, required this.asset, this.exifInfo}); @override Widget build(BuildContext context, WidgetRef ref) { diff --git a/mobile/lib/widgets/asset_viewer/detail_panel/asset_location.dart b/mobile/lib/widgets/asset_viewer/detail_panel/asset_location.dart index 2a9e6f4a24..7ad290c152 100644 --- a/mobile/lib/widgets/asset_viewer/detail_panel/asset_location.dart +++ b/mobile/lib/widgets/asset_viewer/detail_panel/asset_location.dart @@ -11,10 +11,7 @@ import 'package:immich_mobile/widgets/asset_viewer/detail_panel/exif_map.dart'; class AssetLocation extends HookConsumerWidget { final Asset asset; - const AssetLocation({ - super.key, - required this.asset, - }); + const AssetLocation({super.key, required this.asset}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -35,10 +32,7 @@ class AssetLocation extends HookConsumerWidget { leading: const Icon(Icons.location_on), title: Text( "add_a_location", - style: context.textTheme.bodyMedium?.copyWith( - fontWeight: FontWeight.w600, - color: context.primaryColor, - ), + style: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600, color: context.primaryColor), ).tr(), onTap: editLocation, ) @@ -56,10 +50,7 @@ class AssetLocation extends HookConsumerWidget { bool hasLocationName = (cityName != null && stateName != null); return hasLocationName - ? Text( - "$cityName, $stateName", - style: context.textTheme.labelLarge, - ) + ? Text("$cityName, $stateName", style: context.textTheme.labelLarge) : const SizedBox.shrink(); } @@ -79,25 +70,16 @@ class AssetLocation extends HookConsumerWidget { ), ).tr(), if (asset.isRemote) - IconButton( - onPressed: editLocation, - icon: const Icon(Icons.edit_outlined), - iconSize: 20, - ), + IconButton(onPressed: editLocation, icon: const Icon(Icons.edit_outlined), iconSize: 20), ], ), asset.isRemote ? const SizedBox.shrink() : const SizedBox(height: 16), - ExifMap( - exifInfo: exifInfo!, - markerId: asset.remoteId, - ), + ExifMap(exifInfo: exifInfo!, markerId: asset.remoteId), const SizedBox(height: 16), getLocationName(), Text( "${exifInfo.latitude!.toStringAsFixed(4)}, ${exifInfo.longitude!.toStringAsFixed(4)}", - style: context.textTheme.labelMedium?.copyWith( - color: context.textTheme.labelMedium?.color?.withAlpha(150), - ), + style: context.textTheme.labelMedium?.copyWith(color: context.textTheme.labelMedium?.color?.withAlpha(150)), ), ], ), diff --git a/mobile/lib/widgets/asset_viewer/detail_panel/camera_info.dart b/mobile/lib/widgets/asset_viewer/detail_panel/camera_info.dart index aec18c6a16..5ae29d32c7 100644 --- a/mobile/lib/widgets/asset_viewer/detail_panel/camera_info.dart +++ b/mobile/lib/widgets/asset_viewer/detail_panel/camera_info.dart @@ -5,10 +5,7 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; class CameraInfo extends StatelessWidget { final ExifInfo exifInfo; - const CameraInfo({ - super.key, - required this.exifInfo, - }); + const CameraInfo({super.key, required this.exifInfo}); @override Widget build(BuildContext context) { @@ -16,18 +13,9 @@ class CameraInfo extends StatelessWidget { return ListTile( contentPadding: const EdgeInsets.all(0), dense: true, - leading: Icon( - Icons.camera, - color: textColor.withAlpha(200), - ), - title: Text( - "${exifInfo.make} ${exifInfo.model}", - style: context.textTheme.labelLarge, - ), - subtitle: exifInfo.f != null || - exifInfo.exposureSeconds != null || - exifInfo.mm != null || - exifInfo.iso != null + leading: Icon(Icons.camera, color: textColor.withAlpha(200)), + title: Text("${exifInfo.make} ${exifInfo.model}", style: context.textTheme.labelLarge), + subtitle: exifInfo.f != null || exifInfo.exposureSeconds != null || exifInfo.mm != null || exifInfo.iso != null ? Text( "ƒ/${exifInfo.fNumber} ${exifInfo.exposureTime} ${exifInfo.focalLength} mm ISO ${exifInfo.iso ?? ''} ", style: context.textTheme.bodySmall, diff --git a/mobile/lib/widgets/asset_viewer/detail_panel/detail_panel.dart b/mobile/lib/widgets/asset_viewer/detail_panel/detail_panel.dart index 8ad2cdc687..97c9477c97 100644 --- a/mobile/lib/widgets/asset_viewer/detail_panel/detail_panel.dart +++ b/mobile/lib/widgets/asset_viewer/detail_panel/detail_panel.dart @@ -24,9 +24,7 @@ class DetailPanel extends HookConsumerWidget { child: Column( children: [ AssetDateTime(asset: asset), - asset.isRemote - ? DescriptionInput(asset: asset) - : const SizedBox.shrink(), + asset.isRemote ? DescriptionInput(asset: asset) : const SizedBox.shrink(), PeopleInfo(asset: asset), AssetLocation(asset: asset), AssetDetails(asset: asset), diff --git a/mobile/lib/widgets/asset_viewer/detail_panel/exif_map.dart b/mobile/lib/widgets/asset_viewer/detail_panel/exif_map.dart index 7b6325cf2c..04d01194e9 100644 --- a/mobile/lib/widgets/asset_viewer/detail_panel/exif_map.dart +++ b/mobile/lib/widgets/asset_viewer/detail_panel/exif_map.dart @@ -11,12 +11,7 @@ class ExifMap extends StatelessWidget { final String? markerId; final MapCreatedCallback? onMapCreated; - const ExifMap({ - super.key, - required this.exifInfo, - this.markerId = 'marker', - this.onMapCreated, - }); + const ExifMap({super.key, required this.exifInfo, this.markerId = 'marker', this.onMapCreated}); @override Widget build(BuildContext context) { @@ -35,20 +30,13 @@ class ExifMap extends StatelessWidget { Uri uri = Uri( scheme: 'geo', host: '$latitude,$longitude', - queryParameters: { - 'z': '$zoomLevel', - 'q': '$latitude,$longitude', - }, + queryParameters: {'z': '$zoomLevel', 'q': '$latitude,$longitude'}, ); if (await canLaunchUrl(uri)) { return uri; } } else if (Platform.isIOS) { - var params = { - 'll': '$latitude,$longitude', - 'q': '$latitude,$longitude', - 'z': '$zoomLevel', - }; + var params = {'ll': '$latitude,$longitude', 'q': '$latitude,$longitude', 'z': '$zoomLevel'}; Uri uri = Uri.https('maps.apple.com', '/', params); if (await canLaunchUrl(uri)) { return uri; @@ -66,10 +54,7 @@ class ExifMap extends StatelessWidget { return LayoutBuilder( builder: (context, constraints) { return MapThumbnail( - centre: LatLng( - exifInfo.latitude ?? 0, - exifInfo.longitude ?? 0, - ), + centre: LatLng(exifInfo.latitude ?? 0, exifInfo.longitude ?? 0), height: 150, width: constraints.maxWidth, zoom: 12.0, diff --git a/mobile/lib/widgets/asset_viewer/detail_panel/file_info.dart b/mobile/lib/widgets/asset_viewer/detail_panel/file_info.dart index 4af9846cf6..78d9ac1776 100644 --- a/mobile/lib/widgets/asset_viewer/detail_panel/file_info.dart +++ b/mobile/lib/widgets/asset_viewer/detail_panel/file_info.dart @@ -6,10 +6,7 @@ import 'package:immich_mobile/utils/bytes_units.dart'; class FileInfo extends StatelessWidget { final Asset asset; - const FileInfo({ - super.key, - required this.asset, - }); + const FileInfo({super.key, required this.asset}); @override Widget build(BuildContext context) { @@ -17,11 +14,8 @@ class FileInfo extends StatelessWidget { final height = asset.orientatedHeight ?? asset.height; final width = asset.orientatedWidth ?? asset.width; - String resolution = - height != null && width != null ? "$width x $height " : ""; - String fileSize = asset.exifInfo?.fileSize != null - ? formatBytes(asset.exifInfo!.fileSize!) - : ""; + String resolution = height != null && width != null ? "$width x $height " : ""; + String fileSize = asset.exifInfo?.fileSize != null ? formatBytes(asset.exifInfo!.fileSize!) : ""; String text = resolution + fileSize; final imgSizeString = text.isNotEmpty ? text : null; @@ -44,15 +38,9 @@ class FileInfo extends StatelessWidget { return ListTile( contentPadding: const EdgeInsets.all(0), dense: true, - leading: Icon( - Icons.image, - color: textColor.withAlpha(200), - ), + leading: Icon(Icons.image, color: textColor.withAlpha(200)), titleAlignment: ListTileTitleAlignment.center, - title: Text( - title, - style: context.textTheme.labelLarge, - ), + title: Text(title, style: context.textTheme.labelLarge), subtitle: subtitle == null ? null : Text(subtitle), ); } diff --git a/mobile/lib/widgets/asset_viewer/detail_panel/people_info.dart b/mobile/lib/widgets/asset_viewer/detail_panel/people_info.dart index cbb003bd72..b2aa50493c 100644 --- a/mobile/lib/widgets/asset_viewer/detail_panel/people_info.dart +++ b/mobile/lib/widgets/asset_viewer/detail_panel/people_info.dart @@ -18,17 +18,10 @@ class PeopleInfo extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final peopleProvider = - ref.watch(assetPeopleNotifierProvider(asset).notifier); - final people = ref - .watch(assetPeopleNotifierProvider(asset)) - .value - ?.where((p) => !p.isHidden); + final peopleProvider = ref.watch(assetPeopleNotifierProvider(asset).notifier); + final people = ref.watch(assetPeopleNotifierProvider(asset)).value?.where((p) => !p.isHidden); - showPersonNameEditModel( - String personId, - String personName, - ) { + showPersonNameEditModel(String personId, String personName) { return showDialog( context: context, useRootNavigator: false, @@ -41,13 +34,13 @@ class PeopleInfo extends ConsumerWidget { }); } - final curatedPeople = people + final curatedPeople = + people ?.map( (p) => SearchCuratedContent( id: p.id, label: p.name, - subtitle: p.birthDate != null && - p.birthDate!.isBefore(asset.fileCreatedAt) + subtitle: p.birthDate != null && p.birthDate!.isBefore(asset.fileCreatedAt) ? _formatAge(p.birthDate!, asset.fileCreatedAt) : null, ), @@ -56,9 +49,7 @@ class PeopleInfo extends ConsumerWidget { []; return AnimatedCrossFade( - crossFadeState: (people?.isEmpty ?? true) - ? CrossFadeState.showFirst - : CrossFadeState.showSecond, + crossFadeState: (people?.isEmpty ?? true) ? CrossFadeState.showFirst : CrossFadeState.showSecond, duration: const Duration(milliseconds: 200), firstChild: Container(), secondChild: Padding( @@ -85,17 +76,10 @@ class PeopleInfo extends ConsumerWidget { content: curatedPeople, onTap: (content, index) { context - .pushRoute( - PersonResultRoute( - personId: content.id, - personName: content.label, - ), - ) + .pushRoute(PersonResultRoute(personId: content.id, personName: content.label)) .then((_) => peopleProvider.refresh()); }, - onNameTap: (person, index) => { - showPersonNameEditModel(person.id, person.label), - }, + onNameTap: (person, index) => {showPersonNameEditModel(person.id, person.label)}, ), ), ], @@ -109,22 +93,18 @@ class PeopleInfo extends ConsumerWidget { int ageInMonths = _calculateAgeInMonths(birthDate, referenceDate); if (ageInMonths <= 11) { - return "exif_bottom_sheet_person_age_months" - .tr(namedArgs: {'months': ageInMonths.toString()}); + return "exif_bottom_sheet_person_age_months".tr(namedArgs: {'months': ageInMonths.toString()}); } else if (ageInMonths > 12 && ageInMonths <= 23) { - return "exif_bottom_sheet_person_age_year_months" - .tr(namedArgs: {'months': (ageInMonths - 12).toString()}); + return "exif_bottom_sheet_person_age_year_months".tr(namedArgs: {'months': (ageInMonths - 12).toString()}); } else { - return "exif_bottom_sheet_person_age_years" - .tr(namedArgs: {'years': ageInYears.toString()}); + return "exif_bottom_sheet_person_age_years".tr(namedArgs: {'years': ageInYears.toString()}); } } int _calculateAge(DateTime birthDate, DateTime referenceDate) { int age = referenceDate.year - birthDate.year; if (referenceDate.month < birthDate.month || - (referenceDate.month == birthDate.month && - referenceDate.day < birthDate.day)) { + (referenceDate.month == birthDate.month && referenceDate.day < birthDate.day)) { age--; } return age; diff --git a/mobile/lib/widgets/asset_viewer/formatted_duration.dart b/mobile/lib/widgets/asset_viewer/formatted_duration.dart index d18dc92575..fbcc8e6482 100644 --- a/mobile/lib/widgets/asset_viewer/formatted_duration.dart +++ b/mobile/lib/widgets/asset_viewer/formatted_duration.dart @@ -11,11 +11,7 @@ class FormattedDuration extends StatelessWidget { width: data.inHours > 0 ? 70 : 60, // use a fixed width to prevent jitter child: Text( data.format(), - style: const TextStyle( - fontSize: 14.0, - color: Colors.white, - fontWeight: FontWeight.w500, - ), + style: const TextStyle(fontSize: 14.0, color: Colors.white, fontWeight: FontWeight.w500), textAlign: TextAlign.center, ), ); diff --git a/mobile/lib/widgets/asset_viewer/gallery_app_bar.dart b/mobile/lib/widgets/asset_viewer/gallery_app_bar.dart index f47e73ea78..dcb0334801 100644 --- a/mobile/lib/widgets/asset_viewer/gallery_app_bar.dart +++ b/mobile/lib/widgets/asset_viewer/gallery_app_bar.dart @@ -34,17 +34,12 @@ class GalleryAppBar extends ConsumerWidget { return const SizedBox(); } final album = ref.watch(currentAlbumProvider); - final isOwner = - asset.ownerId == fastHash(ref.watch(currentUserProvider)?.id ?? ''); + final isOwner = asset.ownerId == fastHash(ref.watch(currentUserProvider)?.id ?? ''); final showControls = ref.watch(showControlsProvider); - final isPartner = ref - .watch(partnerSharedWithProvider) - .map((e) => fastHash(e.id)) - .contains(asset.ownerId); + final isPartner = ref.watch(partnerSharedWithProvider).map((e) => fastHash(e.id)).contains(asset.ownerId); - toggleFavorite(Asset asset) => - ref.read(assetProvider.notifier).toggleFavorite([asset]); + toggleFavorite(Asset asset) => ref.read(assetProvider.notifier).toggleFavorite([asset]); handleActivities() { if (album != null && album.shared && album.remoteId != null) { @@ -53,15 +48,10 @@ class GalleryAppBar extends ConsumerWidget { } handleRestore(Asset asset) async { - final result = - await ref.read(trashProvider.notifier).restoreAssets([asset]); + final result = await ref.read(trashProvider.notifier).restoreAssets([asset]); if (result && context.mounted) { - ImmichToast.show( - context: context, - msg: 'asset_restored_successfully'.tr(), - gravity: ToastGravity.BOTTOM, - ); + ImmichToast.show(context: context, msg: 'asset_restored_successfully'.tr(), gravity: ToastGravity.BOTTOM); } } @@ -71,9 +61,7 @@ class GalleryAppBar extends ConsumerWidget { builder: (BuildContext _) { return UploadDialog( onUpload: () { - ref - .read(manualUploadProvider.notifier) - .uploadAssets(context, [asset]); + ref.read(manualUploadProvider.notifier).uploadAssets(context, [asset]); }, ); }, @@ -83,16 +71,10 @@ class GalleryAppBar extends ConsumerWidget { addToAlbum(Asset addToAlbumAsset) { showModalBottomSheet( elevation: 0, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(15.0), - ), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(15.0))), context: context, builder: (BuildContext _) { - return AddToAlbumBottomSheet( - assets: [addToAlbumAsset], - ); + return AddToAlbumBottomSheet(assets: [addToAlbumAsset]); }, ); } @@ -104,8 +86,7 @@ class GalleryAppBar extends ConsumerWidget { handleLocateAsset() async { // Go back to the gallery await context.maybePop(); - await context - .navigateTo(const TabControllerRoute(children: [PhotosRoute()])); + await context.navigateTo(const TabControllerRoute(children: [PhotosRoute()])); ref.read(tabProvider.notifier).update((state) => state = TabEnum.home); // Scroll to the asset's date scrollToDateNotifierProvider.scrollToDate(asset.fileCreatedAt); diff --git a/mobile/lib/widgets/asset_viewer/top_control_app_bar.dart b/mobile/lib/widgets/asset_viewer/top_control_app_bar.dart index a868aff617..35f3840797 100644 --- a/mobile/lib/widgets/asset_viewer/top_control_app_bar.dart +++ b/mobile/lib/widgets/asset_viewer/top_control_app_bar.dart @@ -49,22 +49,16 @@ class TopControlAppBar extends HookConsumerWidget { final a = ref.watch(assetWatcher(asset)).value ?? asset; final album = ref.watch(currentAlbumProvider); final isCasting = ref.watch(castProvider.select((c) => c.isCasting)); - final websocketConnected = - ref.watch(websocketProvider.select((c) => c.isConnected)); + final websocketConnected = ref.watch(websocketProvider.select((c) => c.isConnected)); - final comments = album != null && - album.remoteId != null && - asset.remoteId != null + final comments = album != null && album.remoteId != null && asset.remoteId != null ? ref.watch(activityStatisticsProvider(album.remoteId!, asset.remoteId)) : 0; Widget buildFavoriteButton(a) { return IconButton( onPressed: () => onFavorite(a), - icon: Icon( - a.isFavorite ? Icons.favorite : Icons.favorite_border, - color: Colors.grey[200], - ), + icon: Icon(a.isFavorite ? Icons.favorite : Icons.favorite_border, color: Colors.grey[200]), ); } @@ -73,10 +67,7 @@ class TopControlAppBar extends HookConsumerWidget { onPressed: () { onLocatePressed(); }, - icon: Icon( - Icons.image_search, - color: Colors.grey[200], - ), + icon: Icon(Icons.image_search, color: Colors.grey[200]), ); } @@ -85,20 +76,14 @@ class TopControlAppBar extends HookConsumerWidget { onPressed: () { onMoreInfoPressed(); }, - icon: Icon( - Icons.info_outline_rounded, - color: Colors.grey[200], - ), + icon: Icon(Icons.info_outline_rounded, color: Colors.grey[200]), ); } Widget buildDownloadButton() { return IconButton( onPressed: onDownloadPressed, - icon: Icon( - Icons.cloud_download_outlined, - color: Colors.grey[200], - ), + icon: Icon(Icons.cloud_download_outlined, color: Colors.grey[200]), ); } @@ -107,10 +92,7 @@ class TopControlAppBar extends HookConsumerWidget { onPressed: () { onAddToAlbumPressed(); }, - icon: Icon( - Icons.add, - color: Colors.grey[200], - ), + icon: Icon(Icons.add, color: Colors.grey[200]), ); } @@ -119,10 +101,7 @@ class TopControlAppBar extends HookConsumerWidget { onPressed: () { onRestorePressed(); }, - icon: Icon( - Icons.history_rounded, - color: Colors.grey[200], - ), + icon: Icon(Icons.history_rounded, color: Colors.grey[200]), ); } @@ -134,19 +113,13 @@ class TopControlAppBar extends HookConsumerWidget { icon: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ - Icon( - Icons.mode_comment_outlined, - color: Colors.grey[200], - ), + Icon(Icons.mode_comment_outlined, color: Colors.grey[200]), if (comments != 0) Padding( padding: const EdgeInsets.only(left: 5), child: Text( comments.toString(), - style: TextStyle( - fontWeight: FontWeight.bold, - color: Colors.grey[200], - ), + style: TextStyle(fontWeight: FontWeight.bold, color: Colors.grey[200]), ), ), ], @@ -157,10 +130,7 @@ class TopControlAppBar extends HookConsumerWidget { Widget buildUploadButton() { return IconButton( onPressed: onUploadPressed, - icon: Icon( - Icons.backup_outlined, - color: Colors.grey[200], - ), + icon: Icon(Icons.backup_outlined, color: Colors.grey[200]), ); } @@ -169,21 +139,14 @@ class TopControlAppBar extends HookConsumerWidget { onPressed: () { context.maybePop(); }, - icon: Icon( - Icons.arrow_back_ios_new_rounded, - size: 20.0, - color: Colors.grey[200], - ), + icon: Icon(Icons.arrow_back_ios_new_rounded, size: 20.0, color: Colors.grey[200]), ); } Widget buildCastButton() { return IconButton( onPressed: () { - showDialog( - context: context, - builder: (context) => const CastDialog(), - ); + showDialog(context: context, builder: (context) => const CastDialog()); }, icon: Icon( isCasting ? Icons.cast_connected_rounded : Icons.cast_rounded, @@ -204,24 +167,14 @@ class TopControlAppBar extends HookConsumerWidget { shape: const Border(), actions: [ if (asset.isRemote && isOwner) buildFavoriteButton(a), - if (isOwner && - !isInHomePage && - !(isInTrash ?? false) && - !isInLockedView) - buildLocateButton(), + if (isOwner && !isInHomePage && !(isInTrash ?? false) && !isInLockedView) buildLocateButton(), if (asset.livePhotoVideoId != null) const MotionPhotoButton(), if (asset.isLocal && !asset.isRemote) buildUploadButton(), if (asset.isRemote && !asset.isLocal && isOwner) buildDownloadButton(), - if (asset.isRemote && - (isOwner || isPartner) && - !asset.isTrashed && - !isInLockedView) - buildAddToAlbumButton(), - if (isCasting || (asset.isRemote && websocketConnected)) - buildCastButton(), + if (asset.isRemote && (isOwner || isPartner) && !asset.isTrashed && !isInLockedView) buildAddToAlbumButton(), + if (isCasting || (asset.isRemote && websocketConnected)) buildCastButton(), if (asset.isTrashed) buildRestoreButton(), - if (album != null && album.shared && !isInLockedView) - buildActivitiesButton(), + if (album != null && album.shared && !isInLockedView) buildActivitiesButton(), buildMoreInfoButton(), ], ); diff --git a/mobile/lib/widgets/asset_viewer/video_controls.dart b/mobile/lib/widgets/asset_viewer/video_controls.dart index 22aa2b17d1..42f6078478 100644 --- a/mobile/lib/widgets/asset_viewer/video_controls.dart +++ b/mobile/lib/widgets/asset_viewer/video_controls.dart @@ -12,9 +12,6 @@ class VideoControls extends ConsumerWidget { final isPortrait = context.orientation == Orientation.portrait; return isPortrait ? const VideoPosition() - : const Padding( - padding: EdgeInsets.symmetric(horizontal: 60.0), - child: VideoPosition(), - ); + : const Padding(padding: EdgeInsets.symmetric(horizontal: 60.0), child: VideoPosition()); } } diff --git a/mobile/lib/widgets/asset_viewer/video_position.dart b/mobile/lib/widgets/asset_viewer/video_position.dart index 0e90669fe3..c12bb5e682 100644 --- a/mobile/lib/widgets/asset_viewer/video_position.dart +++ b/mobile/lib/widgets/asset_viewer/video_position.dart @@ -17,12 +17,8 @@ class VideoPosition extends HookConsumerWidget { final isCasting = ref.watch(castProvider).isCasting; final (position, duration) = isCasting - ? ref.watch( - castProvider.select((c) => (c.currentTime, c.duration)), - ) - : ref.watch( - videoPlaybackValueProvider.select((v) => (v.position, v.duration)), - ); + ? ref.watch(castProvider.select((c) => (c.currentTime, c.duration))) + : ref.watch(videoPlaybackValueProvider.select((v) => (v.position, v.duration))); final wasPlaying = useRef(true); return duration == Duration.zero @@ -34,28 +30,21 @@ class VideoPosition extends HookConsumerWidget { padding: const EdgeInsets.symmetric(horizontal: 12.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - FormattedDuration(position), - FormattedDuration(duration), - ], + children: [FormattedDuration(position), FormattedDuration(duration)], ), ), Row( children: [ Expanded( child: Slider( - value: min( - position.inMicroseconds / duration.inMicroseconds * 100, - 100, - ), + value: min(position.inMicroseconds / duration.inMicroseconds * 100, 100), min: 0, max: 100, thumbColor: Colors.white, activeColor: Colors.white, inactiveColor: whiteOpacity75, onChangeStart: (value) { - final state = - ref.read(videoPlaybackValueProvider).state; + final state = ref.read(videoPlaybackValueProvider).state; wasPlaying.value = state != VideoPlaybackState.paused; ref.read(videoPlayerControlsProvider.notifier).pause(); }, @@ -68,19 +57,14 @@ class VideoPosition extends HookConsumerWidget { final seekToDuration = (duration * (value / 100.0)); if (isCasting) { - ref - .read(castProvider.notifier) - .seekTo(seekToDuration); + ref.read(castProvider.notifier).seekTo(seekToDuration); return; } - ref - .read(videoPlayerControlsProvider.notifier) - .position = seekToDuration.inSeconds.toDouble(); + ref.read(videoPlayerControlsProvider.notifier).position = seekToDuration.inSeconds.toDouble(); // This immediately updates the slider position without waiting for the video to update - ref.read(videoPlaybackValueProvider.notifier).position = - seekToDuration; + ref.read(videoPlaybackValueProvider.notifier).position = seekToDuration; }, ), ), @@ -104,10 +88,7 @@ class _VideoPositionPlaceholder extends StatelessWidget { padding: EdgeInsets.symmetric(horizontal: 12.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - FormattedDuration(Duration.zero), - FormattedDuration(Duration.zero), - ], + children: [FormattedDuration(Duration.zero), FormattedDuration(Duration.zero)], ), ), Row( diff --git a/mobile/lib/widgets/backup/album_info_card.dart b/mobile/lib/widgets/backup/album_info_card.dart index fa113c6291..d635e136bc 100644 --- a/mobile/lib/widgets/backup/album_info_card.dart +++ b/mobile/lib/widgets/backup/album_info_card.dart @@ -16,39 +16,25 @@ import 'package:immich_mobile/widgets/common/immich_toast.dart'; class AlbumInfoCard extends HookConsumerWidget { final AvailableAlbum album; - const AlbumInfoCard({ - super.key, - required this.album, - }); + const AlbumInfoCard({super.key, required this.album}); @override Widget build(BuildContext context, WidgetRef ref) { - final bool isSelected = - ref.watch(backupProvider).selectedBackupAlbums.contains(album); - final bool isExcluded = - ref.watch(backupProvider).excludedBackupAlbums.contains(album); - final syncAlbum = ref - .watch(appSettingsServiceProvider) - .getSetting(AppSettingsEnum.syncAlbums); + final bool isSelected = ref.watch(backupProvider).selectedBackupAlbums.contains(album); + final bool isExcluded = ref.watch(backupProvider).excludedBackupAlbums.contains(album); + final syncAlbum = ref.watch(appSettingsServiceProvider).getSetting(AppSettingsEnum.syncAlbums); final isDarkTheme = context.isDarkTheme; - ColorFilter selectedFilter = ColorFilter.mode( - context.primaryColor.withAlpha(100), - BlendMode.darken, - ); - ColorFilter excludedFilter = - ColorFilter.mode(Colors.red.withAlpha(75), BlendMode.darken); - ColorFilter unselectedFilter = - const ColorFilter.mode(Colors.black, BlendMode.color); + ColorFilter selectedFilter = ColorFilter.mode(context.primaryColor.withAlpha(100), BlendMode.darken); + ColorFilter excludedFilter = ColorFilter.mode(Colors.red.withAlpha(75), BlendMode.darken); + ColorFilter unselectedFilter = const ColorFilter.mode(Colors.black, BlendMode.color); buildSelectedTextBox() { if (isSelected) { return Chip( visualDensity: VisualDensity.compact, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(5)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))), label: Text( "album_info_card_backup_album_included", style: TextStyle( @@ -62,9 +48,7 @@ class AlbumInfoCard extends HookConsumerWidget { } else if (isExcluded) { return Chip( visualDensity: VisualDensity.compact, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(5)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))), label: Text( "album_info_card_backup_album_excluded", style: TextStyle( @@ -133,9 +117,7 @@ class AlbumInfoCard extends HookConsumerWidget { Radius.circular(12), // if you need this ), side: BorderSide( - color: isDarkTheme - ? const Color.fromARGB(255, 37, 35, 35) - : const Color(0xFFC9C9C9), + color: isDarkTheme ? const Color.fromARGB(255, 37, 35, 35) : const Color(0xFFC9C9C9), width: 1, ), ), @@ -153,24 +135,16 @@ class AlbumInfoCard extends HookConsumerWidget { child: const Image( width: double.infinity, height: double.infinity, - image: AssetImage( - 'assets/immich-logo.png', - ), + image: AssetImage('assets/immich-logo.png'), fit: BoxFit.cover, ), ), - Positioned( - bottom: 10, - right: 25, - child: buildSelectedTextBox(), - ), + Positioned(bottom: 10, right: 25, child: buildSelectedTextBox()), ], ), ), Padding( - padding: const EdgeInsets.only( - left: 25, - ), + padding: const EdgeInsets.only(left: 25), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ @@ -181,21 +155,13 @@ class AlbumInfoCard extends HookConsumerWidget { children: [ Text( album.name, - style: TextStyle( - fontSize: 14, - color: context.primaryColor, - fontWeight: FontWeight.bold, - ), + style: TextStyle(fontSize: 14, color: context.primaryColor, fontWeight: FontWeight.bold), ), Padding( padding: const EdgeInsets.only(top: 2.0), child: Text( - album.assetCount.toString() + - (album.isAll ? " (${'all'.tr()})" : ""), - style: TextStyle( - fontSize: 12, - color: Colors.grey[600], - ), + album.assetCount.toString() + (album.isAll ? " (${'all'.tr()})" : ""), + style: TextStyle(fontSize: 12, color: Colors.grey[600]), ), ), ], @@ -203,15 +169,9 @@ class AlbumInfoCard extends HookConsumerWidget { ), IconButton( onPressed: () { - context.pushRoute( - AlbumPreviewRoute(album: album.album), - ); + context.pushRoute(AlbumPreviewRoute(album: album.album)); }, - icon: Icon( - Icons.image_outlined, - color: context.primaryColor, - size: 24, - ), + icon: Icon(Icons.image_outlined, color: context.primaryColor, size: 24), splashRadius: 25, ), ], diff --git a/mobile/lib/widgets/backup/album_info_list_tile.dart b/mobile/lib/widgets/backup/album_info_list_tile.dart index a263c004bd..9796f45e8b 100644 --- a/mobile/lib/widgets/backup/album_info_list_tile.dart +++ b/mobile/lib/widgets/backup/album_info_list_tile.dart @@ -19,23 +19,15 @@ class AlbumInfoListTile extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final bool isSelected = - ref.watch(backupProvider).selectedBackupAlbums.contains(album); - final bool isExcluded = - ref.watch(backupProvider).excludedBackupAlbums.contains(album); - final syncAlbum = ref - .watch(appSettingsServiceProvider) - .getSetting(AppSettingsEnum.syncAlbums); + final bool isSelected = ref.watch(backupProvider).selectedBackupAlbums.contains(album); + final bool isExcluded = ref.watch(backupProvider).excludedBackupAlbums.contains(album); + final syncAlbum = ref.watch(appSettingsServiceProvider).getSetting(AppSettingsEnum.syncAlbums); buildTileColor() { if (isSelected) { - return context.isDarkTheme - ? context.primaryColor.withAlpha(100) - : context.primaryColor.withAlpha(25); + return context.isDarkTheme ? context.primaryColor.withAlpha(100) : context.primaryColor.withAlpha(25); } else if (isExcluded) { - return context.isDarkTheme - ? Colors.red[300]?.withAlpha(150) - : Colors.red[100]?.withAlpha(150); + return context.isDarkTheme ? Colors.red[300]?.withAlpha(150) : Colors.red[100]?.withAlpha(150); } else { return Colors.transparent; } @@ -43,23 +35,14 @@ class AlbumInfoListTile extends HookConsumerWidget { buildIcon() { if (isSelected) { - return Icon( - Icons.check_circle_rounded, - color: context.colorScheme.primary, - ); + return Icon(Icons.check_circle_rounded, color: context.colorScheme.primary); } if (isExcluded) { - return Icon( - Icons.remove_circle_rounded, - color: context.colorScheme.error, - ); + return Icon(Icons.remove_circle_rounded, color: context.colorScheme.error); } - return Icon( - Icons.circle, - color: context.colorScheme.surfaceContainerHighest, - ); + return Icon(Icons.circle, color: context.colorScheme.surfaceContainerHighest); } return GestureDetector( @@ -100,25 +83,13 @@ class AlbumInfoListTile extends HookConsumerWidget { } }, leading: buildIcon(), - title: Text( - album.name, - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - ), - ), + title: Text(album.name, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), subtitle: Text(album.assetCount.toString()), trailing: IconButton( onPressed: () { - context.pushRoute( - AlbumPreviewRoute(album: album.album), - ); + context.pushRoute(AlbumPreviewRoute(album: album.album)); }, - icon: Icon( - Icons.image_outlined, - color: context.primaryColor, - size: 24, - ), + icon: Icon(Icons.image_outlined, color: context.primaryColor, size: 24), splashRadius: 25, ), ), diff --git a/mobile/lib/widgets/backup/asset_info_table.dart b/mobile/lib/widgets/backup/asset_info_table.dart index 98bcc2b3da..2cccded2bb 100644 --- a/mobile/lib/widgets/backup/asset_info_table.dart +++ b/mobile/lib/widgets/backup/asset_info_table.dart @@ -14,9 +14,7 @@ class BackupAssetInfoTable extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final isManualUpload = ref.watch( - backupProvider.select( - (value) => value.backupProgress == BackUpProgressEnum.manualInProgress, - ), + backupProvider.select((value) => value.backupProgress == BackUpProgressEnum.manualInProgress), ); final isUploadInProgress = ref.watch( @@ -29,18 +27,13 @@ class BackupAssetInfoTable extends ConsumerWidget { ); final asset = isManualUpload - ? ref.watch( - manualUploadProvider.select((value) => value.currentUploadAsset), - ) + ? ref.watch(manualUploadProvider.select((value) => value.currentUploadAsset)) : ref.watch(backupProvider.select((value) => value.currentUploadAsset)); return Padding( padding: const EdgeInsets.only(top: 8.0), child: Table( - border: TableBorder.all( - color: context.colorScheme.outlineVariant, - width: 1, - ), + border: TableBorder.all(color: context.colorScheme.outlineVariant, width: 1), children: [ TableRow( children: [ @@ -48,21 +41,19 @@ class BackupAssetInfoTable extends ConsumerWidget { verticalAlignment: TableCellVerticalAlignment.middle, child: Padding( padding: const EdgeInsets.all(6.0), - child: Text( - 'backup_controller_page_filename', - style: TextStyle( - color: context.colorScheme.onSurfaceSecondary, - fontWeight: FontWeight.bold, - fontSize: 10.0, - ), - ).tr( - namedArgs: isUploadInProgress - ? { - 'filename': asset.fileName, - 'size': asset.fileType.toLowerCase(), - } - : {'filename': "-", 'size': "-"}, - ), + child: + Text( + 'backup_controller_page_filename', + style: TextStyle( + color: context.colorScheme.onSurfaceSecondary, + fontWeight: FontWeight.bold, + fontSize: 10.0, + ), + ).tr( + namedArgs: isUploadInProgress + ? {'filename': asset.fileName, 'size': asset.fileType.toLowerCase()} + : {'filename': "-", 'size': "-"}, + ), ), ), ], @@ -80,13 +71,7 @@ class BackupAssetInfoTable extends ConsumerWidget { fontWeight: FontWeight.bold, fontSize: 10.0, ), - ).tr( - namedArgs: { - 'date': isUploadInProgress - ? _getAssetCreationDate(asset) - : "-", - }, - ), + ).tr(namedArgs: {'date': isUploadInProgress ? _getAssetCreationDate(asset) : "-"}), ), ), ], @@ -103,9 +88,7 @@ class BackupAssetInfoTable extends ConsumerWidget { fontWeight: FontWeight.bold, fontSize: 10.0, ), - ).tr( - namedArgs: {'id': isUploadInProgress ? asset.id : "-"}, - ), + ).tr(namedArgs: {'id': isUploadInProgress ? asset.id : "-"}), ), ), ], diff --git a/mobile/lib/widgets/backup/backup_info_card.dart b/mobile/lib/widgets/backup/backup_info_card.dart index 54551da35a..767d94bbf9 100644 --- a/mobile/lib/widgets/backup/backup_info_card.dart +++ b/mobile/lib/widgets/backup/backup_info_card.dart @@ -7,12 +7,7 @@ class BackupInfoCard extends StatelessWidget { final String title; final String subtitle; final String info; - const BackupInfoCard({ - super.key, - required this.title, - required this.subtitle, - required this.info, - }); + const BackupInfoCard({super.key, required this.title, required this.subtitle, required this.info}); @override Widget build(BuildContext context) { @@ -21,40 +16,26 @@ class BackupInfoCard extends StatelessWidget { borderRadius: const BorderRadius.all( Radius.circular(20), // if you need this ), - side: BorderSide( - color: context.colorScheme.outlineVariant, - width: 1, - ), + side: BorderSide(color: context.colorScheme.outlineVariant, width: 1), ), elevation: 0, borderOnForeground: false, child: ListTile( minVerticalPadding: 18, isThreeLine: true, - title: Text( - title, - style: context.textTheme.titleMedium, - ), + title: Text(title, style: context.textTheme.titleMedium), subtitle: Padding( padding: const EdgeInsets.only(top: 4.0, right: 18.0), child: Text( subtitle, - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), ), trailing: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Text( - info, - style: context.textTheme.titleLarge, - ), - Text( - "backup_info_card_assets", - style: context.textTheme.labelLarge, - ).tr(), + Text(info, style: context.textTheme.titleLarge), + Text("backup_info_card_assets", style: context.textTheme.labelLarge).tr(), ], ), ), diff --git a/mobile/lib/widgets/backup/current_backup_asset_info_box.dart b/mobile/lib/widgets/backup/current_backup_asset_info_box.dart index b6d0edb200..c2f94e706a 100644 --- a/mobile/lib/widgets/backup/current_backup_asset_info_box.dart +++ b/mobile/lib/widgets/backup/current_backup_asset_info_box.dart @@ -16,18 +16,11 @@ class CurrentUploadingAssetInfoBox extends StatelessWidget { Widget build(BuildContext context) { return ListTile( isThreeLine: true, - leading: Icon( - Icons.image_outlined, - color: context.primaryColor, - size: 30, - ), + leading: Icon(Icons.image_outlined, color: context.primaryColor, size: 30), title: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - "backup_controller_page_uploading_file_info", - style: context.textTheme.titleSmall, - ).tr(), + Text("backup_controller_page_uploading_file_info", style: context.textTheme.titleSmall).tr(), const BackupErrorChip(), ], ), diff --git a/mobile/lib/widgets/backup/drift_album_info_list_tile.dart b/mobile/lib/widgets/backup/drift_album_info_list_tile.dart new file mode 100644 index 0000000000..cc3485e6f9 --- /dev/null +++ b/mobile/lib/widgets/backup/drift_album_info_list_tile.dart @@ -0,0 +1,96 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:hooks_riverpod/hooks_riverpod.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/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/haptic_feedback.provider.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'; + +class DriftAlbumInfoListTile extends HookConsumerWidget { + final LocalAlbum album; + + const DriftAlbumInfoListTile({super.key, required this.album}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final bool isSelected = album.backupSelection == BackupSelection.selected; + final bool isExcluded = album.backupSelection == BackupSelection.excluded; + + final syncAlbum = ref.watch(appSettingsServiceProvider).getSetting(AppSettingsEnum.syncAlbums); + + buildTileColor() { + if (isSelected) { + return context.isDarkTheme ? context.primaryColor.withAlpha(100) : context.primaryColor.withAlpha(25); + } else if (isExcluded) { + return context.isDarkTheme ? Colors.red[300]?.withAlpha(150) : Colors.red[100]?.withAlpha(150); + } else { + return Colors.transparent; + } + } + + buildIcon() { + if (isSelected) { + return Icon(Icons.check_circle_rounded, color: context.colorScheme.primary); + } + + if (isExcluded) { + return Icon(Icons.remove_circle_rounded, color: context.colorScheme.error); + } + + return Icon(Icons.circle, color: context.colorScheme.surfaceContainerHighest); + } + + return GestureDetector( + onDoubleTap: () { + ref.watch(hapticFeedbackProvider.notifier).selectionClick(); + + if (isExcluded) { + ref.read(backupAlbumProvider.notifier).deselectAlbum(album); + } else { + if (album.id == 'isAll' || album.name == 'Recents') { + ImmichToast.show( + context: context, + msg: 'Cannot exclude album contains all assets', + toastType: ToastType.error, + gravity: ToastGravity.BOTTOM, + ); + return; + } + + ref.read(backupAlbumProvider.notifier).excludeAlbum(album); + } + }, + child: ListTile( + tileColor: buildTileColor(), + contentPadding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16), + onTap: () { + ref.read(hapticFeedbackProvider.notifier).selectionClick(); + if (isSelected) { + ref.read(backupAlbumProvider.notifier).deselectAlbum(album); + } else { + ref.read(backupAlbumProvider.notifier).selectAlbum(album); + if (syncAlbum) { + ref.read(albumProvider.notifier).createSyncAlbum(album.name); + } + } + }, + leading: buildIcon(), + title: Text(album.name, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), + subtitle: Text(album.assetCount.toString()), + trailing: IconButton( + onPressed: () { + context.pushRoute(LocalTimelineRoute(album: album)); + }, + icon: Icon(Icons.image_outlined, color: context.primaryColor, size: 24), + splashRadius: 25, + ), + ), + ); + } +} diff --git a/mobile/lib/widgets/backup/error_chip.dart b/mobile/lib/widgets/backup/error_chip.dart index 4df3e50f64..191049cd75 100644 --- a/mobile/lib/widgets/backup/error_chip.dart +++ b/mobile/lib/widgets/backup/error_chip.dart @@ -11,17 +11,13 @@ class BackupErrorChip extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final hasErrors = - ref.watch(errorBackupListProvider.select((value) => value.isNotEmpty)); + final hasErrors = ref.watch(errorBackupListProvider.select((value) => value.isNotEmpty)); if (!hasErrors) { return const SizedBox(); } return ActionChip( - avatar: const Icon( - Icons.info, - color: red400, - ), + avatar: const Icon(Icons.info, color: red400), elevation: 1, visualDensity: VisualDensity.compact, label: const BackupErrorChipText(), diff --git a/mobile/lib/widgets/backup/error_chip_text.dart b/mobile/lib/widgets/backup/error_chip_text.dart index 38c527ccfa..c987dfd331 100644 --- a/mobile/lib/widgets/backup/error_chip_text.dart +++ b/mobile/lib/widgets/backup/error_chip_text.dart @@ -16,11 +16,7 @@ class BackupErrorChipText extends ConsumerWidget { return const Text( "backup_controller_page_failed", - style: TextStyle( - color: red400, - fontWeight: FontWeight.bold, - fontSize: 11, - ), + style: TextStyle(color: red400, fontWeight: FontWeight.bold, fontSize: 11), ).tr(namedArgs: {'count': count.toString()}); } } diff --git a/mobile/lib/widgets/backup/icloud_download_progress_bar.dart b/mobile/lib/widgets/backup/icloud_download_progress_bar.dart index c61fb1a0d1..9f0f7ec3eb 100644 --- a/mobile/lib/widgets/backup/icloud_download_progress_bar.dart +++ b/mobile/lib/widgets/backup/icloud_download_progress_bar.dart @@ -10,39 +10,24 @@ class IcloudDownloadProgressBar extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final isManualUpload = ref.watch( - backupProvider.select( - (value) => value.backupProgress == BackUpProgressEnum.manualInProgress, - ), + backupProvider.select((value) => value.backupProgress == BackUpProgressEnum.manualInProgress), ); final isIcloudAsset = isManualUpload - ? ref.watch( - manualUploadProvider - .select((value) => value.currentUploadAsset.isIcloudAsset), - ) - : ref.watch( - backupProvider - .select((value) => value.currentUploadAsset.isIcloudAsset), - ); + ? ref.watch(manualUploadProvider.select((value) => value.currentUploadAsset.isIcloudAsset)) + : ref.watch(backupProvider.select((value) => value.currentUploadAsset.isIcloudAsset)); if (!isIcloudAsset) { return const SizedBox(); } - final iCloudDownloadProgress = ref - .watch(backupProvider.select((value) => value.iCloudDownloadProgress)); + final iCloudDownloadProgress = ref.watch(backupProvider.select((value) => value.iCloudDownloadProgress)); return Padding( padding: const EdgeInsets.only(top: 8.0), child: Row( children: [ - SizedBox( - width: 110, - child: Text( - "iCloud Download", - style: context.textTheme.labelSmall, - ), - ), + SizedBox(width: 110, child: Text("iCloud Download", style: context.textTheme.labelSmall)), Expanded( child: LinearProgressIndicator( minHeight: 10.0, @@ -50,10 +35,7 @@ class IcloudDownloadProgressBar extends ConsumerWidget { borderRadius: const BorderRadius.all(Radius.circular(10.0)), ), ), - Text( - " ${iCloudDownloadProgress ~/ 1}%", - style: const TextStyle(fontSize: 12), - ), + Text(" ${iCloudDownloadProgress ~/ 1}%", style: const TextStyle(fontSize: 12)), ], ), ); diff --git a/mobile/lib/widgets/backup/ios_debug_info_tile.dart b/mobile/lib/widgets/backup/ios_debug_info_tile.dart index de80b3bfd1..be333c6460 100644 --- a/mobile/lib/widgets/backup/ios_debug_info_tile.dart +++ b/mobile/lib/widgets/backup/ios_debug_info_tile.dart @@ -9,10 +9,7 @@ import 'package:immich_mobile/providers/backup/ios_background_settings.provider. /// more confident about background sync class IosDebugInfoTile extends HookConsumerWidget { final IOSBackgroundSettings settings; - const IosDebugInfoTile({ - super.key, - required this.settings, - }); + const IosDebugInfoTile({super.key, required this.settings}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -24,8 +21,7 @@ class IosDebugInfoTile extends HookConsumerWidget { if (processes == 0) { title = 'ios_debug_info_no_processes_queued'.t(context: context); } else { - title = 'ios_debug_info_processes_queued' - .t(context: context, args: {'count': processes}); + title = 'ios_debug_info_processes_queued'.t(context: context, args: {'count': processes}); } final df = DateFormat.yMd().add_jm(); @@ -33,39 +29,21 @@ class IosDebugInfoTile extends HookConsumerWidget { if (fetch == null && processing == null) { subtitle = 'ios_debug_info_no_sync_yet'.t(context: context); } else if (fetch != null && processing == null) { - subtitle = 'ios_debug_info_fetch_ran_at' - .t(context: context, args: {'dateTime': df.format(fetch)}); + subtitle = 'ios_debug_info_fetch_ran_at'.t(context: context, args: {'dateTime': df.format(fetch)}); } else if (processing != null && fetch == null) { - subtitle = 'ios_debug_info_processing_ran_at' - .t(context: context, args: {'dateTime': df.format(processing)}); + subtitle = 'ios_debug_info_processing_ran_at'.t(context: context, args: {'dateTime': df.format(processing)}); } else { - final fetchOrProcessing = - fetch!.isAfter(processing!) ? fetch : processing; - subtitle = 'ios_debug_info_last_sync_at'.t( - context: context, - args: {'dateTime': df.format(fetchOrProcessing)}, - ); + final fetchOrProcessing = fetch!.isAfter(processing!) ? fetch : processing; + subtitle = 'ios_debug_info_last_sync_at'.t(context: context, args: {'dateTime': df.format(fetchOrProcessing)}); } return ListTile( title: Text( title, - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 14, - color: context.primaryColor, - ), - ), - subtitle: Text( - subtitle, - style: const TextStyle( - fontSize: 14, - ), - ), - leading: Icon( - Icons.bug_report, - color: context.primaryColor, + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14, color: context.primaryColor), ), + subtitle: Text(subtitle, style: const TextStyle(fontSize: 14)), + leading: Icon(Icons.bug_report, color: context.primaryColor), ); } } diff --git a/mobile/lib/widgets/backup/upload_progress_bar.dart b/mobile/lib/widgets/backup/upload_progress_bar.dart index 9281914d9c..65ff6c758a 100644 --- a/mobile/lib/widgets/backup/upload_progress_bar.dart +++ b/mobile/lib/widgets/backup/upload_progress_bar.dart @@ -11,41 +11,22 @@ class BackupUploadProgressBar extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final isManualUpload = ref.watch( - backupProvider.select( - (value) => value.backupProgress == BackUpProgressEnum.manualInProgress, - ), + backupProvider.select((value) => value.backupProgress == BackUpProgressEnum.manualInProgress), ); final isIcloudAsset = isManualUpload - ? ref.watch( - manualUploadProvider - .select((value) => value.currentUploadAsset.isIcloudAsset), - ) - : ref.watch( - backupProvider - .select((value) => value.currentUploadAsset.isIcloudAsset), - ); + ? ref.watch(manualUploadProvider.select((value) => value.currentUploadAsset.isIcloudAsset)) + : ref.watch(backupProvider.select((value) => value.currentUploadAsset.isIcloudAsset)); final uploadProgress = isManualUpload - ? ref.watch( - manualUploadProvider.select((value) => value.progressInPercentage), - ) - : ref.watch( - backupProvider.select((value) => value.progressInPercentage), - ); + ? ref.watch(manualUploadProvider.select((value) => value.progressInPercentage)) + : ref.watch(backupProvider.select((value) => value.progressInPercentage)); return Padding( padding: const EdgeInsets.only(top: 8.0), child: Row( children: [ - if (isIcloudAsset) - SizedBox( - width: 110, - child: Text( - "Immich Upload", - style: context.textTheme.labelSmall, - ), - ), + if (isIcloudAsset) SizedBox(width: 110, child: Text("Immich Upload", style: context.textTheme.labelSmall)), Expanded( child: LinearProgressIndicator( minHeight: 10.0, diff --git a/mobile/lib/widgets/backup/upload_stats.dart b/mobile/lib/widgets/backup/upload_stats.dart index 965202ce33..c9b626c51c 100644 --- a/mobile/lib/widgets/backup/upload_stats.dart +++ b/mobile/lib/widgets/backup/upload_stats.dart @@ -10,34 +10,23 @@ class BackupUploadStats extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final isManualUpload = ref.watch( - backupProvider.select( - (value) => value.backupProgress == BackUpProgressEnum.manualInProgress, - ), + backupProvider.select((value) => value.backupProgress == BackUpProgressEnum.manualInProgress), ); final uploadFileProgress = isManualUpload - ? ref.watch( - manualUploadProvider.select((value) => value.progressInFileSize), - ) + ? ref.watch(manualUploadProvider.select((value) => value.progressInFileSize)) : ref.watch(backupProvider.select((value) => value.progressInFileSize)); final uploadFileSpeed = isManualUpload - ? ref.watch( - manualUploadProvider.select((value) => value.progressInFileSpeed), - ) - : ref.watch( - backupProvider.select((value) => value.progressInFileSpeed), - ); + ? ref.watch(manualUploadProvider.select((value) => value.progressInFileSpeed)) + : ref.watch(backupProvider.select((value) => value.progressInFileSpeed)); return Padding( padding: const EdgeInsets.only(top: 2.0, bottom: 2.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - uploadFileProgress, - style: const TextStyle(fontSize: 10, fontFamily: "OverpassMono"), - ), + Text(uploadFileProgress, style: const TextStyle(fontSize: 10, fontFamily: "OverpassMono")), Text( _formatUploadFileSpeed(uploadFileSpeed), style: const TextStyle(fontSize: 10, fontFamily: "OverpassMono"), diff --git a/mobile/lib/widgets/common/app_bar_dialog/app_bar_dialog.dart b/mobile/lib/widgets/common/app_bar_dialog/app_bar_dialog.dart index 388f202b6d..ccfc374fef 100644 --- a/mobile/lib/widgets/common/app_bar_dialog/app_bar_dialog.dart +++ b/mobile/lib/widgets/common/app_bar_dialog/app_bar_dialog.dart @@ -34,33 +34,22 @@ class ImmichAppBarDialog extends HookConsumerWidget { final user = ref.watch(currentUserProvider); final isLoggingOut = useState(false); - useEffect( - () { - ref.read(backupProvider.notifier).updateDiskInfo(); - ref.read(currentUserProvider.notifier).refresh(); - return null; - }, - [], - ); + useEffect(() { + ref.read(backupProvider.notifier).updateDiskInfo(); + ref.read(currentUserProvider.notifier).refresh(); + return null; + }, []); buildTopRow() { return Stack( children: [ Align( alignment: Alignment.topLeft, - child: InkWell( - onTap: () => context.pop(), - child: const Icon( - Icons.close, - size: 20, - ), - ), + child: InkWell(onTap: () => context.pop(), child: const Icon(Icons.close, size: 20)), ), Center( child: Image.asset( - context.isDarkTheme - ? 'assets/immich-text-dark.png' - : 'assets/immich-text-light.png', + context.isDarkTheme ? 'assets/immich-text-dark.png' : 'assets/immich-text-light.png', height: 16, ), ), @@ -68,29 +57,16 @@ class ImmichAppBarDialog extends HookConsumerWidget { ); } - buildActionButton( - IconData icon, - String text, - Function() onTap, { - Widget? trailing, - }) { + buildActionButton(IconData icon, String text, Function() onTap, {Widget? trailing}) { return ListTile( dense: true, visualDensity: VisualDensity.standard, contentPadding: const EdgeInsets.only(left: 30, right: 30), minLeadingWidth: 40, - leading: SizedBox( - child: Icon( - icon, - color: theme.textTheme.labelLarge?.color?.withAlpha(250), - size: 20, - ), - ), + leading: SizedBox(child: Icon(icon, color: theme.textTheme.labelLarge?.color?.withAlpha(250), size: 20)), title: Text( text, - style: theme.textTheme.labelLarge?.copyWith( - color: theme.textTheme.labelLarge?.color?.withAlpha(250), - ), + style: theme.textTheme.labelLarge?.copyWith(color: theme.textTheme.labelLarge?.color?.withAlpha(250)), ).tr(), onTap: onTap, trailing: trailing, @@ -98,11 +74,7 @@ class ImmichAppBarDialog extends HookConsumerWidget { } buildSettingButton() { - return buildActionButton( - Icons.settings_outlined, - "settings", - () => context.pushRoute(const SettingsRoute()), - ); + return buildActionButton(Icons.settings_outlined, "settings", () => context.pushRoute(const SettingsRoute())); } buildAppLogButton() { @@ -131,10 +103,7 @@ class ImmichAppBarDialog extends HookConsumerWidget { ok: "yes", onOk: () async { isLoggingOut.value = true; - await ref - .read(authProvider.notifier) - .logout() - .whenComplete(() => isLoggingOut.value = false); + await ref.read(authProvider.notifier).logout().whenComplete(() => isLoggingOut.value = false); ref.read(manualUploadProvider.notifier).cancelBackup(); ref.read(backupProvider.notifier).cancelBackup(); @@ -147,10 +116,7 @@ class ImmichAppBarDialog extends HookConsumerWidget { ); }, trailing: isLoggingOut.value - ? const SizedBox.square( - dimension: 20, - child: CircularProgressIndicator(strokeWidth: 2), - ) + ? const SizedBox.square(dimension: 20, child: CircularProgressIndicator(strokeWidth: 2)) : null, ); } @@ -170,20 +136,13 @@ class ImmichAppBarDialog extends HookConsumerWidget { padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 3), child: Container( padding: const EdgeInsets.symmetric(vertical: 4), - decoration: BoxDecoration( - color: context.colorScheme.surface, - ), + decoration: BoxDecoration(color: context.colorScheme.surface), child: ListTile( minLeadingWidth: 50, - leading: Icon( - Icons.storage_rounded, - color: theme.primaryColor, - ), + leading: Icon(Icons.storage_rounded, color: theme.primaryColor), title: Text( "backup_controller_page_server_storage", - style: context.textTheme.labelLarge?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.labelLarge?.copyWith(fontWeight: FontWeight.w500), ).tr(), isThreeLine: true, subtitle: Padding( @@ -196,19 +155,14 @@ class ImmichAppBarDialog extends HookConsumerWidget { child: LinearProgressIndicator( minHeight: 10.0, value: percentage, - borderRadius: - const BorderRadius.all(Radius.circular(10.0)), + borderRadius: const BorderRadius.all(Radius.circular(10.0)), ), ), Padding( padding: const EdgeInsets.only(top: 12.0), - child: - const Text('backup_controller_page_storage_format').tr( - namedArgs: { - 'used': usedDiskSpace, - 'total': totalDiskSpace, - }, - ), + child: const Text( + 'backup_controller_page_storage_format', + ).tr(namedArgs: {'used': usedDiskSpace, 'total': totalDiskSpace}), ), ], ), @@ -227,43 +181,19 @@ class ImmichAppBarDialog extends HookConsumerWidget { InkWell( onTap: () { context.pop(); - launchUrl( - Uri.parse('https://immich.app'), - mode: LaunchMode.externalApplication, - ); + launchUrl(Uri.parse('https://immich.app'), mode: LaunchMode.externalApplication); }, - child: Text( - "documentation", - style: context.textTheme.bodySmall, - ).tr(), - ), - const SizedBox( - width: 20, - child: Text( - "•", - textAlign: TextAlign.center, - ), + child: Text("documentation", style: context.textTheme.bodySmall).tr(), ), + const SizedBox(width: 20, child: Text("•", textAlign: TextAlign.center)), InkWell( onTap: () { context.pop(); - launchUrl( - Uri.parse('https://github.com/immich-app/immich'), - mode: LaunchMode.externalApplication, - ); + launchUrl(Uri.parse('https://github.com/immich-app/immich'), mode: LaunchMode.externalApplication); }, - child: Text( - "profile_drawer_github", - style: context.textTheme.bodySmall, - ).tr(), - ), - const SizedBox( - width: 20, - child: Text( - "•", - textAlign: TextAlign.center, - ), + child: Text("profile_drawer_github", style: context.textTheme.bodySmall).tr(), ), + const SizedBox(width: 20, child: Text("•", textAlign: TextAlign.center)), InkWell( onTap: () async { context.pop(); @@ -298,20 +228,13 @@ class ImmichAppBarDialog extends HookConsumerWidget { right: horizontalPadding, bottom: isHorizontal ? 20 : 100, ), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(20), - ), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(20))), child: SizedBox( child: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: [ - Container( - padding: const EdgeInsets.all(20), - child: buildTopRow(), - ), + Container(padding: const EdgeInsets.all(20), child: buildTopRow()), const AppBarProfileInfoBox(), buildStorageInformation(), const AppBarServerInfo(), diff --git a/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart b/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart index 0224ff030f..d12ee7b0d1 100644 --- a/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart +++ b/mobile/lib/widgets/common/app_bar_dialog/app_bar_profile_info.dart @@ -10,15 +10,12 @@ import 'package:immich_mobile/widgets/common/immich_loading_indicator.dart'; import 'package:immich_mobile/widgets/common/user_circle_avatar.dart'; class AppBarProfileInfoBox extends HookConsumerWidget { - const AppBarProfileInfoBox({ - super.key, - }); + const AppBarProfileInfoBox({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final authState = ref.watch(authProvider); - final uploadProfileImageStatus = - ref.watch(uploadProfileImageProvider).status; + final uploadProfileImageStatus = ref.watch(uploadProfileImageProvider).status; final user = ref.watch(currentUserProvider); buildUserProfileImage() { @@ -30,40 +27,24 @@ class AppBarProfileInfoBox extends HookConsumerWidget { ); } - final userImage = UserCircleAvatar( - radius: 22, - size: 44, - user: user, - ); + final userImage = UserCircleAvatar(radius: 22, size: 44, user: user); if (uploadProfileImageStatus == UploadProfileStatus.loading) { - return const SizedBox( - height: 40, - width: 40, - child: ImmichLoadingIndicator(borderRadius: 20), - ); + return const SizedBox(height: 40, width: 40, child: ImmichLoadingIndicator(borderRadius: 20)); } return userImage; } pickUserProfileImage() async { - final XFile? image = await ImagePicker().pickImage( - source: ImageSource.gallery, - maxHeight: 1024, - maxWidth: 1024, - ); + final XFile? image = await ImagePicker().pickImage(source: ImageSource.gallery, maxHeight: 1024, maxWidth: 1024); if (image != null) { - var success = - await ref.watch(uploadProfileImageProvider.notifier).upload(image); + var success = await ref.watch(uploadProfileImageProvider.notifier).upload(image); if (success) { - final profileImagePath = - ref.read(uploadProfileImageProvider).profileImagePath; - ref.watch(authProvider.notifier).updateUserProfileImagePath( - profileImagePath, - ); + final profileImagePath = ref.read(uploadProfileImageProvider).profileImagePath; + ref.watch(authProvider.notifier).updateUserProfileImagePath(profileImagePath); if (user != null) { ref.read(currentUserProvider.notifier).refresh(); } @@ -77,10 +58,7 @@ class AppBarProfileInfoBox extends HookConsumerWidget { width: double.infinity, decoration: BoxDecoration( color: context.colorScheme.surface, - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(10), - topRight: Radius.circular(10), - ), + borderRadius: const BorderRadius.only(topLeft: Radius.circular(10), topRight: Radius.circular(10)), ), child: ListTile( minLeadingWidth: 50, @@ -96,16 +74,10 @@ class AppBarProfileInfoBox extends HookConsumerWidget { child: Material( color: context.colorScheme.surfaceContainerHighest, elevation: 3, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(50.0)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(50.0))), child: Padding( padding: const EdgeInsets.all(5.0), - child: Icon( - Icons.camera_alt_outlined, - color: context.primaryColor, - size: 14, - ), + child: Icon(Icons.camera_alt_outlined, color: context.primaryColor, size: 14), ), ), ), @@ -114,16 +86,11 @@ class AppBarProfileInfoBox extends HookConsumerWidget { ), title: Text( authState.name, - style: context.textTheme.titleMedium?.copyWith( - color: context.primaryColor, - fontWeight: FontWeight.w500, - ), + style: context.textTheme.titleMedium?.copyWith(color: context.primaryColor, fontWeight: FontWeight.w500), ), subtitle: Text( authState.userEmail, - style: context.textTheme.bodySmall?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.bodySmall?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), ), ), diff --git a/mobile/lib/widgets/common/app_bar_dialog/app_bar_server_info.dart b/mobile/lib/widgets/common/app_bar_dialog/app_bar_server_info.dart index a867a11e00..4aacfb3322 100644 --- a/mobile/lib/widgets/common/app_bar_dialog/app_bar_server_info.dart +++ b/mobile/lib/widgets/common/app_bar_dialog/app_bar_server_info.dart @@ -11,9 +11,7 @@ import 'package:immich_mobile/utils/url_helper.dart'; import 'package:package_info_plus/package_info_plus.dart'; class AppBarServerInfo extends HookConsumerWidget { - const AppBarServerInfo({ - super.key, - }); + const AppBarServerInfo({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -27,29 +25,20 @@ class AppBarServerInfo extends HookConsumerWidget { getPackageInfo() async { PackageInfo packageInfo = await PackageInfo.fromPlatform(); - appInfo.value = { - "version": packageInfo.version, - "buildNumber": packageInfo.buildNumber, - }; + appInfo.value = {"version": packageInfo.version, "buildNumber": packageInfo.buildNumber}; } - useEffect( - () { - getPackageInfo(); - return null; - }, - [], - ); + useEffect(() { + getPackageInfo(); + return null; + }, []); return Padding( padding: const EdgeInsets.only(left: 10.0, right: 10.0, bottom: 10.0), child: Container( decoration: BoxDecoration( color: context.colorScheme.surface, - borderRadius: const BorderRadius.only( - bottomLeft: Radius.circular(10), - bottomRight: Radius.circular(10), - ), + borderRadius: const BorderRadius.only(bottomLeft: Radius.circular(10), bottomRight: Radius.circular(10)), ), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8), @@ -63,17 +52,10 @@ class AppBarServerInfo extends HookConsumerWidget { ? serverInfoState.versionMismatchErrorMessage : "profile_drawer_client_server_up_to_date".tr(), textAlign: TextAlign.center, - style: TextStyle( - fontSize: 11, - color: context.primaryColor, - fontWeight: FontWeight.w500, - ), + style: TextStyle(fontSize: 11, color: context.primaryColor, fontWeight: FontWeight.w500), ), ), - const Padding( - padding: EdgeInsets.symmetric(horizontal: 10), - child: Divider(thickness: 1), - ), + const Padding(padding: EdgeInsets.symmetric(horizontal: 10), child: Divider(thickness: 1)), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -106,10 +88,7 @@ class AppBarServerInfo extends HookConsumerWidget { ), ], ), - const Padding( - padding: EdgeInsets.symmetric(horizontal: 10), - child: Divider(thickness: 1), - ), + const Padding(padding: EdgeInsets.symmetric(horizontal: 10), child: Divider(thickness: 1)), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -144,10 +123,7 @@ class AppBarServerInfo extends HookConsumerWidget { ), ], ), - const Padding( - padding: EdgeInsets.symmetric(horizontal: 10), - child: Divider(thickness: 1), - ), + const Padding(padding: EdgeInsets.symmetric(horizontal: 10), child: Divider(thickness: 1)), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -173,12 +149,10 @@ class AppBarServerInfo extends HookConsumerWidget { verticalOffset: 0, decoration: BoxDecoration( color: context.primaryColor.withValues(alpha: 0.9), - borderRadius: - const BorderRadius.all(Radius.circular(10)), + borderRadius: const BorderRadius.all(Radius.circular(10)), ), textStyle: TextStyle( - color: - context.isDarkTheme ? Colors.black : Colors.white, + color: context.isDarkTheme ? Colors.black : Colors.white, fontWeight: FontWeight.bold, ), message: getServerUrl() ?? '--', @@ -199,10 +173,7 @@ class AppBarServerInfo extends HookConsumerWidget { ), ], ), - const Padding( - padding: EdgeInsets.symmetric(horizontal: 10), - child: Divider(thickness: 1), - ), + const Padding(padding: EdgeInsets.symmetric(horizontal: 10), child: Divider(thickness: 1)), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -214,11 +185,7 @@ class AppBarServerInfo extends HookConsumerWidget { if (serverInfoState.isNewReleaseAvailable) const Padding( padding: EdgeInsets.only(right: 5.0), - child: Icon( - Icons.info, - color: Color.fromARGB(255, 243, 188, 106), - size: 12, - ), + child: Icon(Icons.info, color: Color.fromARGB(255, 243, 188, 106), size: 12), ), Text( "latest_version".tr(), diff --git a/mobile/lib/widgets/common/confirm_dialog.dart b/mobile/lib/widgets/common/confirm_dialog.dart index 411770e78f..153c124595 100644 --- a/mobile/lib/widgets/common/confirm_dialog.dart +++ b/mobile/lib/widgets/common/confirm_dialog.dart @@ -26,9 +26,7 @@ class ConfirmDialog extends StatelessWidget { } return AlertDialog( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))), title: Text(title).tr(), content: Text(content).tr(), actions: [ @@ -36,20 +34,14 @@ class ConfirmDialog extends StatelessWidget { onPressed: () => context.pop(false), child: Text( cancel, - style: TextStyle( - color: context.primaryColor, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold), ).tr(), ), TextButton( onPressed: onOkPressed, child: Text( ok, - style: TextStyle( - color: context.colorScheme.error, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: context.colorScheme.error, fontWeight: FontWeight.bold), ).tr(), ), ], diff --git a/mobile/lib/widgets/common/date_time_picker.dart b/mobile/lib/widgets/common/date_time_picker.dart index 064c4845fe..9cc8de29ee 100644 --- a/mobile/lib/widgets/common/date_time_picker.dart +++ b/mobile/lib/widgets/common/date_time_picker.dart @@ -16,11 +16,8 @@ Future showDateTimePicker({ }) { return showDialog( context: context, - builder: (context) => _DateTimePicker( - initialDateTime: initialDateTime, - initialTZ: initialTZ, - initialTZOffset: initialTZOffset, - ), + builder: (context) => + _DateTimePicker(initialDateTime: initialDateTime, initialTZ: initialTZ, initialTZOffset: initialTZOffset), ); } @@ -33,18 +30,12 @@ class _DateTimePicker extends HookWidget { final String? initialTZ; final Duration? initialTZOffset; - const _DateTimePicker({ - this.initialDateTime, - this.initialTZ, - this.initialTZOffset, - }); + const _DateTimePicker({this.initialDateTime, this.initialTZ, this.initialTZOffset}); _TimeZoneOffset _getInitiationLocation() { if (initialTZ != null) { try { - return _TimeZoneOffset.fromLocation( - tz.timeZoneDatabase.get(initialTZ!), - ); + return _TimeZoneOffset.fromLocation(tz.timeZoneDatabase.get(initialTZ!)); } on LocationNotFoundException { // no-op } @@ -59,10 +50,8 @@ class _DateTimePicker extends HookWidget { (location) => location.currentTimeZone.offset == offsetInMilli, ); // Prefer locations with abbreviation first - final location = locations.firstWhereOrNull( - (e) => !e.currentTimeZone.abbreviation.contains("0"), - ) ?? - locations.firstOrNull; + final location = + locations.firstWhereOrNull((e) => !e.currentTimeZone.abbreviation.contains("0")) ?? locations.firstOrNull; if (location != null) { return _TimeZoneOffset.fromLocation(location); } @@ -73,10 +62,7 @@ class _DateTimePicker extends HookWidget { // returns a list of location along with it's offset in duration List<_TimeZoneOffset> getAllTimeZones() { - return tz.timeZoneDatabase.locations.values - .map(_TimeZoneOffset.fromLocation) - .sorted() - .toList(); + return tz.timeZoneDatabase.locations.values.map(_TimeZoneOffset.fromLocation).sorted().toList(); } @override @@ -89,11 +75,7 @@ class _DateTimePicker extends HookWidget { (timezone) => DropdownMenuEntry<_TimeZoneOffset>( value: timezone, label: timezone.display, - style: ButtonStyle( - textStyle: WidgetStatePropertyAll( - context.textTheme.bodyMedium, - ), - ), + style: ButtonStyle(textStyle: WidgetStatePropertyAll(context.textTheme.bodyMedium)), ), ) .toList(); @@ -112,10 +94,7 @@ class _DateTimePicker extends HookWidget { return; } - final newTime = await showTimePicker( - context: context, - initialTime: TimeOfDay.fromDateTime(date.value), - ); + final newTime = await showTimePicker(context: context, initialTime: TimeOfDay.fromDateTime(date.value)); if (newTime == null) { return; @@ -125,11 +104,9 @@ class _DateTimePicker extends HookWidget { } void popWithDateTime() { - final formattedDateTime = - DateFormat("yyyy-MM-dd'T'HH:mm:ss").format(date.value); - final dtWithOffset = formattedDateTime + - Duration(milliseconds: tzOffset.value.offsetInMilliseconds) - .formatAsOffset(); + final formattedDateTime = DateFormat("yyyy-MM-dd'T'HH:mm:ss").format(date.value); + final dtWithOffset = + formattedDateTime + Duration(milliseconds: tzOffset.value.offsetInMilliseconds).formatAsOffset(); context.pop(dtWithOffset); } @@ -150,10 +127,7 @@ class _DateTimePicker extends HookWidget { onPressed: popWithDateTime, child: Text( "action_common_update", - style: context.textTheme.bodyMedium?.copyWith( - fontWeight: FontWeight.w600, - color: context.primaryColor, - ), + style: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600, color: context.primaryColor), ).tr(), ), ], @@ -161,46 +135,22 @@ class _DateTimePicker extends HookWidget { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( - "date_and_time", - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600, - ), - ).tr(), + const Text("date_and_time", style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600)).tr(), const SizedBox(height: 32), ListTile( tileColor: context.colorScheme.surfaceContainerHighest, shape: ShapeBorder.lerp( - const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(10), - ), - ), - const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(10), - ), - ), + const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))), + const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))), 1, ), - trailing: Icon( - Icons.edit_outlined, - size: 18, - color: context.primaryColor, - ), - title: Text( - DateFormat("dd-MM-yyyy hh:mm a").format(date.value), - style: context.textTheme.bodyMedium, - ).tr(), + trailing: Icon(Icons.edit_outlined, size: 18, color: context.primaryColor), + title: Text(DateFormat("dd-MM-yyyy hh:mm a").format(date.value), style: context.textTheme.bodyMedium), onTap: pickDate, ), const SizedBox(height: 24), DropdownSearchMenu( - trailingIcon: Icon( - Icons.arrow_drop_down, - color: context.primaryColor, - ), + trailingIcon: Icon(Icons.arrow_drop_down, color: context.primaryColor), hintText: "timezone".tr(), label: const Text('timezone').tr(), textStyle: context.textTheme.bodyMedium, @@ -218,26 +168,17 @@ class _TimeZoneOffset implements Comparable<_TimeZoneOffset> { final String display; final Location location; - const _TimeZoneOffset({ - required this.display, - required this.location, - }); + const _TimeZoneOffset({required this.display, required this.location}); - _TimeZoneOffset copyWith({ - String? display, - Location? location, - }) { - return _TimeZoneOffset( - display: display ?? this.display, - location: location ?? this.location, - ); + _TimeZoneOffset copyWith({String? display, Location? location}) { + return _TimeZoneOffset(display: display ?? this.display, location: location ?? this.location); } int get offsetInMilliseconds => location.currentTimeZone.offset; _TimeZoneOffset.fromLocation(tz.Location l) - : display = _getFormattedOffset(l.currentTimeZone.offset, l), - location = l; + : display = _getFormattedOffset(l.currentTimeZone.offset, l), + location = l; @override int compareTo(_TimeZoneOffset other) { @@ -245,19 +186,15 @@ class _TimeZoneOffset implements Comparable<_TimeZoneOffset> { } @override - String toString() => - '_TimeZoneOffset(display: $display, location: $location)'; + String toString() => '_TimeZoneOffset(display: $display, location: $location)'; @override bool operator ==(Object other) { if (identical(this, other)) return true; - return other is _TimeZoneOffset && - other.display == display && - other.offsetInMilliseconds == offsetInMilliseconds; + return other is _TimeZoneOffset && other.display == display && other.offsetInMilliseconds == offsetInMilliseconds; } @override - int get hashCode => - display.hashCode ^ offsetInMilliseconds.hashCode ^ location.hashCode; + int get hashCode => display.hashCode ^ offsetInMilliseconds.hashCode ^ location.hashCode; } diff --git a/mobile/lib/widgets/common/delayed_loading_indicator.dart b/mobile/lib/widgets/common/delayed_loading_indicator.dart index e54762bb9f..5fad10530e 100644 --- a/mobile/lib/widgets/common/delayed_loading_indicator.dart +++ b/mobile/lib/widgets/common/delayed_loading_indicator.dart @@ -11,12 +11,7 @@ class DelayedLoadingIndicator extends StatelessWidget { /// An optional fade in duration to animate the loading final Duration? fadeInDuration; - const DelayedLoadingIndicator({ - super.key, - this.delay = const Duration(seconds: 3), - this.child, - this.fadeInDuration, - }); + const DelayedLoadingIndicator({super.key, this.delay = const Duration(seconds: 3), this.child, this.fadeInDuration}); @override Widget build(BuildContext context) { @@ -25,18 +20,12 @@ class DelayedLoadingIndicator extends StatelessWidget { builder: (context, snapshot) { late Widget c; if (snapshot.connectionState == ConnectionState.done) { - c = child ?? - const ImmichLoadingIndicator( - key: ValueKey('loading'), - ); + c = child ?? const ImmichLoadingIndicator(key: ValueKey('loading')); } else { c = Container(key: const ValueKey('hiding')); } - return AnimatedSwitcher( - duration: fadeInDuration ?? Duration.zero, - child: c, - ); + return AnimatedSwitcher(duration: fadeInDuration ?? Duration.zero, child: c); }, ); } diff --git a/mobile/lib/widgets/common/drag_sheet.dart b/mobile/lib/widgets/common/drag_sheet.dart index a98e000e51..5d1fda1beb 100644 --- a/mobile/lib/widgets/common/drag_sheet.dart +++ b/mobile/lib/widgets/common/drag_sheet.dart @@ -18,13 +18,7 @@ class CustomDraggingHandle extends StatelessWidget { } class ControlBoxButton extends StatelessWidget { - const ControlBoxButton({ - super.key, - required this.label, - required this.iconData, - this.onPressed, - this.onLongPressed, - }); + const ControlBoxButton({super.key, required this.label, required this.iconData, this.onPressed, this.onLongPressed}); final String label; final IconData iconData; @@ -33,14 +27,11 @@ class ControlBoxButton extends StatelessWidget { @override Widget build(BuildContext context) { - final minWidth = - context.isMobile ? MediaQuery.sizeOf(context).width / 4.5 : 75.0; + final minWidth = context.isMobile ? MediaQuery.sizeOf(context).width / 4.5 : 75.0; return MaterialButton( padding: const EdgeInsets.all(10), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(20)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(20))), onPressed: onPressed, onLongPress: onLongPressed, minWidth: minWidth, diff --git a/mobile/lib/widgets/common/dropdown_search_menu.dart b/mobile/lib/widgets/common/dropdown_search_menu.dart index cf954a8977..bf0c75c8aa 100644 --- a/mobile/lib/widgets/common/dropdown_search_menu.dart +++ b/mobile/lib/widgets/common/dropdown_search_menu.dart @@ -30,18 +30,12 @@ class DropdownSearchMenu extends HookWidget { @override Widget build(BuildContext context) { final selectedItem = useState?>( - dropdownMenuEntries - .firstWhereOrNull((item) => item.value == initialSelection), + dropdownMenuEntries.firstWhereOrNull((item) => item.value == initialSelection), ); final showTimeZoneDropdown = useState(false); - final effectiveConstraints = menuConstraints ?? - const BoxConstraints( - minWidth: 280, - maxWidth: 280, - minHeight: 0, - maxHeight: 280, - ); + final effectiveConstraints = + menuConstraints ?? const BoxConstraints(minWidth: 280, maxWidth: 280, minHeight: 0, maxHeight: 280); final inputDecoration = InputDecoration( contentPadding: const EdgeInsets.fromLTRB(12, 4, 12, 4), @@ -59,12 +53,7 @@ class DropdownSearchMenu extends HookWidget { child: InputDecorator( decoration: inputDecoration, child: selectedItem.value != null - ? Text( - selectedItem.value!.label, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: textStyle, - ) + ? Text(selectedItem.value!.label, maxLines: 1, overflow: TextOverflow.ellipsis, style: textStyle) : null, ), ), @@ -77,10 +66,7 @@ class DropdownSearchMenu extends HookWidget { displayStringForOption: (option) => option.label, optionsBuilder: (textEditingValue) { return dropdownMenuEntries.where( - (item) => item.label - .toLowerCase() - .trim() - .contains(textEditingValue.text.toLowerCase().trim()), + (item) => item.label.toLowerCase().trim().contains(textEditingValue.text.toLowerCase().trim()), ); }, onSelected: (option) { @@ -93,9 +79,7 @@ class DropdownSearchMenu extends HookWidget { autofocus: true, focusNode: focusNode, controller: textEditingController, - decoration: inputDecoration.copyWith( - hintText: "search_timezone".tr(), - ), + decoration: inputDecoration.copyWith(hintText: "search_timezone".tr()), maxLines: 1, style: context.textTheme.bodyMedium, expands: false, @@ -127,32 +111,16 @@ class DropdownSearchMenu extends HookWidget { onTap: () => onSelected(option), child: Builder( builder: (BuildContext context) { - final bool highlight = - AutocompleteHighlightedOption.of(context) == - index; + final bool highlight = AutocompleteHighlightedOption.of(context) == index; if (highlight) { - SchedulerBinding.instance.addPostFrameCallback( - (Duration timeStamp) { - Scrollable.ensureVisible( - context, - alignment: 0.5, - ); - }, - debugLabel: 'AutocompleteOptions.ensureVisible', - ); + SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) { + Scrollable.ensureVisible(context, alignment: 0.5); + }, debugLabel: 'AutocompleteOptions.ensureVisible'); } return Container( - color: highlight - ? Theme.of(context) - .colorScheme - .onSurface - .withValues(alpha: 0.12) - : null, + color: highlight ? Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.12) : null, padding: const EdgeInsets.all(16.0), - child: Text( - option.label, - style: textStyle, - ), + child: Text(option.label, style: textStyle), ); }, ), diff --git a/mobile/lib/widgets/common/fade_in_placeholder_image.dart b/mobile/lib/widgets/common/fade_in_placeholder_image.dart index 2be32fa8ba..2461dbe6bf 100644 --- a/mobile/lib/widgets/common/fade_in_placeholder_image.dart +++ b/mobile/lib/widgets/common/fade_in_placeholder_image.dart @@ -22,12 +22,7 @@ class FadeInPlaceholderImage extends StatelessWidget { fit: StackFit.expand, children: [ placeholder, - FadeInImage( - fadeInDuration: duration, - image: image, - fit: fit, - placeholder: MemoryImage(kTransparentImage), - ), + FadeInImage(fadeInDuration: duration, image: image, fit: fit, placeholder: MemoryImage(kTransparentImage)), ], ), ); diff --git a/mobile/lib/widgets/common/immich_app_bar.dart b/mobile/lib/widgets/common/immich_app_bar.dart index 799cf17f3f..7eaedd27b5 100644 --- a/mobile/lib/widgets/common/immich_app_bar.dart +++ b/mobile/lib/widgets/common/immich_app_bar.dart @@ -2,7 +2,6 @@ import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_svg/svg.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; @@ -28,8 +27,7 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget { @override Widget build(BuildContext context, WidgetRef ref) { final BackUpState backupState = ref.watch(backupProvider); - final bool isEnableAutoBackup = - backupState.backgroundBackup || backupState.autoBackup; + final bool isEnableAutoBackup = backupState.backgroundBackup || backupState.autoBackup; final ServerInfo serverInfoState = ref.watch(serverInfoProvider); final user = ref.watch(currentUserProvider); final isDarkTheme = context.isDarkTheme; @@ -38,42 +36,24 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget { buildProfileIndicator() { return InkWell( - onTap: () => showDialog( - context: context, - useRootNavigator: false, - builder: (ctx) => const ImmichAppBarDialog(), - ), + onTap: () => + showDialog(context: context, useRootNavigator: false, builder: (ctx) => const ImmichAppBarDialog()), borderRadius: const BorderRadius.all(Radius.circular(12)), child: Badge( label: Container( - decoration: BoxDecoration( - color: Colors.black, - borderRadius: BorderRadius.circular(widgetSize / 2), - ), - child: const Icon( - Icons.info, - color: Color.fromARGB(255, 243, 188, 106), - size: widgetSize / 2, - ), + decoration: BoxDecoration(color: Colors.black, borderRadius: BorderRadius.circular(widgetSize / 2)), + child: const Icon(Icons.info, color: Color.fromARGB(255, 243, 188, 106), size: widgetSize / 2), ), backgroundColor: Colors.transparent, alignment: Alignment.bottomRight, - isLabelVisible: serverInfoState.isVersionMismatch || - ((user?.isAdmin ?? false) && - serverInfoState.isNewReleaseAvailable), + isLabelVisible: + serverInfoState.isVersionMismatch || ((user?.isAdmin ?? false) && serverInfoState.isNewReleaseAvailable), offset: const Offset(-2, -12), child: user == null - ? const Icon( - Icons.face_outlined, - size: widgetSize, - ) + ? const Icon(Icons.face_outlined, size: widgetSize) : Semantics( label: "logged_in_as".tr(namedArgs: {"user": user.name}), - child: UserCircleAvatar( - radius: 17, - size: 31, - user: user, - ), + child: UserCircleAvatar(radius: 17, size: 31, user: user), ), ), ); @@ -93,8 +73,7 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget { semanticsLabel: 'backup_controller_page_backup'.tr(), ), ); - } else if (backupState.backupProgress != - BackUpProgressEnum.inBackground && + } else if (backupState.backupProgress != BackUpProgressEnum.inBackground && backupState.backupProgress != BackUpProgressEnum.manualInProgress) { return Icon( Icons.check_outlined, @@ -128,9 +107,7 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget { height: widgetSize / 2, decoration: BoxDecoration( color: badgeBackground, - border: Border.all( - color: context.colorScheme.outline.withValues(alpha: .3), - ), + border: Border.all(color: context.colorScheme.outline.withValues(alpha: .3)), borderRadius: BorderRadius.circular(widgetSize / 2), ), child: indicatorIcon, @@ -139,22 +116,14 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget { alignment: Alignment.bottomRight, isLabelVisible: indicatorIcon != null, offset: const Offset(-2, -12), - child: Icon( - Icons.backup_rounded, - size: widgetSize, - color: context.primaryColor, - ), + child: Icon(Icons.backup_rounded, size: widgetSize, color: context.primaryColor), ), ); } return AppBar( backgroundColor: context.themeData.appBarTheme.backgroundColor, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(5), - ), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))), automaticallyImplyLeading: false, centerTitle: false, title: Builder( @@ -180,13 +149,8 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget { ), actions: [ if (actions != null) - ...actions!.map( - (action) => Padding( - padding: const EdgeInsets.only(right: 16), - child: action, - ), - ), - if (kDebugMode || kProfileMode || appFlavor == 'beta') + ...actions!.map((action) => Padding(padding: const EdgeInsets.only(right: 16), child: action)), + if (kDebugMode || kProfileMode) IconButton( icon: const Icon(Icons.science_rounded), onPressed: () => context.pushRoute(const FeatInDevRoute()), @@ -196,25 +160,13 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget { padding: const EdgeInsets.only(right: 12), child: IconButton( onPressed: () { - showDialog( - context: context, - builder: (context) => const CastDialog(), - ); + showDialog(context: context, builder: (context) => const CastDialog()); }, - icon: Icon( - isCasting ? Icons.cast_connected_rounded : Icons.cast_rounded, - ), + icon: Icon(isCasting ? Icons.cast_connected_rounded : Icons.cast_rounded), ), ), - if (showUploadButton) - Padding( - padding: const EdgeInsets.only(right: 20), - child: buildBackupIndicator(), - ), - Padding( - padding: const EdgeInsets.only(right: 20), - child: buildProfileIndicator(), - ), + if (showUploadButton) Padding(padding: const EdgeInsets.only(right: 20), child: buildBackupIndicator()), + Padding(padding: const EdgeInsets.only(right: 20), child: buildProfileIndicator()), ], ); } diff --git a/mobile/lib/widgets/common/immich_image.dart b/mobile/lib/widgets/common/immich_image.dart index cbad9037c0..c8bc9c1f6a 100644 --- a/mobile/lib/widgets/common/immich_image.dart +++ b/mobile/lib/widgets/common/immich_image.dart @@ -28,39 +28,25 @@ class ImmichImage extends StatelessWidget { // either by using the asset ID or the asset itself /// [asset] is the Asset to request, or else use [assetId] to get a remote /// image provider - static ImageProvider imageProvider({ - Asset? asset, - String? assetId, - double width = 1080, - double height = 1920, - }) { + static ImageProvider imageProvider({Asset? asset, String? assetId, double width = 1080, double height = 1920}) { if (asset == null && assetId == null) { throw Exception('Must supply either asset or assetId'); } if (asset == null) { - return ImmichRemoteImageProvider( - assetId: assetId!, - ); + return ImmichRemoteImageProvider(assetId: assetId!); } if (useLocal(asset)) { - return ImmichLocalImageProvider( - asset: asset, - width: width, - height: height, - ); + return ImmichLocalImageProvider(asset: asset, width: width, height: height); } else { - return ImmichRemoteImageProvider( - assetId: asset.remoteId!, - ); + return ImmichRemoteImageProvider(assetId: asset.remoteId!); } } // Whether to use the local asset image provider or a remote one static bool useLocal(Asset asset) => - !asset.isRemote || - asset.isLocal && !Store.get(StoreKey.preferRemoteImage, false); + !asset.isRemote || asset.isLocal && !Store.get(StoreKey.preferRemoteImage, false); @override Widget build(BuildContext context) { @@ -69,17 +55,11 @@ class ImmichImage extends StatelessWidget { color: Colors.grey, width: width, height: height, - child: const Center( - child: Icon(Icons.no_photography), - ), + child: const Center(child: Icon(Icons.no_photography)), ); } - final imageProviderInstance = ImmichImage.imageProvider( - asset: asset, - width: context.width, - height: context.height, - ); + final imageProviderInstance = ImmichImage.imageProvider(asset: asset, width: context.width, height: context.height); return OctoImage( fadeInDuration: const Duration(milliseconds: 0), @@ -97,11 +77,7 @@ class ImmichImage extends StatelessWidget { errorBuilder: (context, error, stackTrace) { imageProviderInstance.evict(); - return Icon( - Icons.image_not_supported_outlined, - size: 32, - color: Colors.red[200], - ); + return Icon(Icons.image_not_supported_outlined, size: 32, color: Colors.red[200]); }, ); } diff --git a/mobile/lib/widgets/common/immich_loading_indicator.dart b/mobile/lib/widgets/common/immich_loading_indicator.dart index 8f9eaeaa99..52f957f7e7 100644 --- a/mobile/lib/widgets/common/immich_loading_indicator.dart +++ b/mobile/lib/widgets/common/immich_loading_indicator.dart @@ -5,22 +5,15 @@ import 'package:immich_mobile/widgets/common/immich_logo.dart'; class ImmichLoadingIndicator extends HookWidget { final double? borderRadius; - const ImmichLoadingIndicator({ - super.key, - this.borderRadius, - }); + const ImmichLoadingIndicator({super.key, this.borderRadius}); @override Widget build(BuildContext context) { - final logoAnimationController = useAnimationController( - duration: const Duration(seconds: 6), - ) + final logoAnimationController = useAnimationController(duration: const Duration(seconds: 6)) ..reverse() ..repeat(); - final borderAnimationController = useAnimationController( - duration: const Duration(seconds: 6), - )..repeat(); + final borderAnimationController = useAnimationController(duration: const Duration(seconds: 6))..repeat(); return Container( height: 80, @@ -34,10 +27,7 @@ class ImmichLoadingIndicator extends HookWidget { animation: borderAnimationController, builder: (context, child) { return CustomPaint( - painter: GradientBorderPainter( - animation: borderAnimationController.value, - strokeWidth: 3, - ), + painter: GradientBorderPainter(animation: borderAnimationController.value, strokeWidth: 3), child: child, ); }, @@ -45,9 +35,7 @@ class ImmichLoadingIndicator extends HookWidget { padding: const EdgeInsets.all(15), child: RotationTransition( turns: logoAnimationController, - child: const ImmichLogo( - heroTag: 'logo', - ), + child: const ImmichLogo(heroTag: 'logo'), ), ), ), @@ -67,10 +55,7 @@ class GradientBorderPainter extends CustomPainter { const Color(0xFF18C249), ]; - GradientBorderPainter({ - required this.animation, - required this.strokeWidth, - }); + GradientBorderPainter({required this.animation, required this.strokeWidth}); @override void paint(Canvas canvas, Size size) { @@ -96,10 +81,7 @@ class GradientBorderPainter extends CustomPainter { colors.first.withValues(alpha: opacity), ], // Add evenly distributed stops - stops: List.generate( - colors.length + 1, - (index) => index / colors.length, - ), + stops: List.generate(colors.length + 1, (index) => index / colors.length), tileMode: TileMode.clamp, // Use transformations to rotate the gradient transform: GradientRotation(-animation * 2 * 3.14159), diff --git a/mobile/lib/widgets/common/immich_logo.dart b/mobile/lib/widgets/common/immich_logo.dart index 43987878cb..a369275282 100644 --- a/mobile/lib/widgets/common/immich_logo.dart +++ b/mobile/lib/widgets/common/immich_logo.dart @@ -4,11 +4,7 @@ class ImmichLogo extends StatelessWidget { final double size; final dynamic heroTag; - const ImmichLogo({ - super.key, - this.size = 100, - this.heroTag, - }); + const ImmichLogo({super.key, this.size = 100, this.heroTag}); @override Widget build(BuildContext context) { diff --git a/mobile/lib/widgets/common/immich_sliver_app_bar.dart b/mobile/lib/widgets/common/immich_sliver_app_bar.dart index 5ab60c913c..06a97d1ce5 100644 --- a/mobile/lib/widgets/common/immich_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/immich_sliver_app_bar.dart @@ -1,14 +1,17 @@ import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/setting.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; -import 'package:immich_mobile/models/backup/backup_state.model.dart'; import 'package:immich_mobile/models/server_info/server_info.model.dart'; -import 'package:immich_mobile/providers/backup/backup.provider.dart'; +import 'package:immich_mobile/providers/backup/drift_backup.provider.dart'; import 'package:immich_mobile/providers/cast.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/setting.provider.dart'; import 'package:immich_mobile/providers/server_info.provider.dart'; +import 'package:immich_mobile/providers/sync_status.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/routing/router.dart'; @@ -39,8 +42,7 @@ class ImmichSliverAppBar extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final isCasting = ref.watch(castProvider.select((c) => c.isCasting)); - final isMultiSelectEnabled = - ref.watch(multiSelectProvider.select((s) => s.isEnabled)); + final isMultiSelectEnabled = ref.watch(multiSelectProvider.select((s) => s.isEnabled)); return SliverAnimatedOpacity( duration: Durations.medium1, @@ -50,46 +52,31 @@ class ImmichSliverAppBar extends ConsumerWidget { pinned: pinned, snap: snap, expandedHeight: expandedHeight, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(5), - ), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))), automaticallyImplyLeading: false, centerTitle: false, title: title ?? const _ImmichLogoWithText(), actions: [ - if (actions != null) - ...actions!.map( - (action) => Padding( - padding: const EdgeInsets.only(right: 16), - child: action, - ), - ), if (isCasting) Padding( padding: const EdgeInsets.only(right: 12), child: IconButton( onPressed: () { - showDialog( - context: context, - builder: (context) => const CastDialog(), - ); + showDialog(context: context, builder: (context) => const CastDialog()); }, - icon: Icon( - isCasting ? Icons.cast_connected_rounded : Icons.cast_rounded, - ), + icon: Icon(isCasting ? Icons.cast_connected_rounded : Icons.cast_rounded), ), ), - if (showUploadButton) - const Padding( - padding: EdgeInsets.only(right: 20), - child: _BackupIndicator(), + const _SyncStatusIndicator(), + if (actions != null) + ...actions!.map((action) => Padding(padding: const EdgeInsets.only(right: 16), child: action)), + if (kDebugMode || kProfileMode) + IconButton( + icon: const Icon(Icons.science_rounded), + onPressed: () => context.pushRoute(const FeatInDevRoute()), ), - const Padding( - padding: EdgeInsets.only(right: 20), - child: _ProfileIndicator(), - ), + if (showUploadButton) const Padding(padding: EdgeInsets.only(right: 20), child: _BackupIndicator()), + const Padding(padding: EdgeInsets.only(right: 20), child: _ProfileIndicator()), ], ), ); @@ -108,8 +95,7 @@ class _ImmichLogoWithText extends StatelessWidget { Builder( builder: (context) { return Badge( - padding: - const EdgeInsets.symmetric(horizontal: 6, vertical: 4), + padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4), backgroundColor: context.primaryColor, alignment: Alignment.centerRight, offset: const Offset(16, -8), @@ -152,41 +138,23 @@ class _ProfileIndicator extends ConsumerWidget { const widgetSize = 30.0; return InkWell( - onTap: () => showDialog( - context: context, - useRootNavigator: false, - builder: (ctx) => const ImmichAppBarDialog(), - ), + onTap: () => showDialog(context: context, useRootNavigator: false, builder: (ctx) => const ImmichAppBarDialog()), borderRadius: const BorderRadius.all(Radius.circular(12)), child: Badge( label: Container( - decoration: BoxDecoration( - color: Colors.black, - borderRadius: BorderRadius.circular(widgetSize / 2), - ), - child: const Icon( - Icons.info, - color: Color.fromARGB(255, 243, 188, 106), - size: widgetSize / 2, - ), + decoration: BoxDecoration(color: Colors.black, borderRadius: BorderRadius.circular(widgetSize / 2)), + child: const Icon(Icons.info, color: Color.fromARGB(255, 243, 188, 106), size: widgetSize / 2), ), backgroundColor: Colors.transparent, alignment: Alignment.bottomRight, - isLabelVisible: serverInfoState.isVersionMismatch || - ((user?.isAdmin ?? false) && serverInfoState.isNewReleaseAvailable), + isLabelVisible: + serverInfoState.isVersionMismatch || ((user?.isAdmin ?? false) && serverInfoState.isNewReleaseAvailable), offset: const Offset(-2, -12), child: user == null - ? const Icon( - Icons.face_outlined, - size: widgetSize, - ) + ? const Icon(Icons.face_outlined, size: widgetSize) : Semantics( label: "logged_in_as".tr(namedArgs: {"user": user.name}), - child: UserCircleAvatar( - radius: 17, - size: 31, - user: user, - ), + child: UserCircleAvatar(radius: 17, size: 31, user: user), ), ), ); @@ -203,7 +171,7 @@ class _BackupIndicator extends ConsumerWidget { final badgeBackground = context.colorScheme.surfaceContainer; return InkWell( - onTap: () => context.pushRoute(const BackupControllerRoute()), + onTap: () => context.pushRoute(const DriftBackupRoute()), borderRadius: const BorderRadius.all(Radius.circular(12)), child: Badge( label: Container( @@ -211,9 +179,7 @@ class _BackupIndicator extends ConsumerWidget { height: widgetSize / 2, decoration: BoxDecoration( color: badgeBackground, - border: Border.all( - color: context.colorScheme.outline.withValues(alpha: .3), - ), + border: Border.all(color: context.colorScheme.outline.withValues(alpha: .3)), borderRadius: BorderRadius.circular(widgetSize / 2), ), child: indicatorIcon, @@ -222,54 +188,132 @@ class _BackupIndicator extends ConsumerWidget { alignment: Alignment.bottomRight, isLabelVisible: indicatorIcon != null, offset: const Offset(-2, -12), - child: Icon( - Icons.backup_rounded, - size: widgetSize, - color: context.primaryColor, - ), + child: Icon(Icons.backup_rounded, size: widgetSize, color: context.primaryColor), ), ); } Widget? _getBackupBadgeIcon(BuildContext context, WidgetRef ref) { - final BackUpState backupState = ref.watch(backupProvider); - final bool isEnableAutoBackup = - backupState.backgroundBackup || backupState.autoBackup; + final backupStateStream = ref.watch(settingsProvider).watch(Setting.enableBackup); final isDarkTheme = context.isDarkTheme; final iconColor = isDarkTheme ? Colors.white : Colors.black; + final isUploading = ref.watch(driftBackupProvider.select((state) => state.uploadItems.isNotEmpty)); + + return StreamBuilder( + stream: backupStateStream, + initialData: false, + builder: (ctx, snapshot) { + final backupEnabled = snapshot.data ?? false; + + if (!backupEnabled) { + return Icon( + Icons.cloud_off_rounded, + size: 9, + color: iconColor, + semanticLabel: 'backup_controller_page_backup'.tr(), + ); + } + + if (isUploading) { + return Container( + padding: const EdgeInsets.all(3.5), + child: Theme( + data: context.themeData.copyWith( + progressIndicatorTheme: context.themeData.progressIndicatorTheme.copyWith(year2023: true), + ), + child: CircularProgressIndicator( + strokeWidth: 2, + strokeCap: StrokeCap.round, + valueColor: AlwaysStoppedAnimation(iconColor), + semanticsLabel: 'backup_controller_page_backup'.tr(), + ), + ), + ); + } - if (isEnableAutoBackup) { - if (backupState.backupProgress == BackUpProgressEnum.inProgress) { - return Container( - padding: const EdgeInsets.all(3.5), - child: CircularProgressIndicator( - strokeWidth: 2, - strokeCap: StrokeCap.round, - valueColor: AlwaysStoppedAnimation(iconColor), - semanticsLabel: 'backup_controller_page_backup'.tr(), - ), - ); - } else if (backupState.backupProgress != - BackUpProgressEnum.inBackground && - backupState.backupProgress != BackUpProgressEnum.manualInProgress) { return Icon( Icons.check_outlined, size: 9, color: iconColor, semanticLabel: 'backup_controller_page_backup'.tr(), ); + }, + ); + } +} + +class _SyncStatusIndicator extends ConsumerStatefulWidget { + const _SyncStatusIndicator(); + + @override + ConsumerState<_SyncStatusIndicator> createState() => _SyncStatusIndicatorState(); +} + +class _SyncStatusIndicatorState extends ConsumerState<_SyncStatusIndicator> with TickerProviderStateMixin { + late AnimationController _rotationController; + late AnimationController _dismissalController; + late Animation _rotationAnimation; + late Animation _dismissalAnimation; + + @override + void initState() { + super.initState(); + _rotationController = AnimationController(duration: const Duration(seconds: 2), vsync: this); + _dismissalController = AnimationController(duration: const Duration(milliseconds: 300), vsync: this); + _rotationAnimation = Tween(begin: 0.0, end: 1.0).animate(_rotationController); + _dismissalAnimation = Tween( + begin: 1.0, + end: 0.0, + ).animate(CurvedAnimation(parent: _dismissalController, curve: Curves.easeOutQuart)); + } + + @override + void dispose() { + _rotationController.dispose(); + _dismissalController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final syncStatus = ref.watch(syncStatusProvider); + final isSyncing = syncStatus.isRemoteSyncing; + + // Control animations based on sync status + if (isSyncing) { + if (!_rotationController.isAnimating) { + _rotationController.repeat(); + } + _dismissalController.reset(); + } else { + _rotationController.stop(); + if (_dismissalController.status == AnimationStatus.dismissed) { + _dismissalController.forward(); } } - if (!isEnableAutoBackup) { - return Icon( - Icons.cloud_off_rounded, - size: 9, - color: iconColor, - semanticLabel: 'backup_controller_page_backup'.tr(), - ); + // Don't show anything if not syncing and dismissal animation is complete + if (!isSyncing && _dismissalController.status == AnimationStatus.completed) { + return const SizedBox.shrink(); } - return null; + return AnimatedBuilder( + animation: Listenable.merge([_rotationAnimation, _dismissalAnimation]), + builder: (context, child) { + return Padding( + padding: EdgeInsets.only(right: isSyncing ? 16 : 0), + child: Transform.scale( + scale: isSyncing ? 1.0 : _dismissalAnimation.value, + child: Opacity( + opacity: isSyncing ? 1.0 : _dismissalAnimation.value, + child: Transform.rotate( + angle: _rotationAnimation.value * 2 * 3.14159 * -1, // Rotate counter-clockwise + child: Icon(Icons.sync, size: 24, color: context.primaryColor), + ), + ), + ), + ); + }, + ); } } diff --git a/mobile/lib/widgets/common/immich_thumbnail.dart b/mobile/lib/widgets/common/immich_thumbnail.dart index 0918348f4c..612a6a4bd0 100644 --- a/mobile/lib/widgets/common/immich_thumbnail.dart +++ b/mobile/lib/widgets/common/immich_thumbnail.dart @@ -13,13 +13,7 @@ import 'package:octo_image/octo_image.dart'; import 'package:immich_mobile/providers/user.provider.dart'; class ImmichThumbnail extends HookConsumerWidget { - const ImmichThumbnail({ - this.asset, - this.width = 250, - this.height = 250, - this.fit = BoxFit.cover, - super.key, - }); + const ImmichThumbnail({this.asset, this.width = 250, this.height = 250, this.fit = BoxFit.cover, super.key}); final Asset? asset; final double width; @@ -30,35 +24,19 @@ class ImmichThumbnail extends HookConsumerWidget { /// either by using the asset ID or the asset itself /// [asset] is the Asset to request, or else use [assetId] to get a remote /// image provider - static ImageProvider imageProvider({ - Asset? asset, - String? assetId, - String? userId, - int thumbnailSize = 256, - }) { + static ImageProvider imageProvider({Asset? asset, String? assetId, String? userId, int thumbnailSize = 256}) { if (asset == null && assetId == null) { throw Exception('Must supply either asset or assetId'); } if (asset == null) { - return ImmichRemoteThumbnailProvider( - assetId: assetId!, - ); + return ImmichRemoteThumbnailProvider(assetId: assetId!); } if (ImmichImage.useLocal(asset)) { - return ImmichLocalThumbnailProvider( - asset: asset, - height: thumbnailSize, - width: thumbnailSize, - userId: userId, - ); + return ImmichLocalThumbnailProvider(asset: asset, height: thumbnailSize, width: thumbnailSize, userId: userId); } else { - return ImmichRemoteThumbnailProvider( - assetId: asset.remoteId!, - height: thumbnailSize, - width: thumbnailSize, - ); + return ImmichRemoteThumbnailProvider(assetId: asset.remoteId!, height: thumbnailSize, width: thumbnailSize); } } @@ -72,29 +50,18 @@ class ImmichThumbnail extends HookConsumerWidget { color: Colors.grey, width: width, height: height, - child: const Center( - child: Icon(Icons.no_photography), - ), + child: const Center(child: Icon(Icons.no_photography)), ); } - final assetAltText = getAltText( - asset!.exifInfo, - asset!.fileCreatedAt, - asset!.type, - [], - ); + final assetAltText = getAltText(asset!.exifInfo, asset!.fileCreatedAt, asset!.type, []); - final thumbnailProviderInstance = ImmichThumbnail.imageProvider( - asset: asset, - userId: userId, - ); + final thumbnailProviderInstance = ImmichThumbnail.imageProvider(asset: asset, userId: userId); customErrorBuilder(BuildContext ctx, Object error, StackTrace? stackTrace) { thumbnailProviderInstance.evict(); - final originalErrorWidgetBuilder = - blurHashErrorBuilder(blurhash, fit: fit); + final originalErrorWidgetBuilder = blurHashErrorBuilder(blurhash, fit: fit); return originalErrorWidgetBuilder(ctx, error, stackTrace); } diff --git a/mobile/lib/widgets/common/immich_title_text.dart b/mobile/lib/widgets/common/immich_title_text.dart index 711d0bf396..3a848a1db6 100644 --- a/mobile/lib/widgets/common/immich_title_text.dart +++ b/mobile/lib/widgets/common/immich_title_text.dart @@ -5,20 +5,12 @@ class ImmichTitleText extends StatelessWidget { final double fontSize; final Color? color; - const ImmichTitleText({ - super.key, - this.fontSize = 48, - this.color, - }); + const ImmichTitleText({super.key, this.fontSize = 48, this.color}); @override Widget build(BuildContext context) { return Image( - image: AssetImage( - context.isDarkTheme - ? 'assets/immich-text-dark.png' - : 'assets/immich-text-light.png', - ), + image: AssetImage(context.isDarkTheme ? 'assets/immich-text-dark.png' : 'assets/immich-text-light.png'), width: fontSize * 4, filterQuality: FilterQuality.high, color: context.primaryColor, diff --git a/mobile/lib/widgets/common/immich_toast.dart b/mobile/lib/widgets/common/immich_toast.dart index 945568a74c..dad8b33283 100644 --- a/mobile/lib/widgets/common/immich_toast.dart +++ b/mobile/lib/widgets/common/immich_toast.dart @@ -16,25 +16,16 @@ class ImmichToast { fToast.init(context); Color getColor(ToastType type, BuildContext context) => switch (type) { - ToastType.info => context.primaryColor, - ToastType.success => const Color.fromARGB(255, 78, 140, 124), - ToastType.error => const Color.fromARGB(255, 220, 48, 85), - }; + ToastType.info => context.primaryColor, + ToastType.success => const Color.fromARGB(255, 78, 140, 124), + ToastType.error => const Color.fromARGB(255, 220, 48, 85), + }; Icon getIcon(ToastType type) => switch (type) { - ToastType.info => Icon( - Icons.info_outline_rounded, - color: context.primaryColor, - ), - ToastType.success => const Icon( - Icons.check_circle_rounded, - color: Color.fromARGB(255, 78, 140, 124), - ), - ToastType.error => const Icon( - Icons.error_outline_rounded, - color: Color.fromARGB(255, 240, 162, 156), - ), - }; + ToastType.info => Icon(Icons.info_outline_rounded, color: context.primaryColor), + ToastType.success => const Icon(Icons.check_circle_rounded, color: Color.fromARGB(255, 78, 140, 124)), + ToastType.error => const Icon(Icons.error_outline_rounded, color: Color.fromARGB(255, 240, 162, 156)), + }; fToast.showToast( child: Container( @@ -42,26 +33,17 @@ class ImmichToast { decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(16.0)), color: context.colorScheme.surfaceContainer, - border: Border.all( - color: context.colorScheme.outline.withValues(alpha: .5), - width: 1, - ), + border: Border.all(color: context.colorScheme.outline.withValues(alpha: .5), width: 1), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ getIcon(toastType), - const SizedBox( - width: 12.0, - ), + const SizedBox(width: 12.0), Flexible( child: Text( msg, - style: TextStyle( - color: getColor(toastType, context), - fontWeight: FontWeight.w600, - fontSize: 14, - ), + style: TextStyle(color: getColor(toastType, context), fontWeight: FontWeight.w600, fontSize: 14), ), ), ], diff --git a/mobile/lib/widgets/common/local_album_sliver_app_bar.dart b/mobile/lib/widgets/common/local_album_sliver_app_bar.dart index 4880865e66..85d8f4bb01 100644 --- a/mobile/lib/widgets/common/local_album_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/local_album_sliver_app_bar.dart @@ -12,14 +12,10 @@ class LocalAlbumsSliverAppBar extends StatelessWidget { pinned: true, snap: false, backgroundColor: context.colorScheme.surfaceContainer, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(5)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))), automaticallyImplyLeading: true, centerTitle: true, - title: Text( - "on_this_device".t(context: context), - ), + title: Text("on_this_device".t(context: context)), ); } } diff --git a/mobile/lib/widgets/common/location_picker.dart b/mobile/lib/widgets/common/location_picker.dart index 7bfdf296bb..1f63299dd7 100644 --- a/mobile/lib/widgets/common/location_picker.dart +++ b/mobile/lib/widgets/common/location_picker.dart @@ -9,16 +9,11 @@ import 'package:immich_mobile/widgets/map/map_thumbnail.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; -Future showLocationPicker({ - required BuildContext context, - LatLng? initialLatLng, -}) { +Future showLocationPicker({required BuildContext context, LatLng? initialLatLng}) { return showDialog( context: context, useRootNavigator: false, - builder: (ctx) => _LocationPicker( - initialLatLng: initialLatLng, - ), + builder: (ctx) => _LocationPicker(initialLatLng: initialLatLng), ); } @@ -27,9 +22,7 @@ enum _LocationPickerMode { map, manual } class _LocationPicker extends HookWidget { final LatLng? initialLatLng; - const _LocationPicker({ - this.initialLatLng, - }); + const _LocationPicker({this.initialLatLng}); @override Widget build(BuildContext context) { @@ -39,9 +32,7 @@ class _LocationPicker extends HookWidget { final pickerMode = useState(_LocationPickerMode.map); Future onMapTap() async { - final newLatLng = await context.pushRoute( - MapLocationPickerRoute(initialLatLng: latlng), - ); + final newLatLng = await context.pushRoute(MapLocationPickerRoute(initialLatLng: latlng)); if (newLatLng != null) { latitude.value = newLatLng.latitude; longitude.value = newLatLng.longitude; @@ -56,8 +47,7 @@ class _LocationPicker extends HookWidget { ? _MapPicker( key: ValueKey(latlng), latlng: latlng, - onModeSwitch: () => - pickerMode.value = _LocationPickerMode.manual, + onModeSwitch: () => pickerMode.value = _LocationPickerMode.manual, onMapTap: onMapTap, ) : _ManualPicker( @@ -82,10 +72,7 @@ class _LocationPicker extends HookWidget { onPressed: () => context.maybePop(latlng), child: Text( "action_common_update", - style: context.textTheme.bodyMedium?.copyWith( - fontWeight: FontWeight.w600, - color: context.primaryColor, - ), + style: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600, color: context.primaryColor), ).tr(), ), ], @@ -130,10 +117,7 @@ class _ManualPickerInput extends HookWidget { autofocus: false, decoration: InputDecoration( labelText: decorationText.tr(), - labelStyle: TextStyle( - fontWeight: FontWeight.bold, - color: context.primaryColor, - ), + labelStyle: TextStyle(fontWeight: FontWeight.bold, color: context.primaryColor), floatingLabelBehavior: FloatingLabelBehavior.auto, border: const OutlineInputBorder(), hintText: hintText.tr(), @@ -141,8 +125,7 @@ class _ManualPickerInput extends HookWidget { errorText: isValid.value ? null : errorText.tr(), ), onEditingComplete: onEditingComplete, - keyboardType: - const TextInputType.numberWithOptions(decimal: true, signed: true), + keyboardType: const TextInputType.numberWithOptions(decimal: true, signed: true), inputFormatters: [LengthLimitingTextInputFormatter(8)], onTapOutside: (_) => focusNode.unfocus(), ); @@ -190,10 +173,7 @@ class _ManualPicker extends HookWidget { return Column( mainAxisSize: MainAxisSize.min, children: [ - const Text( - "edit_location_dialog_title", - textAlign: TextAlign.center, - ).tr(), + const Text("edit_location_dialog_title", textAlign: TextAlign.center).tr(), const SizedBox(height: 12), TextButton.icon( icon: const Text("location_picker_choose_on_map").tr(), @@ -230,27 +210,17 @@ class _MapPicker extends StatelessWidget { final Function() onModeSwitch; final Function() onMapTap; - const _MapPicker({ - required this.latlng, - required this.onModeSwitch, - required this.onMapTap, - super.key, - }); + const _MapPicker({required this.latlng, required this.onModeSwitch, required this.onMapTap, super.key}); @override Widget build(BuildContext context) { return Column( mainAxisSize: MainAxisSize.min, children: [ - const Text( - "edit_location_dialog_title", - textAlign: TextAlign.center, - ).tr(), + const Text("edit_location_dialog_title", textAlign: TextAlign.center).tr(), const SizedBox(height: 12), TextButton.icon( - icon: Text( - "${latlng.latitude.toStringAsFixed(4)}, ${latlng.longitude.toStringAsFixed(4)}", - ), + icon: Text("${latlng.latitude.toStringAsFixed(4)}, ${latlng.longitude.toStringAsFixed(4)}"), label: const Icon(Icons.edit_outlined, size: 16), onPressed: onModeSwitch, ), diff --git a/mobile/lib/widgets/common/mesmerizing_sliver_app_bar.dart b/mobile/lib/widgets/common/mesmerizing_sliver_app_bar.dart index eace57fe5c..359b400456 100644 --- a/mobile/lib/widgets/common/mesmerizing_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/mesmerizing_sliver_app_bar.dart @@ -14,22 +14,15 @@ import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; class MesmerizingSliverAppBar extends ConsumerStatefulWidget { - const MesmerizingSliverAppBar({ - super.key, - required this.title, - this.icon = Icons.camera, - }); + const MesmerizingSliverAppBar({super.key, required this.title, this.icon = Icons.camera}); final String title; final IconData icon; - @override - ConsumerState createState() => - _MesmerizingSliverAppBarState(); + ConsumerState createState() => _MesmerizingSliverAppBarState(); } -class _MesmerizingSliverAppBarState - extends ConsumerState { +class _MesmerizingSliverAppBarState extends ConsumerState { double _scrollProgress = 0.0; double _calculateScrollProgress(FlexibleSpaceBarSettings? settings) { @@ -42,14 +35,12 @@ class _MesmerizingSliverAppBarState return 1.0; } - return (1.0 - (settings.currentExtent - settings.minExtent) / deltaExtent) - .clamp(0.0, 1.0); + return (1.0 - (settings.currentExtent - settings.minExtent) / deltaExtent).clamp(0.0, 1.0); } @override Widget build(BuildContext context) { - final isMultiSelectEnabled = - ref.watch(multiSelectProvider.select((s) => s.isEnabled)); + final isMultiSelectEnabled = ref.watch(multiSelectProvider.select((s) => s.isEnabled)); return isMultiSelectEnabled ? SliverToBoxAdapter( @@ -66,26 +57,12 @@ class _MesmerizingSliverAppBarState elevation: 0, leading: IconButton( icon: Icon( - Platform.isIOS - ? Icons.arrow_back_ios_new_rounded - : Icons.arrow_back, - color: Color.lerp( - Colors.white, - context.primaryColor, - _scrollProgress, - ), + Platform.isIOS ? Icons.arrow_back_ios_new_rounded : Icons.arrow_back, + color: Color.lerp(Colors.white, context.primaryColor, _scrollProgress), shadows: [ _scrollProgress < 0.95 - ? Shadow( - offset: const Offset(0, 2), - blurRadius: 5, - color: Colors.black.withValues(alpha: 0.5), - ) - : const Shadow( - offset: Offset(0, 2), - blurRadius: 0, - color: Colors.transparent, - ), + ? Shadow(offset: const Offset(0, 2), blurRadius: 5, color: Colors.black.withValues(alpha: 0.5)) + : const Shadow(offset: Offset(0, 2), blurRadius: 0, color: Colors.transparent), ], ), onPressed: () { @@ -94,8 +71,7 @@ class _MesmerizingSliverAppBarState ), flexibleSpace: Builder( builder: (context) { - final settings = context.dependOnInheritedWidgetOfExactType< - FlexibleSpaceBarSettings>(); + final settings = context.dependOnInheritedWidgetOfExactType(); final scrollProgress = _calculateScrollProgress(settings); // Update scroll progress for the leading button @@ -114,11 +90,7 @@ class _MesmerizingSliverAppBarState child: scrollProgress > 0.95 ? Text( widget.title, - style: TextStyle( - color: context.primaryColor, - fontWeight: FontWeight.w600, - fontSize: 18, - ), + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.w600, fontSize: 18), ) : null, ), @@ -139,19 +111,13 @@ class _ExpandedBackground extends ConsumerStatefulWidget { final String title; final IconData icon; - const _ExpandedBackground({ - required this.scrollProgress, - required this.title, - required this.icon, - }); + const _ExpandedBackground({required this.scrollProgress, required this.title, required this.icon}); @override - ConsumerState<_ExpandedBackground> createState() => - _ExpandedBackgroundState(); + ConsumerState<_ExpandedBackground> createState() => _ExpandedBackgroundState(); } -class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> - with SingleTickerProviderStateMixin { +class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with SingleTickerProviderStateMixin { late AnimationController _slideController; late Animation _slideAnimation; @@ -159,20 +125,12 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> void initState() { super.initState(); - _slideController = AnimationController( - duration: const Duration(milliseconds: 800), - vsync: this, - ); + _slideController = AnimationController(duration: const Duration(milliseconds: 800), vsync: this); _slideAnimation = Tween( begin: const Offset(0, 1.5), end: Offset.zero, - ).animate( - CurvedAnimation( - parent: _slideController, - curve: Curves.easeOutCubic, - ), - ); + ).animate(CurvedAnimation(parent: _slideController, curve: Curves.easeOutCubic)); Future.delayed(const Duration(milliseconds: 100), () { if (mounted) { @@ -198,10 +156,7 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> offset: Offset(0, widget.scrollProgress * 50), child: Transform.scale( scale: 1.4 - (widget.scrollProgress * 0.2), - child: _RandomAssetBackground( - timelineService: timelineService, - icon: widget.icon, - ), + child: _RandomAssetBackground(timelineService: timelineService, icon: widget.icon), ), ), Container( @@ -212,9 +167,7 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> colors: [ Colors.transparent, Colors.transparent, - Colors.black.withValues( - alpha: 0.6 + (widget.scrollProgress * 0.2), - ), + Colors.black.withValues(alpha: 0.6 + (widget.scrollProgress * 0.2)), ], stops: const [0.0, 0.65, 1.0], ), @@ -242,21 +195,12 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> fontSize: 36, fontWeight: FontWeight.bold, letterSpacing: 0.5, - shadows: [ - Shadow( - offset: Offset(0, 2), - blurRadius: 12, - color: Colors.black45, - ), - ], + shadows: [Shadow(offset: Offset(0, 2), blurRadius: 12, color: Colors.black45)], ), ), ), ), - AnimatedContainer( - duration: const Duration(milliseconds: 300), - child: const _ItemCountText(), - ), + AnimatedContainer(duration: const Duration(milliseconds: 300), child: const _ItemCountText()), ], ), ), @@ -279,8 +223,7 @@ class _ItemCountTextState extends ConsumerState<_ItemCountText> { @override void initState() { super.initState(); - _reloadSubscription = - EventStream.shared.listen((_) => setState(() {})); + _reloadSubscription = EventStream.shared.listen((_) => setState(() {})); } @override @@ -291,26 +234,15 @@ class _ItemCountTextState extends ConsumerState<_ItemCountText> { @override Widget build(BuildContext context) { - final assetCount = ref.watch( - timelineServiceProvider.select((s) => s.totalAssets), - ); + final assetCount = ref.watch(timelineServiceProvider.select((s) => s.totalAssets)); return Text( - 'items_count'.t( - context: context, - args: {"count": assetCount}, - ), + 'items_count'.t(context: context, args: {"count": assetCount}), style: context.textTheme.labelLarge?.copyWith( // letterSpacing: 0.2, fontWeight: FontWeight.bold, color: Colors.white, - shadows: [ - const Shadow( - offset: Offset(0, 1), - blurRadius: 6, - color: Colors.black45, - ), - ], + shadows: [const Shadow(offset: Offset(0, 1), blurRadius: 6, color: Colors.black45)], ), ); } @@ -320,17 +252,13 @@ class _RandomAssetBackground extends StatefulWidget { final TimelineService timelineService; final IconData icon; - const _RandomAssetBackground({ - required this.timelineService, - required this.icon, - }); + const _RandomAssetBackground({required this.timelineService, required this.icon}); @override State<_RandomAssetBackground> createState() => _RandomAssetBackgroundState(); } -class _RandomAssetBackgroundState extends State<_RandomAssetBackground> - with TickerProviderStateMixin { +class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with TickerProviderStateMixin { late AnimationController _zoomController; late AnimationController _crossFadeController; late Animation _zoomAnimation; @@ -344,50 +272,26 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> void initState() { super.initState(); - _zoomController = AnimationController( - duration: const Duration(seconds: 12), - vsync: this, - ); + _zoomController = AnimationController(duration: const Duration(seconds: 12), vsync: this); - _crossFadeController = AnimationController( - duration: const Duration(milliseconds: 1200), - vsync: this, - ); + _crossFadeController = AnimationController(duration: const Duration(milliseconds: 1200), vsync: this); _zoomAnimation = Tween( begin: 1.0, end: 1.2, - ).animate( - CurvedAnimation( - parent: _zoomController, - curve: Curves.easeInOut, - ), - ); + ).animate(CurvedAnimation(parent: _zoomController, curve: Curves.easeInOut)); _panAnimation = Tween( begin: Offset.zero, end: const Offset(0.5, -0.5), - ).animate( - CurvedAnimation( - parent: _zoomController, - curve: Curves.easeInOut, - ), - ); + ).animate(CurvedAnimation(parent: _zoomController, curve: Curves.easeInOut)); _crossFadeAnimation = Tween( begin: 0.0, end: 1.0, - ).animate( - CurvedAnimation( - parent: _crossFadeController, - curve: Curves.easeInOutCubic, - ), - ); + ).animate(CurvedAnimation(parent: _crossFadeController, curve: Curves.easeInOutCubic)); - Future.delayed( - Durations.medium1, - () => _loadFirstAsset(), - ); + Future.delayed(Durations.medium1, () => _loadFirstAsset()); } @override @@ -477,9 +381,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> } return AnimatedBuilder( - animation: Listenable.merge( - [_zoomAnimation, _panAnimation, _crossFadeAnimation], - ), + animation: Listenable.merge([_zoomAnimation, _panAnimation, _crossFadeAnimation]), builder: (context, child) { return Transform.scale( scale: _zoomAnimation.value, @@ -501,8 +403,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> alignment: Alignment.topRight, image: getFullImageProvider(_currentAsset!), fit: BoxFit.cover, - frameBuilder: - (context, child, frame, wasSynchronouslyLoaded) { + frameBuilder: (context, child, frame, wasSynchronouslyLoaded) { if (wasSynchronouslyLoaded || frame != null) { return child; } @@ -512,11 +413,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> return SizedBox( width: double.infinity, height: double.infinity, - child: Icon( - Icons.error_outline_rounded, - size: 24, - color: Colors.red[300], - ), + child: Icon(Icons.error_outline_rounded, size: 24, color: Colors.red[300]), ); }, ), @@ -533,8 +430,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> alignment: Alignment.topRight, image: getFullImageProvider(_nextAsset!), fit: BoxFit.cover, - frameBuilder: - (context, child, frame, wasSynchronouslyLoaded) { + frameBuilder: (context, child, frame, wasSynchronouslyLoaded) { if (wasSynchronouslyLoaded || frame != null) { return child; } @@ -544,11 +440,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> return SizedBox( width: double.infinity, height: double.infinity, - child: Icon( - Icons.error_outline_rounded, - size: 24, - color: Colors.red[300], - ), + child: Icon(Icons.error_outline_rounded, size: 24, color: Colors.red[300]), ); }, ), diff --git a/mobile/lib/widgets/common/person_sliver_app_bar.dart b/mobile/lib/widgets/common/person_sliver_app_bar.dart new file mode 100644 index 0000000000..1cc117139d --- /dev/null +++ b/mobile/lib/widgets/common/person_sliver_app_bar.dart @@ -0,0 +1,562 @@ +import 'dart:async'; +import 'dart:io'; +import 'dart:ui'; + +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/models/person.model.dart'; +import 'package:immich_mobile/domain/models/timeline.model.dart'; +import 'package:immich_mobile/domain/services/timeline.service.dart'; +import 'package:immich_mobile/domain/utils/event_stream.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/presentation/widgets/images/image_provider.dart'; +import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart'; +import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/services/api.service.dart'; +import 'package:immich_mobile/utils/people.utils.dart'; +import 'package:immich_mobile/utils/image_url_builder.dart'; + +class PersonSliverAppBar extends ConsumerStatefulWidget { + const PersonSliverAppBar({ + super.key, + required this.person, + required this.onNameTap, + required this.onShowOptions, + required this.onBirthdayTap, + }); + + final DriftPerson person; + final VoidCallback onNameTap; + final VoidCallback onBirthdayTap; + final VoidCallback onShowOptions; + + @override + ConsumerState createState() => _MesmerizingSliverAppBarState(); +} + +class _MesmerizingSliverAppBarState extends ConsumerState { + double _scrollProgress = 0.0; + + double _calculateScrollProgress(FlexibleSpaceBarSettings? settings) { + if (settings?.maxExtent == null || settings?.minExtent == null) { + return 1.0; + } + + final deltaExtent = settings!.maxExtent - settings.minExtent; + if (deltaExtent <= 0.0) { + return 1.0; + } + + return (1.0 - (settings.currentExtent - settings.minExtent) / deltaExtent).clamp(0.0, 1.0); + } + + @override + Widget build(BuildContext context) { + final isMultiSelectEnabled = ref.watch(multiSelectProvider.select((s) => s.isEnabled)); + Color? actionIconColor = Color.lerp(Colors.white, context.primaryColor, _scrollProgress); + List actionIconShadows = [ + if (_scrollProgress < 0.95) + Shadow(offset: const Offset(0, 2), blurRadius: 5, color: Colors.black.withValues(alpha: 0.5)) + else + const Shadow(offset: Offset(0, 2), blurRadius: 0, color: Colors.transparent), + ]; + + return isMultiSelectEnabled + ? SliverToBoxAdapter( + child: switch (_scrollProgress) { + < 0.8 => const SizedBox(height: 120), + _ => const SizedBox(height: 352), + }, + ) + : SliverAppBar( + expandedHeight: 300.0, + floating: false, + pinned: true, + snap: false, + elevation: 0, + leading: IconButton( + icon: Icon( + Platform.isIOS ? Icons.arrow_back_ios_new_rounded : Icons.arrow_back, + color: Color.lerp(Colors.white, context.primaryColor, _scrollProgress), + shadows: [ + _scrollProgress < 0.95 + ? Shadow(offset: const Offset(0, 2), blurRadius: 5, color: Colors.black.withValues(alpha: 0.5)) + : const Shadow(offset: Offset(0, 2), blurRadius: 0, color: Colors.transparent), + ], + ), + onPressed: () { + context.pop(); + }, + ), + actions: [ + IconButton( + icon: Icon(Icons.more_vert, color: actionIconColor, shadows: actionIconShadows), + onPressed: widget.onShowOptions, + ), + ], + flexibleSpace: Builder( + builder: (context) { + final settings = context.dependOnInheritedWidgetOfExactType(); + final scrollProgress = _calculateScrollProgress(settings); + + // Update scroll progress for the leading button + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted && _scrollProgress != scrollProgress) { + setState(() { + _scrollProgress = scrollProgress; + }); + } + }); + + return FlexibleSpaceBar( + centerTitle: true, + title: AnimatedSwitcher( + duration: const Duration(milliseconds: 200), + child: scrollProgress > 0.95 + ? Text( + widget.person.name, + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.w600, fontSize: 18), + ) + : null, + ), + background: _ExpandedBackground( + scrollProgress: scrollProgress, + person: widget.person, + onNameTap: widget.onNameTap, + onBirthdayTap: widget.onBirthdayTap, + ), + ); + }, + ), + ); + } +} + +class _ExpandedBackground extends ConsumerStatefulWidget { + final double scrollProgress; + final DriftPerson person; + final VoidCallback onNameTap; + final VoidCallback onBirthdayTap; + + const _ExpandedBackground({ + required this.scrollProgress, + required this.person, + required this.onNameTap, + required this.onBirthdayTap, + }); + + @override + ConsumerState<_ExpandedBackground> createState() => _ExpandedBackgroundState(); +} + +class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with SingleTickerProviderStateMixin { + late AnimationController _slideController; + late Animation _slideAnimation; + + @override + void initState() { + super.initState(); + + _slideController = AnimationController(duration: const Duration(milliseconds: 800), vsync: this); + + _slideAnimation = Tween( + begin: const Offset(0, 1.5), + end: Offset.zero, + ).animate(CurvedAnimation(parent: _slideController, curve: Curves.easeOutCubic)); + + Future.delayed(const Duration(milliseconds: 100), () { + if (mounted) { + _slideController.forward(); + } + }); + } + + @override + void dispose() { + _slideController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final timelineService = ref.watch(timelineServiceProvider); + + return Stack( + fit: StackFit.expand, + children: [ + Transform.translate( + offset: Offset(0, widget.scrollProgress * 50), + child: Transform.scale( + scale: 1.4 - (widget.scrollProgress * 0.2), + child: _RandomAssetBackground(timelineService: timelineService), + ), + ), + ClipRect( + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: widget.scrollProgress * 2.0, sigmaY: widget.scrollProgress * 2.0), + child: Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Colors.black.withValues(alpha: 0.05), + Colors.transparent, + Colors.black.withValues(alpha: 0.3), + Colors.black.withValues(alpha: 0.6 + (widget.scrollProgress * 0.25)), + ], + stops: const [0.0, 0.15, 0.55, 1.0], + ), + ), + ), + ), + ), + Positioned( + bottom: 16, + left: 16, + right: 16, + child: SlideTransition( + position: _slideAnimation, + child: Row( + children: [ + SizedBox( + height: 84, + width: 84, + child: Material( + shape: const CircleBorder(side: BorderSide(color: Colors.grey, width: 1.0)), + elevation: 3, + child: CircleAvatar( + maxRadius: 84 / 2, + backgroundImage: NetworkImage( + getFaceThumbnailUrl(widget.person.id), + headers: ApiService.getRequestHeaders(), + ), + ), + ), + ), + const SizedBox(width: 8), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + GestureDetector( + onTap: () => widget.onNameTap.call(), + child: SizedBox( + width: double.infinity, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: widget.person.name.isNotEmpty + ? Text( + widget.person.name, + maxLines: 1, + style: const TextStyle( + color: Colors.white, + fontSize: 36, + fontWeight: FontWeight.bold, + letterSpacing: 0.5, + shadows: [Shadow(offset: Offset(0, 2), blurRadius: 12, color: Colors.black45)], + ), + ) + : Text( + 'add_a_name'.tr(), + style: context.textTheme.titleLarge?.copyWith( + color: Colors.grey[400], + fontSize: 36, + decoration: TextDecoration.underline, + decorationColor: Colors.white, + ), + ), + ), + ), + ), + AnimatedContainer(duration: const Duration(milliseconds: 300), child: const _ItemCountText()), + const SizedBox(height: 8), + GestureDetector( + onTap: widget.onBirthdayTap, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Icon(Icons.cake_rounded, color: Colors.white, size: 14), + const SizedBox(width: 4), + + if (widget.person.birthDate != null) + Text( + "${DateFormat.yMMMd(context.locale.toString()).format(widget.person.birthDate!)} (${formatAge(widget.person.birthDate!, DateTime.now())})", + style: context.textTheme.labelLarge?.copyWith( + color: Colors.white, + height: 1.2, + fontSize: 14, + ), + ) + else + Text( + 'add_birthday'.tr(), + style: context.textTheme.labelLarge?.copyWith( + color: Colors.grey[400], + height: 1.2, + fontSize: 14, + decoration: TextDecoration.underline, + decorationColor: Colors.white, + ), + ), + ], + ), + ), + ], + ), + ), + ], + ), + ), + ), + ], + ); + } +} + +class _ItemCountText extends ConsumerStatefulWidget { + const _ItemCountText(); + + @override + ConsumerState<_ItemCountText> createState() => _ItemCountTextState(); +} + +class _ItemCountTextState extends ConsumerState<_ItemCountText> { + StreamSubscription? _reloadSubscription; + + @override + void initState() { + super.initState(); + _reloadSubscription = EventStream.shared.listen((_) => setState(() {})); + } + + @override + void dispose() { + _reloadSubscription?.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final assetCount = ref.watch(timelineServiceProvider.select((s) => s.totalAssets)); + + return Text( + 'items_count'.t(context: context, args: {"count": assetCount}), + style: context.textTheme.labelLarge?.copyWith( + fontWeight: FontWeight.bold, + color: Colors.white, + shadows: [const Shadow(offset: Offset(0, 1), blurRadius: 6, color: Colors.black45)], + ), + ); + } +} + +class _RandomAssetBackground extends StatefulWidget { + final TimelineService timelineService; + + const _RandomAssetBackground({required this.timelineService}); + + @override + State<_RandomAssetBackground> createState() => _RandomAssetBackgroundState(); +} + +class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with TickerProviderStateMixin { + late AnimationController _zoomController; + late AnimationController _crossFadeController; + late Animation _zoomAnimation; + late Animation _panAnimation; + late Animation _crossFadeAnimation; + BaseAsset? _currentAsset; + BaseAsset? _nextAsset; + bool _isZoomingIn = true; + + @override + void initState() { + super.initState(); + + _zoomController = AnimationController(duration: const Duration(seconds: 12), vsync: this); + + _crossFadeController = AnimationController(duration: const Duration(milliseconds: 1200), vsync: this); + + _zoomAnimation = Tween( + begin: 1.0, + end: 1.2, + ).animate(CurvedAnimation(parent: _zoomController, curve: Curves.easeInOut)); + + _panAnimation = Tween( + begin: Offset.zero, + end: const Offset(0.5, -0.5), + ).animate(CurvedAnimation(parent: _zoomController, curve: Curves.easeInOut)); + + _crossFadeAnimation = Tween( + begin: 0.0, + end: 1.0, + ).animate(CurvedAnimation(parent: _crossFadeController, curve: Curves.easeInOutCubic)); + + Future.delayed(Durations.medium1, () => _loadFirstAsset()); + } + + @override + void dispose() { + _zoomController.dispose(); + _crossFadeController.dispose(); + super.dispose(); + } + + void _startAnimationCycle() { + if (_isZoomingIn) { + _zoomController.forward().then((_) { + _loadNextAsset(); + }); + } else { + _zoomController.reverse().then((_) { + _loadNextAsset(); + }); + } + } + + Future _loadFirstAsset() async { + if (!mounted) { + return; + } + + if (widget.timelineService.totalAssets == 0) { + setState(() { + _currentAsset = null; + }); + + return; + } + + setState(() { + _currentAsset = widget.timelineService.getRandomAsset(); + }); + + await _crossFadeController.forward(); + + if (_zoomController.status == AnimationStatus.dismissed) { + if (_isZoomingIn) { + _zoomController.reset(); + } else { + _zoomController.value = 1.0; + } + _startAnimationCycle(); + } + } + + Future _loadNextAsset() async { + if (!mounted) { + return; + } + + try { + if (widget.timelineService.totalAssets > 1) { + // Load next asset while keeping current one visible + final nextAsset = widget.timelineService.getRandomAsset(); + + setState(() { + _nextAsset = nextAsset; + }); + + await _crossFadeController.reverse(); + setState(() { + _currentAsset = _nextAsset; + _nextAsset = null; + }); + + _crossFadeController.value = 1.0; + + _isZoomingIn = !_isZoomingIn; + + _startAnimationCycle(); + } + } catch (e) { + _zoomController.reset(); + _startAnimationCycle(); + } + } + + @override + Widget build(BuildContext context) { + if (widget.timelineService.totalAssets == 0) { + return const SizedBox.shrink(); + } + + return AnimatedBuilder( + animation: Listenable.merge([_zoomAnimation, _panAnimation, _crossFadeAnimation]), + builder: (context, child) { + return Transform.scale( + scale: _zoomAnimation.value, + filterQuality: Platform.isAndroid ? FilterQuality.low : null, + child: Transform.translate( + offset: _panAnimation.value, + filterQuality: Platform.isAndroid ? FilterQuality.low : null, + child: Stack( + fit: StackFit.expand, + children: [ + // Current image + if (_currentAsset != null) + Opacity( + opacity: _crossFadeAnimation.value, + child: SizedBox( + width: double.infinity, + height: double.infinity, + child: Image( + alignment: Alignment.topRight, + image: getFullImageProvider(_currentAsset!), + fit: BoxFit.cover, + frameBuilder: (context, child, frame, wasSynchronouslyLoaded) { + if (wasSynchronouslyLoaded || frame != null) { + return child; + } + return Container(); + }, + errorBuilder: (context, error, stackTrace) { + return SizedBox( + width: double.infinity, + height: double.infinity, + child: Icon(Icons.error_outline_rounded, size: 24, color: Colors.red[300]), + ); + }, + ), + ), + ), + + if (_nextAsset != null) + Opacity( + opacity: 1.0 - _crossFadeAnimation.value, + child: SizedBox( + width: double.infinity, + height: double.infinity, + child: Image( + alignment: Alignment.topRight, + image: getFullImageProvider(_nextAsset!), + fit: BoxFit.cover, + frameBuilder: (context, child, frame, wasSynchronouslyLoaded) { + if (wasSynchronouslyLoaded || frame != null) { + return child; + } + return const SizedBox.shrink(); + }, + errorBuilder: (context, error, stackTrace) { + return SizedBox( + width: double.infinity, + height: double.infinity, + child: Icon(Icons.error_outline_rounded, size: 24, color: Colors.red[300]), + ); + }, + ), + ), + ), + ], + ), + ), + ); + }, + ); + } +} diff --git a/mobile/lib/widgets/common/remote_album_sliver_app_bar.dart b/mobile/lib/widgets/common/remote_album_sliver_app_bar.dart index 41eed09d8c..2efe8a3ce1 100644 --- a/mobile/lib/widgets/common/remote_album_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/remote_album_sliver_app_bar.dart @@ -35,12 +35,10 @@ class RemoteAlbumSliverAppBar extends ConsumerStatefulWidget { final void Function()? onEditTitle; @override - ConsumerState createState() => - _MesmerizingSliverAppBarState(); + ConsumerState createState() => _MesmerizingSliverAppBarState(); } -class _MesmerizingSliverAppBarState - extends ConsumerState { +class _MesmerizingSliverAppBarState extends ConsumerState { double _scrollProgress = 0.0; double _calculateScrollProgress(FlexibleSpaceBarSettings? settings) { @@ -53,39 +51,25 @@ class _MesmerizingSliverAppBarState return 1.0; } - return (1.0 - (settings.currentExtent - settings.minExtent) / deltaExtent) - .clamp(0.0, 1.0); + return (1.0 - (settings.currentExtent - settings.minExtent) / deltaExtent).clamp(0.0, 1.0); } @override Widget build(BuildContext context) { - final isMultiSelectEnabled = - ref.watch(multiSelectProvider.select((s) => s.isEnabled)); + final isMultiSelectEnabled = ref.watch(multiSelectProvider.select((s) => s.isEnabled)); final currentAlbum = ref.watch(currentRemoteAlbumProvider); if (currentAlbum == null) { return const SliverToBoxAdapter(child: SizedBox.shrink()); } - Color? actionIconColor = Color.lerp( - Colors.white, - context.primaryColor, - _scrollProgress, - ); + Color? actionIconColor = Color.lerp(Colors.white, context.primaryColor, _scrollProgress); List actionIconShadows = [ if (_scrollProgress < 0.95) - Shadow( - offset: const Offset(0, 2), - blurRadius: 5, - color: Colors.black.withValues(alpha: 0.5), - ) + Shadow(offset: const Offset(0, 2), blurRadius: 5, color: Colors.black.withValues(alpha: 0.5)) else - const Shadow( - offset: Offset(0, 2), - blurRadius: 0, - color: Colors.transparent, - ), + const Shadow(offset: Offset(0, 2), blurRadius: 0, color: Colors.transparent), ]; return isMultiSelectEnabled @@ -103,9 +87,7 @@ class _MesmerizingSliverAppBarState elevation: 0, leading: IconButton( icon: Icon( - Platform.isIOS - ? Icons.arrow_back_ios_new_rounded - : Icons.arrow_back, + Platform.isIOS ? Icons.arrow_back_ios_new_rounded : Icons.arrow_back, color: actionIconColor, shadows: actionIconShadows, ), @@ -117,27 +99,18 @@ class _MesmerizingSliverAppBarState actions: [ if (widget.onToggleAlbumOrder != null) IconButton( - icon: Icon( - Icons.swap_vert_rounded, - color: actionIconColor, - shadows: actionIconShadows, - ), + icon: Icon(Icons.swap_vert_rounded, color: actionIconColor, shadows: actionIconShadows), onPressed: widget.onToggleAlbumOrder, ), if (widget.onShowOptions != null) IconButton( - icon: Icon( - Icons.more_vert, - color: actionIconColor, - shadows: actionIconShadows, - ), + icon: Icon(Icons.more_vert, color: actionIconColor, shadows: actionIconShadows), onPressed: widget.onShowOptions, ), ], flexibleSpace: Builder( builder: (context) { - final settings = context.dependOnInheritedWidgetOfExactType< - FlexibleSpaceBarSettings>(); + final settings = context.dependOnInheritedWidgetOfExactType(); final scrollProgress = _calculateScrollProgress(settings); // Update scroll progress for the leading button @@ -156,11 +129,7 @@ class _MesmerizingSliverAppBarState child: scrollProgress > 0.95 ? Text( currentAlbum.name, - style: TextStyle( - color: context.primaryColor, - fontWeight: FontWeight.w600, - fontSize: 18, - ), + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.w600, fontSize: 18), ) : null, ), @@ -181,19 +150,13 @@ class _ExpandedBackground extends ConsumerStatefulWidget { final IconData icon; final void Function()? onEditTitle; - const _ExpandedBackground({ - required this.scrollProgress, - required this.icon, - this.onEditTitle, - }); + const _ExpandedBackground({required this.scrollProgress, required this.icon, this.onEditTitle}); @override - ConsumerState<_ExpandedBackground> createState() => - _ExpandedBackgroundState(); + ConsumerState<_ExpandedBackground> createState() => _ExpandedBackgroundState(); } -class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> - with SingleTickerProviderStateMixin { +class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with SingleTickerProviderStateMixin { late AnimationController _slideController; late Animation _slideAnimation; @@ -201,20 +164,12 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> void initState() { super.initState(); - _slideController = AnimationController( - duration: const Duration(milliseconds: 800), - vsync: this, - ); + _slideController = AnimationController(duration: const Duration(milliseconds: 800), vsync: this); _slideAnimation = Tween( begin: const Offset(0, 1.5), end: Offset.zero, - ).animate( - CurvedAnimation( - parent: _slideController, - curve: Curves.easeOutCubic, - ), - ); + ).animate(CurvedAnimation(parent: _slideController, curve: Curves.easeOutCubic)); Future.delayed(const Duration(milliseconds: 100), () { if (mounted) { @@ -238,9 +193,7 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> return const SizedBox.shrink(); } - final dateRange = ref.watch( - remoteAlbumDateRangeProvider(currentAlbum.id), - ); + final dateRange = ref.watch(remoteAlbumDateRangeProvider(currentAlbum.id)); return Stack( fit: StackFit.expand, children: [ @@ -248,18 +201,12 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> offset: Offset(0, widget.scrollProgress * 50), child: Transform.scale( scale: 1.4 - (widget.scrollProgress * 0.2), - child: _RandomAssetBackground( - timelineService: timelineService, - icon: widget.icon, - ), + child: _RandomAssetBackground(timelineService: timelineService, icon: widget.icon), ), ), ClipRect( child: BackdropFilter( - filter: ImageFilter.blur( - sigmaX: widget.scrollProgress * 2.0, - sigmaY: widget.scrollProgress * 2.0, - ), + filter: ImageFilter.blur(sigmaX: widget.scrollProgress * 2.0, sigmaY: widget.scrollProgress * 2.0), child: Container( decoration: BoxDecoration( gradient: LinearGradient( @@ -269,9 +216,7 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> Colors.black.withValues(alpha: 0.05), Colors.transparent, Colors.black.withValues(alpha: 0.3), - Colors.black.withValues( - alpha: 0.6 + (widget.scrollProgress * 0.25), - ), + Colors.black.withValues(alpha: 0.6 + (widget.scrollProgress * 0.25)), ], stops: const [0.0, 0.15, 0.55, 1.0], ), @@ -300,32 +245,17 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> ), style: const TextStyle( color: Colors.white, - shadows: [ - Shadow( - offset: Offset(0, 2), - blurRadius: 12, - color: Colors.black87, - ), - ], + shadows: [Shadow(offset: Offset(0, 2), blurRadius: 12, color: Colors.black87)], ), ), const Text( " • ", style: TextStyle( color: Colors.white, - shadows: [ - Shadow( - offset: Offset(0, 2), - blurRadius: 12, - color: Colors.black87, - ), - ], + shadows: [Shadow(offset: Offset(0, 2), blurRadius: 12, color: Colors.black87)], ), ), - AnimatedContainer( - duration: const Duration(milliseconds: 300), - child: const _ItemCountText(), - ), + AnimatedContainer(duration: const Duration(milliseconds: 300), child: const _ItemCountText()), ], ), GestureDetector( @@ -342,13 +272,7 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> fontSize: 36, fontWeight: FontWeight.bold, letterSpacing: 0.5, - shadows: [ - Shadow( - offset: Offset(0, 2), - blurRadius: 12, - color: Colors.black54, - ), - ], + shadows: [Shadow(offset: Offset(0, 2), blurRadius: 12, color: Colors.black54)], ), ), ), @@ -358,31 +282,20 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> GestureDetector( onTap: widget.onEditTitle, child: ConstrainedBox( - constraints: const BoxConstraints( - maxHeight: 80, - ), + constraints: const BoxConstraints(maxHeight: 80), child: SingleChildScrollView( child: Text( currentAlbum.description, style: const TextStyle( color: Colors.white, fontSize: 14, - shadows: [ - Shadow( - offset: Offset(0, 2), - blurRadius: 8, - color: Colors.black54, - ), - ], + shadows: [Shadow(offset: Offset(0, 2), blurRadius: 8, color: Colors.black54)], ), ), ), ), ), - const Padding( - padding: EdgeInsets.only(top: 8.0), - child: RemoteAlbumSharedUserIcons(), - ), + const Padding(padding: EdgeInsets.only(top: 8.0), child: RemoteAlbumSharedUserIcons()), ], ), ), @@ -405,8 +318,7 @@ class _ItemCountTextState extends ConsumerState<_ItemCountText> { @override void initState() { super.initState(); - _reloadSubscription = - EventStream.shared.listen((_) => setState(() {})); + _reloadSubscription = EventStream.shared.listen((_) => setState(() {})); } @override @@ -417,24 +329,13 @@ class _ItemCountTextState extends ConsumerState<_ItemCountText> { @override Widget build(BuildContext context) { - final assetCount = ref.watch( - timelineServiceProvider.select((s) => s.totalAssets), - ); + final assetCount = ref.watch(timelineServiceProvider.select((s) => s.totalAssets)); return Text( - 'items_count'.t( - context: context, - args: {"count": assetCount}, - ), + 'items_count'.t(context: context, args: {"count": assetCount}), style: context.textTheme.labelLarge?.copyWith( color: Colors.white, - shadows: [ - const Shadow( - offset: Offset(0, 2), - blurRadius: 12, - color: Colors.black87, - ), - ], + shadows: [const Shadow(offset: Offset(0, 2), blurRadius: 12, color: Colors.black87)], ), ); } @@ -444,17 +345,13 @@ class _RandomAssetBackground extends StatefulWidget { final TimelineService timelineService; final IconData icon; - const _RandomAssetBackground({ - required this.timelineService, - required this.icon, - }); + const _RandomAssetBackground({required this.timelineService, required this.icon}); @override State<_RandomAssetBackground> createState() => _RandomAssetBackgroundState(); } -class _RandomAssetBackgroundState extends State<_RandomAssetBackground> - with TickerProviderStateMixin { +class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with TickerProviderStateMixin { late AnimationController _zoomController; late AnimationController _crossFadeController; late Animation _zoomAnimation; @@ -468,50 +365,26 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> void initState() { super.initState(); - _zoomController = AnimationController( - duration: const Duration(seconds: 12), - vsync: this, - ); + _zoomController = AnimationController(duration: const Duration(seconds: 12), vsync: this); - _crossFadeController = AnimationController( - duration: const Duration(milliseconds: 1200), - vsync: this, - ); + _crossFadeController = AnimationController(duration: const Duration(milliseconds: 1200), vsync: this); _zoomAnimation = Tween( begin: 1.0, end: 1.2, - ).animate( - CurvedAnimation( - parent: _zoomController, - curve: Curves.easeInOut, - ), - ); + ).animate(CurvedAnimation(parent: _zoomController, curve: Curves.easeInOut)); _panAnimation = Tween( begin: Offset.zero, end: const Offset(0.5, -0.5), - ).animate( - CurvedAnimation( - parent: _zoomController, - curve: Curves.easeInOut, - ), - ); + ).animate(CurvedAnimation(parent: _zoomController, curve: Curves.easeInOut)); _crossFadeAnimation = Tween( begin: 0.0, end: 1.0, - ).animate( - CurvedAnimation( - parent: _crossFadeController, - curve: Curves.easeInOutCubic, - ), - ); + ).animate(CurvedAnimation(parent: _crossFadeController, curve: Curves.easeInOutCubic)); - Future.delayed( - Durations.medium1, - () => _loadFirstAsset(), - ); + Future.delayed(Durations.medium1, () => _loadFirstAsset()); } @override @@ -601,9 +474,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> } return AnimatedBuilder( - animation: Listenable.merge( - [_zoomAnimation, _panAnimation, _crossFadeAnimation], - ), + animation: Listenable.merge([_zoomAnimation, _panAnimation, _crossFadeAnimation]), builder: (context, child) { return Transform.scale( scale: _zoomAnimation.value, @@ -625,8 +496,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> alignment: Alignment.topRight, image: getFullImageProvider(_currentAsset!), fit: BoxFit.cover, - frameBuilder: - (context, child, frame, wasSynchronouslyLoaded) { + frameBuilder: (context, child, frame, wasSynchronouslyLoaded) { if (wasSynchronouslyLoaded || frame != null) { return child; } @@ -636,11 +506,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> return SizedBox( width: double.infinity, height: double.infinity, - child: Icon( - Icons.error_outline_rounded, - size: 24, - color: Colors.red[300], - ), + child: Icon(Icons.error_outline_rounded, size: 24, color: Colors.red[300]), ); }, ), @@ -657,8 +523,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> alignment: Alignment.topRight, image: getFullImageProvider(_nextAsset!), fit: BoxFit.cover, - frameBuilder: - (context, child, frame, wasSynchronouslyLoaded) { + frameBuilder: (context, child, frame, wasSynchronouslyLoaded) { if (wasSynchronouslyLoaded || frame != null) { return child; } @@ -668,11 +533,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> return SizedBox( width: double.infinity, height: double.infinity, - child: Icon( - Icons.error_outline_rounded, - size: 24, - color: Colors.red[300], - ), + child: Icon(Icons.error_outline_rounded, size: 24, color: Colors.red[300]), ); }, ), diff --git a/mobile/lib/widgets/common/scaffold_error_body.dart b/mobile/lib/widgets/common/scaffold_error_body.dart index 5011d229e7..2e2d8fb506 100644 --- a/mobile/lib/widgets/common/scaffold_error_body.dart +++ b/mobile/lib/widgets/common/scaffold_error_body.dart @@ -15,11 +15,7 @@ class ScaffoldErrorBody extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ - Text( - "scaffold_body_error_occurred", - style: context.textTheme.displayMedium, - textAlign: TextAlign.center, - ).tr(), + Text("scaffold_body_error_occurred", style: context.textTheme.displayMedium, textAlign: TextAlign.center).tr(), if (withIcon) Center( child: Padding( @@ -27,19 +23,14 @@ class ScaffoldErrorBody extends StatelessWidget { child: Icon( Icons.error_outline, size: 100, - color: - context.themeData.iconTheme.color?.withValues(alpha: 0.5), + color: context.themeData.iconTheme.color?.withValues(alpha: 0.5), ), ), ), if (withIcon && errorMsg != null) Padding( padding: const EdgeInsets.all(20), - child: Text( - errorMsg!, - style: context.textTheme.displaySmall, - textAlign: TextAlign.center, - ), + child: Text(errorMsg!, style: context.textTheme.displaySmall, textAlign: TextAlign.center), ), ], ); diff --git a/mobile/lib/widgets/common/search_field.dart b/mobile/lib/widgets/common/search_field.dart index 97ac75a63b..84af2d050c 100644 --- a/mobile/lib/widgets/common/search_field.dart +++ b/mobile/lib/widgets/common/search_field.dart @@ -43,40 +43,22 @@ class SearchField extends StatelessWidget { contentPadding: contentPadding, filled: filled, fillColor: context.primaryColor.withValues(alpha: 0.1), - hintStyle: context.textTheme.bodyLarge?.copyWith( - color: context.themeData.colorScheme.onSurfaceSecondary, - ), + hintStyle: context.textTheme.bodyLarge?.copyWith(color: context.themeData.colorScheme.onSurfaceSecondary), border: OutlineInputBorder( - borderRadius: const BorderRadius.all( - Radius.circular(25), - ), - borderSide: BorderSide( - color: context.colorScheme.surfaceDim, - ), + borderRadius: const BorderRadius.all(Radius.circular(25)), + borderSide: BorderSide(color: context.colorScheme.surfaceDim), ), enabledBorder: OutlineInputBorder( - borderRadius: const BorderRadius.all( - Radius.circular(25), - ), - borderSide: BorderSide( - color: context.colorScheme.surfaceContainer, - ), + borderRadius: const BorderRadius.all(Radius.circular(25)), + borderSide: BorderSide(color: context.colorScheme.surfaceContainer), ), disabledBorder: OutlineInputBorder( - borderRadius: const BorderRadius.all( - Radius.circular(25), - ), - borderSide: BorderSide( - color: context.colorScheme.surfaceDim, - ), + borderRadius: const BorderRadius.all(Radius.circular(25)), + borderSide: BorderSide(color: context.colorScheme.surfaceDim), ), focusedBorder: OutlineInputBorder( - borderRadius: const BorderRadius.all( - Radius.circular(25), - ), - borderSide: BorderSide( - color: context.colorScheme.primary.withAlpha(100), - ), + borderRadius: const BorderRadius.all(Radius.circular(25)), + borderSide: BorderSide(color: context.colorScheme.primary.withAlpha(100)), ), prefixIcon: prefixIcon, suffixIcon: suffixIcon, diff --git a/mobile/lib/widgets/common/selection_sliver_app_bar.dart b/mobile/lib/widgets/common/selection_sliver_app_bar.dart index 2f06934bc0..780062e50e 100644 --- a/mobile/lib/widgets/common/selection_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/selection_sliver_app_bar.dart @@ -7,25 +7,18 @@ import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; class SelectionSliverAppBar extends ConsumerStatefulWidget { - const SelectionSliverAppBar({ - super.key, - }); + const SelectionSliverAppBar({super.key}); @override - ConsumerState createState() => - _SelectionSliverAppBarState(); + ConsumerState createState() => _SelectionSliverAppBarState(); } class _SelectionSliverAppBarState extends ConsumerState { @override Widget build(BuildContext context) { - final selection = ref.watch( - multiSelectProvider.select((s) => s.selectedAssets), - ); + final selection = ref.watch(multiSelectProvider.select((s) => s.selectedAssets)); - final toExclude = ref.watch( - multiSelectProvider.select((s) => s.lockedSelectionAssets), - ); + final toExclude = ref.watch(multiSelectProvider.select((s) => s.lockedSelectionAssets)); final filteredAssets = selection.where((asset) { return !toExclude.contains(asset); @@ -41,9 +34,7 @@ class _SelectionSliverAppBarState extends ConsumerState { pinned: true, snap: false, backgroundColor: context.colorScheme.surfaceContainer, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(5)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))), automaticallyImplyLeading: false, leading: IconButton( icon: const Icon(Icons.close_rounded), @@ -53,22 +44,13 @@ class _SelectionSliverAppBarState extends ConsumerState { }, ), centerTitle: true, - title: Text( - "Select {count}".t( - context: context, - args: { - 'count': filteredAssets.length.toString(), - }, - ), - ), + title: Text("Select {count}".t(context: context, args: {'count': filteredAssets.length.toString()})), actions: [ TextButton( onPressed: () => onDone(filteredAssets), child: Text( 'done'.t(context: context), - style: context.textTheme.titleSmall?.copyWith( - color: context.colorScheme.primary, - ), + style: context.textTheme.titleSmall?.copyWith(color: context.colorScheme.primary), ), ), ], diff --git a/mobile/lib/widgets/common/share_dialog.dart b/mobile/lib/widgets/common/share_dialog.dart index 1c7eb3580a..625390c4b7 100644 --- a/mobile/lib/widgets/common/share_dialog.dart +++ b/mobile/lib/widgets/common/share_dialog.dart @@ -11,10 +11,7 @@ class ShareDialog extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ const CircularProgressIndicator(), - Container( - margin: const EdgeInsets.only(top: 12), - child: const Text('share_dialog_preparing').tr(), - ), + Container(margin: const EdgeInsets.only(top: 12), child: const Text('share_dialog_preparing').tr()), ], ), ); diff --git a/mobile/lib/widgets/common/thumbhash_placeholder.dart b/mobile/lib/widgets/common/thumbhash_placeholder.dart index aa320f4230..0cb1222989 100644 --- a/mobile/lib/widgets/common/thumbhash_placeholder.dart +++ b/mobile/lib/widgets/common/thumbhash_placeholder.dart @@ -6,22 +6,14 @@ import 'package:octo_image/octo_image.dart'; /// Simple set to show [OctoPlaceholder.circularProgressIndicator] as /// placeholder and [OctoError.icon] as error. -OctoSet blurHashOrPlaceholder( - Uint8List? blurhash, { - BoxFit? fit, - Text? errorMessage, -}) { +OctoSet blurHashOrPlaceholder(Uint8List? blurhash, {BoxFit? fit, Text? errorMessage}) { return OctoSet( placeholderBuilder: blurHashPlaceholderBuilder(blurhash, fit: fit), - errorBuilder: - blurHashErrorBuilder(blurhash, fit: fit, message: errorMessage), + errorBuilder: blurHashErrorBuilder(blurhash, fit: fit, message: errorMessage), ); } -OctoPlaceholderBuilder blurHashPlaceholderBuilder( - Uint8List? blurhash, { - BoxFit? fit, -}) { +OctoPlaceholderBuilder blurHashPlaceholderBuilder(Uint8List? blurhash, {BoxFit? fit}) { return (context) => blurhash == null ? const ThumbnailPlaceholder() : FadeInPlaceholderImage( diff --git a/mobile/lib/widgets/common/user_avatar.dart b/mobile/lib/widgets/common/user_avatar.dart index a5a6fa2bdd..ff0e39f371 100644 --- a/mobile/lib/widgets/common/user_avatar.dart +++ b/mobile/lib/widgets/common/user_avatar.dart @@ -7,8 +7,7 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/services/api.service.dart'; Widget userAvatar(BuildContext context, UserDto u, {double? radius}) { - final url = - "${Store.get(StoreKey.serverEndpoint)}/users/${u.id}/profile-image"; + final url = "${Store.get(StoreKey.serverEndpoint)}/users/${u.id}/profile-image"; final nameFirstLetter = u.name.isNotEmpty ? u.name[0] : ""; return CircleAvatar( radius: radius, diff --git a/mobile/lib/widgets/common/user_circle_avatar.dart b/mobile/lib/widgets/common/user_circle_avatar.dart index 479c30d6da..1fbfce6c53 100644 --- a/mobile/lib/widgets/common/user_circle_avatar.dart +++ b/mobile/lib/widgets/common/user_circle_avatar.dart @@ -16,13 +16,7 @@ class UserCircleAvatar extends ConsumerWidget { double size; bool hasBorder; - UserCircleAvatar({ - super.key, - this.radius = 22, - this.size = 44, - this.hasBorder = false, - required this.user, - }); + UserCircleAvatar({super.key, this.radius = 22, this.size = 44, this.hasBorder = false, required this.user}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -34,34 +28,27 @@ class UserCircleAvatar extends ConsumerWidget { style: TextStyle( fontWeight: FontWeight.bold, fontSize: 12, - color: userAvatarColor.computeLuminance() > 0.5 - ? Colors.black - : Colors.white, + color: userAvatarColor.computeLuminance() > 0.5 ? Colors.black : Colors.white, ), child: Text(user.name[0].toUpperCase()), ); + return Tooltip( message: user.name, child: Container( decoration: BoxDecoration( shape: BoxShape.circle, - border: hasBorder - ? Border.all( - color: Colors.grey[500]!, - width: 1, - ) - : null, + border: hasBorder ? Border.all(color: Colors.grey[500]!, width: 1) : null, ), child: CircleAvatar( backgroundColor: userAvatarColor, radius: radius, - child: user.profileImagePath == null - ? textIcon - : ClipRRect( + child: user.hasProfileImage + ? ClipRRect( borderRadius: const BorderRadius.all(Radius.circular(50)), child: CachedNetworkImage( fit: BoxFit.cover, - cacheKey: user.profileImagePath, + cacheKey: user.profileChangedAt.toIso8601String(), width: size, height: size, placeholder: (_, __) => Image.memory(kTransparentImage), @@ -70,7 +57,8 @@ class UserCircleAvatar extends ConsumerWidget { fadeInDuration: const Duration(milliseconds: 300), errorWidget: (context, error, stackTrace) => textIcon, ), - ), + ) + : textIcon, ), ), ); diff --git a/mobile/lib/widgets/forms/change_password_form.dart b/mobile/lib/widgets/forms/change_password_form.dart index 1c7eed7e5b..179b05a712 100644 --- a/mobile/lib/widgets/forms/change_password_form.dart +++ b/mobile/lib/widgets/forms/change_password_form.dart @@ -17,10 +17,8 @@ class ChangePasswordForm extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final passwordController = - useTextEditingController.fromValue(TextEditingValue.empty); - final confirmPasswordController = - useTextEditingController.fromValue(TextEditingValue.empty); + final passwordController = useTextEditingController.fromValue(TextEditingValue.empty); + final confirmPasswordController = useTextEditingController.fromValue(TextEditingValue.empty); final authState = ref.watch(authProvider); final formKey = GlobalKey(); @@ -35,25 +33,13 @@ class ChangePasswordForm extends HookConsumerWidget { children: [ Text( 'change_password'.tr(), - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - color: context.primaryColor, - ), + style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold, color: context.primaryColor), ), Padding( padding: const EdgeInsets.symmetric(vertical: 24.0), child: Text( - 'change_password_form_description'.tr( - namedArgs: { - 'name': authState.name, - }, - ), - style: TextStyle( - fontSize: 14, - color: context.colorScheme.onSurface, - fontWeight: FontWeight.w600, - ), + 'change_password_form_description'.tr(namedArgs: {'name': authState.name}), + style: TextStyle(fontSize: 14, color: context.colorScheme.onSurface, fontWeight: FontWeight.w600), ), ), Form( @@ -79,13 +65,9 @@ class ChangePasswordForm extends HookConsumerWidget { if (isSuccess) { await ref.read(authProvider.notifier).logout(); - ref - .read(manualUploadProvider.notifier) - .cancelBackup(); + ref.read(manualUploadProvider.notifier).cancelBackup(); ref.read(backupProvider.notifier).cancelBackup(); - await ref - .read(assetProvider.notifier) - .clearAllAssets(); + await ref.read(assetProvider.notifier).clearAllAssets(); ref.read(websocketProvider.notifier).disconnect(); AutoRouter.of(context).back(); @@ -146,11 +128,7 @@ class ConfirmPasswordInput extends StatelessWidget { final TextEditingController originalController; final TextEditingController confirmController; - const ConfirmPasswordInput({ - super.key, - required this.originalController, - required this.confirmController, - }); + const ConfirmPasswordInput({super.key, required this.originalController, required this.confirmController}); String? _validateInput(String? email) { if (confirmController.value != originalController.value) { @@ -178,11 +156,7 @@ class ConfirmPasswordInput extends StatelessWidget { class ChangePasswordButton extends ConsumerWidget { final TextEditingController passwordController; final VoidCallback onPressed; - const ChangePasswordButton({ - super.key, - required this.passwordController, - required this.onPressed, - }); + const ChangePasswordButton({super.key, required this.passwordController, required this.onPressed}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -192,10 +166,7 @@ class ChangePasswordButton extends ConsumerWidget { padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 25), ), onPressed: onPressed, - child: Text( - 'change_password'.tr(), - style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold), - ), + child: Text('change_password'.tr(), style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), ); } } diff --git a/mobile/lib/widgets/forms/login/email_input.dart b/mobile/lib/widgets/forms/login/email_input.dart index 52f2a598f9..4d90d918ac 100644 --- a/mobile/lib/widgets/forms/login/email_input.dart +++ b/mobile/lib/widgets/forms/login/email_input.dart @@ -6,12 +6,7 @@ class EmailInput extends StatelessWidget { final FocusNode? focusNode; final Function()? onSubmit; - const EmailInput({ - super.key, - required this.controller, - this.focusNode, - this.onSubmit, - }); + const EmailInput({super.key, required this.controller, this.focusNode, this.onSubmit}); String? _validateInput(String? email) { if (email == null || email == '') return null; @@ -32,10 +27,7 @@ class EmailInput extends StatelessWidget { labelText: 'email'.tr(), border: const OutlineInputBorder(), hintText: 'login_form_email_hint'.tr(), - hintStyle: const TextStyle( - fontWeight: FontWeight.normal, - fontSize: 14, - ), + hintStyle: const TextStyle(fontWeight: FontWeight.normal, fontSize: 14), ), validator: _validateInput, autovalidateMode: AutovalidateMode.always, diff --git a/mobile/lib/widgets/forms/login/loading_icon.dart b/mobile/lib/widgets/forms/login/loading_icon.dart index 9d3f5eab64..052ce43ac7 100644 --- a/mobile/lib/widgets/forms/login/loading_icon.dart +++ b/mobile/lib/widgets/forms/login/loading_icon.dart @@ -7,15 +7,7 @@ class LoadingIcon extends StatelessWidget { Widget build(BuildContext context) { return const Padding( padding: EdgeInsets.only(top: 18.0), - child: SizedBox( - width: 24, - height: 24, - child: FittedBox( - child: CircularProgressIndicator( - strokeWidth: 2, - ), - ), - ), + child: SizedBox(width: 24, height: 24, child: FittedBox(child: CircularProgressIndicator(strokeWidth: 2))), ); } } diff --git a/mobile/lib/widgets/forms/login/login_button.dart b/mobile/lib/widgets/forms/login/login_button.dart index 479c53a9b7..0f9fb21d8f 100644 --- a/mobile/lib/widgets/forms/login/login_button.dart +++ b/mobile/lib/widgets/forms/login/login_button.dart @@ -5,23 +5,15 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; class LoginButton extends ConsumerWidget { final Function() onPressed; - const LoginButton({ - super.key, - required this.onPressed, - }); + const LoginButton({super.key, required this.onPressed}); @override Widget build(BuildContext context, WidgetRef ref) { return ElevatedButton.icon( - style: ElevatedButton.styleFrom( - padding: const EdgeInsets.symmetric(vertical: 12), - ), + style: ElevatedButton.styleFrom(padding: const EdgeInsets.symmetric(vertical: 12)), onPressed: onPressed, icon: const Icon(Icons.login_rounded), - label: const Text( - "login", - style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold), - ).tr(), + label: const Text("login", style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)).tr(), ); } } diff --git a/mobile/lib/widgets/forms/login/login_form.dart b/mobile/lib/widgets/forms/login/login_form.dart index 24a73b2cbc..b4944ee1e9 100644 --- a/mobile/lib/widgets/forms/login/login_form.dart +++ b/mobile/lib/widgets/forms/login/login_form.dart @@ -43,12 +43,9 @@ class LoginForm extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final emailController = - useTextEditingController.fromValue(TextEditingValue.empty); - final passwordController = - useTextEditingController.fromValue(TextEditingValue.empty); - final serverEndpointController = - useTextEditingController.fromValue(TextEditingValue.empty); + final emailController = useTextEditingController.fromValue(TextEditingValue.empty); + final passwordController = useTextEditingController.fromValue(TextEditingValue.empty); + final serverEndpointController = useTextEditingController.fromValue(TextEditingValue.empty); final emailFocusNode = useFocusNode(); final passwordFocusNode = useFocusNode(); final serverEndpointFocusNode = useFocusNode(); @@ -57,9 +54,7 @@ class LoginForm extends HookConsumerWidget { final isOauthEnable = useState(false); final isPasswordLoginEnable = useState(false); final oAuthButtonLabel = useState('OAuth'); - final logoAnimationController = useAnimationController( - duration: const Duration(seconds: 60), - )..repeat(); + final logoAnimationController = useAnimationController(duration: const Duration(seconds: 60))..repeat(); final serverInfo = ref.watch(serverInfoProvider); final warningMessage = useState(null); final loginFormKey = GlobalKey(); @@ -93,17 +88,12 @@ class LoginForm extends HookConsumerWidget { // Guard empty URL if (serverUrl.isEmpty) { - ImmichToast.show( - context: context, - msg: "login_form_server_empty".tr(), - toastType: ToastType.error, - ); + ImmichToast.show(context: context, msg: "login_form_server_empty".tr(), toastType: ToastType.error); } try { isLoadingServer.value = true; - final endpoint = - await ref.read(authProvider.notifier).validateServerUrl(serverUrl); + final endpoint = await ref.read(authProvider.notifier).validateServerUrl(serverUrl); // Fetch and load server config and features await ref.read(serverInfoProvider.notifier).getServerInfo(); @@ -114,9 +104,7 @@ class LoginForm extends HookConsumerWidget { isOauthEnable.value = features.oauthEnabled; isPasswordLoginEnable.value = features.passwordLogin; - oAuthButtonLabel.value = config.oauthButtonText.isNotEmpty - ? config.oauthButtonText - : 'OAuth'; + oAuthButtonLabel.value = config.oauthButtonText.isNotEmpty ? config.oauthButtonText : 'OAuth'; serverEndpoint.value = endpoint; } on ApiException catch (e) { @@ -154,16 +142,13 @@ class LoginForm extends HookConsumerWidget { isLoadingServer.value = false; } - useEffect( - () { - final serverUrl = getServerUrl(); - if (serverUrl != null) { - serverEndpointController.text = serverUrl; - } - return null; - }, - [], - ); + useEffect(() { + final serverUrl = getServerUrl(); + if (serverUrl != null) { + serverEndpointController.text = serverUrl; + } + return null; + }, []); populateTestLoginInfo() { emailController.text = 'demo@immich.app'; @@ -186,19 +171,14 @@ class LoginForm extends HookConsumerWidget { invalidateAllApiRepositoryProviders(ref); try { - final result = await ref.read(authProvider.notifier).login( - emailController.text, - passwordController.text, - ); + final result = await ref.read(authProvider.notifier).login(emailController.text, passwordController.text); if (result.shouldChangePassword && !result.isAdmin) { context.pushRoute(const ChangePasswordRoute()); } else { final isBeta = Store.isBetaTimelineEnabled; if (isBeta) { - await ref - .read(galleryPermissionNotifier.notifier) - .requestGalleryPermission(); + await ref.read(galleryPermissionNotifier.notifier).requestGalleryPermission(); await runNewSync(ref); context.replaceRoute(const TabShellRoute()); return; @@ -218,15 +198,9 @@ class LoginForm extends HookConsumerWidget { } String generateRandomString(int length) { - const chars = - 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890'; + const chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890'; final random = Random.secure(); - return String.fromCharCodes( - Iterable.generate( - length, - (_) => chars.codeUnitAt(random.nextInt(chars.length)), - ), - ); + return String.fromCharCodes(Iterable.generate(length, (_) => chars.codeUnitAt(random.nextInt(chars.length)))); } List randomBytes(int length) { @@ -282,23 +256,17 @@ class LoginForm extends HookConsumerWidget { if (oAuthServerUrl != null) { try { - final loginResponseDto = await oAuthService.oAuthLogin( - oAuthServerUrl, - state, - codeVerifier, - ); + final loginResponseDto = await oAuthService.oAuthLogin(oAuthServerUrl, state, codeVerifier); if (loginResponseDto == null) { return; } - log.info( - "Finished OAuth login with response: ${loginResponseDto.userEmail}", - ); + log.info("Finished OAuth login with response: ${loginResponseDto.userEmail}"); - final isSuccess = await ref.watch(authProvider.notifier).saveAuthInfo( - accessToken: loginResponseDto.accessToken, - ); + final isSuccess = await ref + .watch(authProvider.notifier) + .saveAuthInfo(accessToken: loginResponseDto.accessToken); if (isSuccess) { isLoading.value = false; @@ -308,9 +276,7 @@ class LoginForm extends HookConsumerWidget { ref.watch(backupProvider.notifier).resumeBackup(); } if (isBeta) { - await ref - .read(galleryPermissionNotifier.notifier) - .requestGalleryPermission(); + await ref.read(galleryPermissionNotifier.notifier).requestGalleryPermission(); await runNewSync(ref); context.replaceRoute(const TabShellRoute()); return; @@ -383,13 +349,9 @@ class LoginForm extends HookConsumerWidget { ), ), ), - onPressed: - isLoadingServer.value ? null : getServerAuthSettings, + onPressed: isLoadingServer.value ? null : getServerAuthSettings, icon: const Icon(Icons.arrow_forward_rounded), - label: const Text( - 'next', - style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold), - ).tr(), + label: const Text('next', style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)).tr(), ), ), ], @@ -412,20 +374,11 @@ class LoginForm extends HookConsumerWidget { child: Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: - context.isDarkTheme ? Colors.red.shade700 : Colors.red.shade100, - borderRadius: const BorderRadius.all( - Radius.circular(8), - ), - border: Border.all( - color: - context.isDarkTheme ? Colors.red.shade900 : Colors.red[200]!, - ), - ), - child: Text( - warningMessage.value!, - textAlign: TextAlign.center, + color: context.isDarkTheme ? Colors.red.shade700 : Colors.red.shade100, + borderRadius: const BorderRadius.all(Radius.circular(8)), + border: Border.all(color: context.isDarkTheme ? Colors.red.shade900 : Colors.red[200]!), ), + child: Text(warningMessage.value!, textAlign: TextAlign.center), ), ); } @@ -449,11 +402,7 @@ class LoginForm extends HookConsumerWidget { onSubmit: passwordFocusNode.requestFocus, ), const SizedBox(height: 8), - PasswordInput( - controller: passwordController, - focusNode: passwordFocusNode, - onSubmit: login, - ), + PasswordInput(controller: passwordController, focusNode: passwordFocusNode, onSubmit: login), ], // Note: This used to have an AnimatedSwitcher, but was removed @@ -465,19 +414,12 @@ class LoginForm extends HookConsumerWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ const SizedBox(height: 18), - if (isPasswordLoginEnable.value) - LoginButton(onPressed: login), + if (isPasswordLoginEnable.value) LoginButton(onPressed: login), if (isOauthEnable.value) ...[ if (isPasswordLoginEnable.value) Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16.0, - ), - child: Divider( - color: context.isDarkTheme - ? Colors.white - : Colors.black, - ), + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Divider(color: context.isDarkTheme ? Colors.white : Colors.black), ), OAuthLoginButton( serverEndpointController: serverEndpointController, @@ -488,10 +430,7 @@ class LoginForm extends HookConsumerWidget { ], ], ), - if (!isOauthEnable.value && !isPasswordLoginEnable.value) - Center( - child: const Text('login_disabled').tr(), - ), + if (!isOauthEnable.value && !isPasswordLoginEnable.value) Center(child: const Text('login_disabled').tr()), const SizedBox(height: 12), TextButton.icon( icon: const Icon(Icons.arrow_back), @@ -503,8 +442,7 @@ class LoginForm extends HookConsumerWidget { ); } - final serverSelectionOrLogin = - serverEndpoint.value == null ? buildSelectServer() : buildLogin(); + final serverSelectionOrLogin = serverEndpoint.value == null ? buildSelectServer() : buildLogin(); return LayoutBuilder( builder: (context, constraints) { @@ -516,9 +454,7 @@ class LoginForm extends HookConsumerWidget { crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisAlignment: MainAxisAlignment.center, children: [ - SizedBox( - height: constraints.maxHeight / 5, - ), + SizedBox(height: constraints.maxHeight / 5), Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.end, @@ -528,24 +464,16 @@ class LoginForm extends HookConsumerWidget { onLongPress: () => populateTestLoginInfo1(), child: RotationTransition( turns: logoAnimationController, - child: const ImmichLogo( - heroTag: 'logo', - ), + child: const ImmichLogo(heroTag: 'logo'), ), ), - const Padding( - padding: EdgeInsets.only(top: 8.0, bottom: 16), - child: ImmichTitleText(), - ), + const Padding(padding: EdgeInsets.only(top: 8.0, bottom: 16), child: ImmichTitleText()), ], ), // Note: This used to have an AnimatedSwitcher, but was removed // because of https://github.com/flutter/flutter/issues/120874 - Form( - key: loginFormKey, - child: serverSelectionOrLogin, - ), + Form(key: loginFormKey, child: serverSelectionOrLogin), ], ), ), diff --git a/mobile/lib/widgets/forms/login/o_auth_login_button.dart b/mobile/lib/widgets/forms/login/o_auth_login_button.dart index 465d88a4d2..2d9b603b3c 100644 --- a/mobile/lib/widgets/forms/login/o_auth_login_button.dart +++ b/mobile/lib/widgets/forms/login/o_auth_login_button.dart @@ -25,10 +25,7 @@ class OAuthLoginButton extends ConsumerWidget { ), onPressed: onPressed, icon: const Icon(Icons.pin_rounded), - label: Text( - buttonLabel, - style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold), - ), + label: Text(buttonLabel, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold)), ); } } diff --git a/mobile/lib/widgets/forms/login/password_input.dart b/mobile/lib/widgets/forms/login/password_input.dart index 2d2c1923f7..5cdfcc9567 100644 --- a/mobile/lib/widgets/forms/login/password_input.dart +++ b/mobile/lib/widgets/forms/login/password_input.dart @@ -8,12 +8,7 @@ class PasswordInput extends HookConsumerWidget { final FocusNode? focusNode; final Function()? onSubmit; - const PasswordInput({ - super.key, - required this.controller, - this.focusNode, - this.onSubmit, - }); + const PasswordInput({super.key, required this.controller, this.focusNode, this.onSubmit}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -26,17 +21,10 @@ class PasswordInput extends HookConsumerWidget { labelText: 'password'.tr(), border: const OutlineInputBorder(), hintText: 'login_form_password_hint'.tr(), - hintStyle: const TextStyle( - fontWeight: FontWeight.normal, - fontSize: 14, - ), + hintStyle: const TextStyle(fontWeight: FontWeight.normal, fontSize: 14), suffixIcon: IconButton( onPressed: () => isPasswordVisible.value = !isPasswordVisible.value, - icon: Icon( - isPasswordVisible.value - ? Icons.visibility_off_sharp - : Icons.visibility_sharp, - ), + icon: Icon(isPasswordVisible.value ? Icons.visibility_off_sharp : Icons.visibility_sharp), ), ), autofillHints: const [AutofillHints.password], diff --git a/mobile/lib/widgets/forms/login/server_endpoint_input.dart b/mobile/lib/widgets/forms/login/server_endpoint_input.dart index 37bcad9d82..f9bc1690af 100644 --- a/mobile/lib/widgets/forms/login/server_endpoint_input.dart +++ b/mobile/lib/widgets/forms/login/server_endpoint_input.dart @@ -7,21 +7,13 @@ class ServerEndpointInput extends StatelessWidget { final FocusNode focusNode; final Function()? onSubmit; - const ServerEndpointInput({ - super.key, - required this.controller, - required this.focusNode, - this.onSubmit, - }); + const ServerEndpointInput({super.key, required this.controller, required this.focusNode, this.onSubmit}); String? _validateInput(String? url) { if (url == null || url.isEmpty) return null; final parsedUrl = Uri.tryParse(sanitizeUrl(url)); - if (parsedUrl == null || - !parsedUrl.isAbsolute || - !parsedUrl.scheme.startsWith("http") || - parsedUrl.host.isEmpty) { + if (parsedUrl == null || !parsedUrl.isAbsolute || !parsedUrl.scheme.startsWith("http") || parsedUrl.host.isEmpty) { return 'login_form_err_invalid_url'.tr(); } diff --git a/mobile/lib/widgets/forms/pin_input.dart b/mobile/lib/widgets/forms/pin_input.dart index 1588a65c60..88e27f005e 100644 --- a/mobile/lib/widgets/forms/pin_input.dart +++ b/mobile/lib/widgets/forms/pin_input.dart @@ -30,8 +30,7 @@ class PinInput extends StatelessWidget { final minimumPadding = 18.0; final gapWidth = 3.0; final screenWidth = context.width; - final pinWidth = - (screenWidth - (minimumPadding * 2) - (gapWidth * 5)) / (length ?? 6); + final pinWidth = (screenWidth - (minimumPadding * 2) - (gapWidth * 5)) / (length ?? 6); if (pinWidth > 60) { return const Size(60, 64); @@ -44,11 +43,7 @@ class PinInput extends StatelessWidget { final defaultPinTheme = PinTheme( width: getPinSize().width, height: getPinSize().height, - textStyle: TextStyle( - fontSize: 24, - color: context.colorScheme.onSurface, - fontFamily: 'Overpass Mono', - ), + textStyle: TextStyle(fontSize: 24, color: context.colorScheme.onSurface, fontFamily: 'Overpass Mono'), decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(19)), border: Border.all(color: context.colorScheme.surfaceBright), @@ -62,8 +57,7 @@ class PinInput extends StatelessWidget { if (label != null) ...[ Text( label!, - style: context.textTheme.displayLarge - ?.copyWith(color: context.colorScheme.onSurface.withAlpha(200)), + style: context.textTheme.displayLarge?.copyWith(color: context.colorScheme.onSurface.withAlpha(200)), ), const SizedBox(height: 4), ], @@ -72,34 +66,19 @@ class PinInput extends StatelessWidget { forceErrorState: hasError ?? false, autofocus: autoFocus ?? false, obscureText: obscureText ?? false, - obscuringWidget: Icon( - Icons.vpn_key_rounded, - color: context.primaryColor, - size: 20, - ), - separatorBuilder: (index) => const SizedBox( - height: 64, - width: 3, - ), + obscuringWidget: Icon(Icons.vpn_key_rounded, color: context.primaryColor, size: 20), + separatorBuilder: (index) => const SizedBox(height: 64, width: 3), cursor: Column( mainAxisAlignment: MainAxisAlignment.end, children: [ - Container( - margin: const EdgeInsets.only(bottom: 9), - width: 18, - height: 2, - color: context.primaryColor, - ), + Container(margin: const EdgeInsets.only(bottom: 9), width: 18, height: 2, color: context.primaryColor), ], ), defaultPinTheme: defaultPinTheme, focusedPinTheme: defaultPinTheme.copyWith( decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(19)), - border: Border.all( - color: context.primaryColor.withValues(alpha: 0.5), - width: 2, - ), + border: Border.all(color: context.primaryColor.withValues(alpha: 0.5), width: 2), color: context.colorScheme.surfaceContainerHigh, ), ), @@ -107,10 +86,7 @@ class PinInput extends StatelessWidget { decoration: BoxDecoration( color: context.colorScheme.error.withAlpha(15), borderRadius: const BorderRadius.all(Radius.circular(19)), - border: Border.all( - color: context.colorScheme.error.withAlpha(100), - width: 2, - ), + border: Border.all(color: context.colorScheme.error.withAlpha(100), width: 2), ), ), pinputAutovalidateMode: PinputAutovalidateMode.onSubmit, diff --git a/mobile/lib/widgets/forms/pin_registration_form.dart b/mobile/lib/widgets/forms/pin_registration_form.dart index c3cfd3a864..d126169aad 100644 --- a/mobile/lib/widgets/forms/pin_registration_form.dart +++ b/mobile/lib/widgets/forms/pin_registration_form.dart @@ -9,10 +9,7 @@ import 'package:immich_mobile/widgets/forms/pin_input.dart'; class PinRegistrationForm extends HookConsumerWidget { final Function() onDone; - const PinRegistrationForm({ - super.key, - required this.onDone, - }); + const PinRegistrationForm({super.key, required this.onDone}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -40,35 +37,25 @@ class PinRegistrationForm extends HookConsumerWidget { } try { - await ref.read(authProvider.notifier).setupPinCode( - newPinCodeController.text, - ); + await ref.read(authProvider.notifier).setupPinCode(newPinCodeController.text); onDone(); } catch (error) { hasError.value = true; - context.showSnackBar( - SnackBar(content: Text(error.toString())), - ); + context.showSnackBar(SnackBar(content: Text(error.toString()))); } } return Form( child: Column( children: [ - Icon( - Icons.pin_outlined, - size: 64, - color: context.primaryColor, - ), + Icon(Icons.pin_outlined, size: 64, color: context.primaryColor), const SizedBox(height: 32), SizedBox( width: context.width * 0.7, child: Text( 'setup_pin_code'.tr(), - style: context.textTheme.labelLarge!.copyWith( - fontSize: 24, - ), + style: context.textTheme.labelLarge!.copyWith(fontSize: 24), textAlign: TextAlign.center, ), ), @@ -76,9 +63,7 @@ class PinRegistrationForm extends HookConsumerWidget { width: context.width * 0.8, child: Text( 'new_pin_code_subtitle'.tr(), - style: context.textTheme.bodyLarge!.copyWith( - fontSize: 16, - ), + style: context.textTheme.bodyLarge!.copyWith(fontSize: 16), textAlign: TextAlign.center, ), ), @@ -113,10 +98,7 @@ class PinRegistrationForm extends HookConsumerWidget { child: Row( children: [ Expanded( - child: ElevatedButton( - onPressed: createNewPinCode, - child: Text('create'.tr()), - ), + child: ElevatedButton(onPressed: createNewPinCode, child: Text('create'.tr())), ), ], ), diff --git a/mobile/lib/widgets/forms/pin_verification_form.dart b/mobile/lib/widgets/forms/pin_verification_form.dart index f4ebf4272f..2b7e3e8251 100644 --- a/mobile/lib/widgets/forms/pin_verification_form.dart +++ b/mobile/lib/widgets/forms/pin_verification_form.dart @@ -30,8 +30,7 @@ class PinVerificationForm extends HookConsumerWidget { final isVerified = useState(false); verifyPin(String pinCode) async { - final isUnlocked = - await ref.read(authProvider.notifier).unlockPinCode(pinCode); + final isUnlocked = await ref.read(authProvider.notifier).unlockPinCode(pinCode); if (isUnlocked) { isVerified.value = true; @@ -50,17 +49,11 @@ class PinVerificationForm extends HookConsumerWidget { AnimatedSwitcher( duration: const Duration(milliseconds: 200), child: isVerified.value - ? Icon( - successIcon ?? Icons.lock_open_rounded, - size: 64, - color: Colors.green[300], - ) + ? Icon(successIcon ?? Icons.lock_open_rounded, size: 64, color: Colors.green[300]) : Icon( icon ?? Icons.lock_outline_rounded, size: 64, - color: hasError.value - ? context.colorScheme.error - : context.primaryColor, + color: hasError.value ? context.colorScheme.error : context.primaryColor, ), ), const SizedBox(height: 36), @@ -68,9 +61,7 @@ class PinVerificationForm extends HookConsumerWidget { width: context.width * 0.7, child: Text( description ?? 'enter_your_pin_code_subtitle'.tr(), - style: context.textTheme.labelLarge!.copyWith( - fontSize: 18, - ), + style: context.textTheme.labelLarge!.copyWith(fontSize: 18), textAlign: TextAlign.center, ), ), diff --git a/mobile/lib/widgets/map/map_app_bar.dart b/mobile/lib/widgets/map/map_app_bar.dart index ccadd2ad15..73706c7661 100644 --- a/mobile/lib/widgets/map/map_app_bar.dart +++ b/mobile/lib/widgets/map/map_app_bar.dart @@ -22,9 +22,8 @@ class MapAppBar extends HookWidget implements PreferredSizeWidget { padding: EdgeInsets.only(top: context.padding.top + 25), child: ValueListenableBuilder( valueListenable: selectedAssets, - builder: (ctx, value, child) => value.isNotEmpty - ? _SelectionRow(selectedAssets: selectedAssets) - : const _NonSelectionRow(), + builder: (ctx, value, child) => + value.isNotEmpty ? _SelectionRow(selectedAssets: selectedAssets) : const _NonSelectionRow(), ), ); } @@ -53,16 +52,12 @@ class _NonSelectionRow extends StatelessWidget { children: [ ElevatedButton( onPressed: () => context.maybePop(), - style: ElevatedButton.styleFrom( - shape: const CircleBorder(), - ), + style: ElevatedButton.styleFrom(shape: const CircleBorder()), child: const Icon(Icons.arrow_back_ios_new_rounded), ), ElevatedButton( onPressed: onSettingsPressed, - style: ElevatedButton.styleFrom( - shape: const CircleBorder(), - ), + style: ElevatedButton.styleFrom(shape: const CircleBorder()), child: const Icon(Icons.more_vert_rounded), ), ], @@ -79,10 +74,7 @@ class _SelectionRow extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final isProcessing = useProcessingOverlay(); - Future handleProcessing( - FutureOr Function() action, [ - bool reloadMarkers = false, - ]) async { + Future handleProcessing(FutureOr Function() action, [bool reloadMarkers = false]) async { isProcessing.value = true; await action(); // Reset state @@ -102,9 +94,7 @@ class _SelectionRow extends HookConsumerWidget { icon: const Icon(Icons.close_rounded), label: Text( '${selectedAssets.value.length}', - style: context.textTheme.titleMedium?.copyWith( - color: context.colorScheme.onPrimary, - ), + style: context.textTheme.titleMedium?.copyWith(color: context.colorScheme.onPrimary), ), ), ), @@ -113,43 +103,20 @@ class _SelectionRow extends HookConsumerWidget { mainAxisAlignment: MainAxisAlignment.end, children: [ ElevatedButton( - onPressed: () => handleProcessing( - () => handleShareAssets( - ref, - context, - selectedAssets.value.toList(), - ), - ), - style: ElevatedButton.styleFrom( - shape: const CircleBorder(), - ), + onPressed: () => handleProcessing(() => handleShareAssets(ref, context, selectedAssets.value.toList())), + style: ElevatedButton.styleFrom(shape: const CircleBorder()), child: const Icon(Icons.ios_share_rounded), ), ElevatedButton( - onPressed: () => handleProcessing( - () => handleFavoriteAssets( - ref, - context, - selectedAssets.value.toList(), - ), - ), - style: ElevatedButton.styleFrom( - shape: const CircleBorder(), - ), + onPressed: () => + handleProcessing(() => handleFavoriteAssets(ref, context, selectedAssets.value.toList())), + style: ElevatedButton.styleFrom(shape: const CircleBorder()), child: const Icon(Icons.favorite), ), ElevatedButton( - onPressed: () => handleProcessing( - () => handleArchiveAssets( - ref, - context, - selectedAssets.value.toList(), - ), - true, - ), - style: ElevatedButton.styleFrom( - shape: const CircleBorder(), - ), + onPressed: () => + handleProcessing(() => handleArchiveAssets(ref, context, selectedAssets.value.toList()), true), + style: ElevatedButton.styleFrom(shape: const CircleBorder()), child: const Icon(Icons.archive), ), ], diff --git a/mobile/lib/widgets/map/map_asset_grid.dart b/mobile/lib/widgets/map/map_asset_grid.dart index c8c0816551..893c36d43f 100644 --- a/mobile/lib/widgets/map/map_asset_grid.dart +++ b/mobile/lib/widgets/map/map_asset_grid.dart @@ -44,8 +44,7 @@ class MapAssetGrid extends HookConsumerWidget { final cachedRenderList = useRef(null); final lastRenderElementIndex = useRef(null); final assetInSheet = useValueNotifier(null); - final gridScrollThrottler = - useThrottler(interval: const Duration(milliseconds: 300)); + final gridScrollThrottler = useThrottler(interval: const Duration(milliseconds: 300)); // Add a cache for assets we've already loaded final assetCache = useRef>({}); @@ -67,8 +66,7 @@ class MapAssetGrid extends HookConsumerWidget { // Only fetch missing assets if (missingIds.isNotEmpty) { - final newAssets = - await ref.read(dbProvider).assets.getAllByRemoteId(missingIds); + final newAssets = await ref.read(dbProvider).assets.getAllByRemoteId(missingIds); // Add new assets to cache and current list for (final asset in newAssets) { @@ -93,8 +91,7 @@ class MapAssetGrid extends HookConsumerWidget { final orderedPos = positions.sortedByField((p) => p.index); // Index of row where the items are mostly visible const partialOffset = 0.20; - final item = orderedPos - .firstWhereOrNull((p) => p.itemTrailingEdge > partialOffset); + final item = orderedPos.firstWhereOrNull((p) => p.itemTrailingEdge > partialOffset); // Guard no elements, reset state // Also fail fast when the sheet is just opened and the user is yet to scroll (i.e leading = 0) @@ -103,8 +100,7 @@ class MapAssetGrid extends HookConsumerWidget { return; } - final renderElement = - cachedRenderList.value?.elements.elementAtOrNull(item.index); + final renderElement = cachedRenderList.value?.elements.elementAtOrNull(item.index); // Guard no render list or render element if (renderElement == null) { return; @@ -123,18 +119,15 @@ class MapAssetGrid extends HookConsumerWidget { final rowOffset = renderElement.offset; // Column offset = (total trailingEdge - trailingEdge crossed) / offset for each asset final totalOffset = item.itemTrailingEdge - item.itemLeadingEdge; - final edgeOffset = (totalOffset - partialOffset) / + final edgeOffset = + (totalOffset - partialOffset) / // Round the total count to the next multiple of [assetsPerRow] ((renderElement.totalCount / assetsPerRow) * assetsPerRow).floor(); // trailing should never be above the totalOffset - final columnOffset = - (totalOffset - math.min(item.itemTrailingEdge, totalOffset)) ~/ - edgeOffset; + final columnOffset = (totalOffset - math.min(item.itemTrailingEdge, totalOffset)) ~/ edgeOffset; final assetOffset = rowOffset + columnOffset; - final selectedAsset = cachedRenderList.value?.allAssets - ?.elementAtOrNull(assetOffset) - ?.remoteId; + final selectedAsset = cachedRenderList.value?.allAssets?.elementAtOrNull(assetOffset)?.remoteId; if (selectedAsset != null) { onGridAssetChanged?.call(selectedAsset); @@ -155,36 +148,31 @@ class MapAssetGrid extends HookConsumerWidget { heightFactor: 0.87, child: assetsInBounds.value.isNotEmpty ? ref - .watch(assetsTimelineProvider(assetsInBounds.value)) - .when( - data: (renderList) { - // Cache render list here to use it back during visibleItemsListener - cachedRenderList.value = renderList; - return ValueListenableBuilder( - valueListenable: selectedAssets, - builder: (_, value, __) => ImmichAssetGrid( - shrinkWrap: true, - renderList: renderList, - showDragScroll: false, - assetsPerRow: assetsPerRow, - showMultiSelectIndicator: false, - selectionActive: value.isNotEmpty, - listener: onAssetsSelected, - visibleItemsListener: (pos) => gridScrollThrottler - .run(() => handleVisibleItems(pos)), - ), - ); - }, - error: (error, stackTrace) { - log.warning( - "Cannot get assets in the current map bounds", - error, - stackTrace, - ); - return const SizedBox.shrink(); - }, - loading: () => const SizedBox.shrink(), - ) + .watch(assetsTimelineProvider(assetsInBounds.value)) + .when( + data: (renderList) { + // Cache render list here to use it back during visibleItemsListener + cachedRenderList.value = renderList; + return ValueListenableBuilder( + valueListenable: selectedAssets, + builder: (_, value, __) => ImmichAssetGrid( + shrinkWrap: true, + renderList: renderList, + showDragScroll: false, + assetsPerRow: assetsPerRow, + showMultiSelectIndicator: false, + selectionActive: value.isNotEmpty, + listener: onAssetsSelected, + visibleItemsListener: (pos) => gridScrollThrottler.run(() => handleVisibleItems(pos)), + ), + ); + }, + error: (error, stackTrace) { + log.warning("Cannot get assets in the current map bounds", error, stackTrace); + return const SizedBox.shrink(); + }, + loading: () => const SizedBox.shrink(), + ) : const _MapNoAssetsInSheet(), ), ), @@ -205,11 +193,7 @@ class _MapNoAssetsInSheet extends StatelessWidget { @override Widget build(BuildContext context) { - const image = Image( - height: 150, - width: 150, - image: AssetImage('assets/lighthouse.png'), - ); + const image = Image(height: 150, width: 150, image: AssetImage('assets/lighthouse.png')); return Center( child: ListView( @@ -217,21 +201,12 @@ class _MapNoAssetsInSheet extends StatelessWidget { children: [ context.isDarkTheme ? const InvertionFilter( - child: SaturationFilter( - saturation: -1, - child: BrightnessFilter( - brightness: -5, - child: image, - ), - ), + child: SaturationFilter(saturation: -1, child: BrightnessFilter(brightness: -5, child: image)), ) : image, const SizedBox(height: 20), Center( - child: Text( - "map_zoom_to_see_photos".tr(), - style: context.textTheme.displayLarge?.copyWith(fontSize: 18), - ), + child: Text("map_zoom_to_see_photos".tr(), style: context.textTheme.displayLarge?.copyWith(fontSize: 18)), ), ], ), @@ -255,8 +230,7 @@ class _MapSheetDragRegion extends StatelessWidget { @override Widget build(BuildContext context) { final assetsInBoundsText = assetsInBoundCount > 0 - ? "map_assets_in_bounds" - .tr(namedArgs: {'count': assetsInBoundCount.toString()}) + ? "map_assets_in_bounds".tr(namedArgs: {'count': assetsInBoundCount.toString()}) : "map_no_assets_in_bounds".tr(); return SingleChildScrollView( @@ -266,10 +240,7 @@ class _MapSheetDragRegion extends StatelessWidget { margin: EdgeInsets.zero, shape: context.isMobile ? const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topRight: Radius.circular(20), - topLeft: Radius.circular(20), - ), + borderRadius: BorderRadius.only(topRight: Radius.circular(20), topLeft: Radius.circular(20)), ) : const BeveledRectangleBorder(), elevation: 0.0, @@ -287,8 +258,7 @@ class _MapSheetDragRegion extends StatelessWidget { assetsInBoundsText, style: TextStyle( fontSize: 20, - color: context.textTheme.displayLarge?.color - ?.withValues(alpha: 0.75), + color: context.textTheme.displayLarge?.color?.withValues(alpha: 0.75), fontWeight: FontWeight.w500, ), ), @@ -304,10 +274,7 @@ class _MapSheetDragRegion extends StatelessWidget { right: 18, top: 24, child: IconButton( - icon: Icon( - Icons.map_outlined, - color: context.textTheme.displayLarge?.color, - ), + icon: Icon(Icons.map_outlined, color: context.textTheme.displayLarge?.color), iconSize: 24, tooltip: 'Zoom to bounds', onPressed: () => onZoomToAsset?.call(value!), diff --git a/mobile/lib/widgets/map/map_bottom_sheet.dart b/mobile/lib/widgets/map/map_bottom_sheet.dart index 0249ca70dc..baf85e8075 100644 --- a/mobile/lib/widgets/map/map_bottom_sheet.dart +++ b/mobile/lib/widgets/map/map_bottom_sheet.dart @@ -34,19 +34,14 @@ class MapBottomSheet extends HookConsumerWidget { void handleMapEvents(MapEvent event) async { if (event is MapCloseBottomSheet) { - sheetController.animateTo( - 0.1, - duration: const Duration(milliseconds: 200), - curve: Curves.linearToEaseOut, - ); + sheetController.animateTo(0.1, duration: const Duration(milliseconds: 200), curve: Curves.linearToEaseOut); } } useOnStreamChange(mapEventStream, onData: handleMapEvents); bool onScrollNotification(DraggableScrollableNotification notification) { - isBottomSheetOpened.value = - notification.extent > (notification.maxExtent * 0.9); + isBottomSheetOpened.value = notification.extent > (notification.maxExtent * 0.9); bottomSheetOffset.value = notification.extent; // do not bubble return true; @@ -70,9 +65,7 @@ class MapBottomSheet extends HookConsumerWidget { selectedAssets: selectedAssets, onAssetsSelected: onAssetsSelected, // Do not bother with the event if the bottom sheet is not user scrolled - onGridAssetChanged: (assetId) => isBottomSheetOpened.value - ? onGridAssetChanged?.call(assetId) - : null, + onGridAssetChanged: (assetId) => isBottomSheetOpened.value ? onGridAssetChanged?.call(assetId) : null, onZoomToAsset: onZoomToAsset, ), ), @@ -88,9 +81,7 @@ class MapBottomSheet extends HookConsumerWidget { duration: const Duration(milliseconds: 150), child: ElevatedButton( onPressed: onZoomToLocation, - style: ElevatedButton.styleFrom( - shape: const CircleBorder(), - ), + style: ElevatedButton.styleFrom(shape: const CircleBorder()), child: const Icon(Icons.my_location), ), ), diff --git a/mobile/lib/widgets/map/map_settings/map_settings_list_tile.dart b/mobile/lib/widgets/map/map_settings/map_settings_list_tile.dart index 1abe64ce31..51567eff15 100644 --- a/mobile/lib/widgets/map/map_settings/map_settings_list_tile.dart +++ b/mobile/lib/widgets/map/map_settings/map_settings_list_tile.dart @@ -8,22 +8,13 @@ class MapSettingsListTile extends StatelessWidget { final bool selected; final Function(bool) onChanged; - const MapSettingsListTile({ - super.key, - required this.title, - required this.selected, - required this.onChanged, - }); + const MapSettingsListTile({super.key, required this.title, required this.selected, required this.onChanged}); @override Widget build(BuildContext context) { return SwitchListTile.adaptive( activeColor: context.primaryColor, - title: Text( - title, - style: - context.textTheme.labelLarge?.copyWith(fontWeight: FontWeight.bold), - ).tr(), + title: Text(title, style: context.textTheme.labelLarge?.copyWith(fontWeight: FontWeight.bold)).tr(), value: selected, onChanged: onChanged, ); diff --git a/mobile/lib/widgets/map/map_settings/map_settings_time_dropdown.dart b/mobile/lib/widgets/map/map_settings/map_settings_time_dropdown.dart index e23716af95..b601887e1e 100644 --- a/mobile/lib/widgets/map/map_settings/map_settings_time_dropdown.dart +++ b/mobile/lib/widgets/map/map_settings/map_settings_time_dropdown.dart @@ -5,11 +5,7 @@ class MapTimeDropDown extends StatelessWidget { final int relativeTime; final Function(int) onTimeChange; - const MapTimeDropDown({ - super.key, - required this.relativeTime, - required this.onTimeChange, - }); + const MapTimeDropDown({super.key, required this.relativeTime, required this.onTimeChange}); @override Widget build(BuildContext context) { @@ -20,10 +16,7 @@ class MapTimeDropDown extends StatelessWidget { children: [ Padding( padding: const EdgeInsets.only(bottom: 20), - child: Text( - "date_range".tr(), - style: const TextStyle(fontWeight: FontWeight.bold), - ), + child: Text("date_range".tr(), style: const TextStyle(fontWeight: FontWeight.bold)), ), LayoutBuilder( builder: (_, constraints) => DropdownMenu( @@ -33,56 +26,21 @@ class MapTimeDropDown extends StatelessWidget { initialSelection: relativeTime, onSelected: (value) => onTimeChange(value!), dropdownMenuEntries: [ - DropdownMenuEntry( - value: 0, - label: "all".tr(), - ), - DropdownMenuEntry( - value: 1, - label: "map_settings_date_range_option_day".tr(), - ), - DropdownMenuEntry( - value: 7, - label: "map_settings_date_range_option_days".tr( - namedArgs: {'days': "7"}, - ), - ), - DropdownMenuEntry( - value: 30, - label: "map_settings_date_range_option_days".tr( - namedArgs: {'days': "30"}, - ), - ), + DropdownMenuEntry(value: 0, label: "all".tr()), + DropdownMenuEntry(value: 1, label: "map_settings_date_range_option_day".tr()), + DropdownMenuEntry(value: 7, label: "map_settings_date_range_option_days".tr(namedArgs: {'days': "7"})), + DropdownMenuEntry(value: 30, label: "map_settings_date_range_option_days".tr(namedArgs: {'days': "30"})), DropdownMenuEntry( value: now - .difference( - DateTime( - now.year - 1, - now.month, - now.day, - now.hour, - now.minute, - now.second, - ), - ) + .difference(DateTime(now.year - 1, now.month, now.day, now.hour, now.minute, now.second)) .inDays, label: "map_settings_date_range_option_year".tr(), ), DropdownMenuEntry( value: now - .difference( - DateTime( - now.year - 3, - now.month, - now.day, - now.hour, - now.minute, - now.second, - ), - ) + .difference(DateTime(now.year - 3, now.month, now.day, now.hour, now.minute, now.second)) .inDays, - label: "map_settings_date_range_option_years" - .tr(namedArgs: {'years': "3"}), + label: "map_settings_date_range_option_years".tr(namedArgs: {'years': "3"}), ), ], ), diff --git a/mobile/lib/widgets/map/map_settings/map_theme_picker.dart b/mobile/lib/widgets/map/map_settings/map_theme_picker.dart index 19298df076..63f35ebe4c 100644 --- a/mobile/lib/widgets/map/map_settings/map_theme_picker.dart +++ b/mobile/lib/widgets/map/map_settings/map_theme_picker.dart @@ -8,11 +8,7 @@ class MapThemePicker extends StatelessWidget { final ThemeMode themeMode; final Function(ThemeMode) onThemeChange; - const MapThemePicker({ - super.key, - required this.themeMode, - required this.onThemeChange, - }); + const MapThemePicker({super.key, required this.themeMode, required this.onThemeChange}); @override Widget build(BuildContext context) { @@ -23,8 +19,7 @@ class MapThemePicker extends StatelessWidget { child: Center( child: Text( "map_settings_theme_settings", - style: context.textTheme.bodyMedium - ?.copyWith(fontWeight: FontWeight.bold), + style: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.bold), ).tr(), ), ), @@ -77,12 +72,7 @@ class _BorderedMapThumbnail extends StatelessWidget { Container( decoration: BoxDecoration( border: Border.fromBorderSide( - BorderSide( - width: 4, - color: shouldHighlight - ? context.colorScheme.onSurface - : Colors.transparent, - ), + BorderSide(width: 4, color: shouldHighlight ? context.colorScheme.onSurface : Colors.transparent), ), borderRadius: const BorderRadius.all(Radius.circular(20)), ), @@ -98,9 +88,7 @@ class _BorderedMapThumbnail extends StatelessWidget { padding: const EdgeInsets.only(top: 10), child: Text( name, - style: context.textTheme.bodyMedium?.copyWith( - fontWeight: shouldHighlight ? FontWeight.bold : null, - ), + style: context.textTheme.bodyMedium?.copyWith(fontWeight: shouldHighlight ? FontWeight.bold : null), ), ), ], diff --git a/mobile/lib/widgets/map/map_settings_sheet.dart b/mobile/lib/widgets/map/map_settings_sheet.dart index 78d8aec75f..644056d153 100644 --- a/mobile/lib/widgets/map/map_settings_sheet.dart +++ b/mobile/lib/widgets/map/map_settings_sheet.dart @@ -26,37 +26,30 @@ class MapSettingsSheet extends HookConsumerWidget { children: [ MapThemePicker( themeMode: mapState.themeMode, - onThemeChange: (mode) => ref - .read(mapStateNotifierProvider.notifier) - .switchTheme(mode), + onThemeChange: (mode) => ref.read(mapStateNotifierProvider.notifier).switchTheme(mode), ), const Divider(height: 30, thickness: 2), MapSettingsListTile( title: "map_settings_only_show_favorites", selected: mapState.showFavoriteOnly, - onChanged: (favoriteOnly) => ref - .read(mapStateNotifierProvider.notifier) - .switchFavoriteOnly(favoriteOnly), + onChanged: (favoriteOnly) => + ref.read(mapStateNotifierProvider.notifier).switchFavoriteOnly(favoriteOnly), ), MapSettingsListTile( title: "map_settings_include_show_archived", selected: mapState.includeArchived, - onChanged: (includeArchive) => ref - .read(mapStateNotifierProvider.notifier) - .switchIncludeArchived(includeArchive), + onChanged: (includeArchive) => + ref.read(mapStateNotifierProvider.notifier).switchIncludeArchived(includeArchive), ), MapSettingsListTile( title: "map_settings_include_show_partners", selected: mapState.withPartners, - onChanged: (withPartners) => ref - .read(mapStateNotifierProvider.notifier) - .switchWithPartners(withPartners), + onChanged: (withPartners) => + ref.read(mapStateNotifierProvider.notifier).switchWithPartners(withPartners), ), MapTimeDropDown( relativeTime: mapState.relativeTime, - onTimeChange: (time) => ref - .read(mapStateNotifierProvider.notifier) - .setRelativeTime(time), + onTimeChange: (time) => ref.read(mapStateNotifierProvider.notifier).setRelativeTime(time), ), const SizedBox(height: 20), ], diff --git a/mobile/lib/widgets/map/map_theme_override.dart b/mobile/lib/widgets/map/map_theme_override.dart index 65425f9e78..57f970b0d1 100644 --- a/mobile/lib/widgets/map/map_theme_override.dart +++ b/mobile/lib/widgets/map/map_theme_override.dart @@ -18,25 +18,20 @@ class MapThemeOverride extends StatefulHookConsumerWidget { ConsumerState createState() => _MapThemeOverrideState(); } -class _MapThemeOverrideState extends ConsumerState - with WidgetsBindingObserver { +class _MapThemeOverrideState extends ConsumerState with WidgetsBindingObserver { late ThemeMode _theme; bool _isDarkTheme = false; - bool get _isSystemDark => - WidgetsBinding.instance.platformDispatcher.platformBrightness == - Brightness.dark; + bool get _isSystemDark => WidgetsBinding.instance.platformDispatcher.platformBrightness == Brightness.dark; bool checkDarkTheme() { - return _theme == ThemeMode.dark || - _theme == ThemeMode.system && _isSystemDark; + return _theme == ThemeMode.dark || _theme == ThemeMode.system && _isSystemDark; } @override void initState() { super.initState(); - _theme = widget.themeMode ?? - ref.read(mapStateNotifierProvider.select((v) => v.themeMode)); + _theme = widget.themeMode ?? ref.read(mapStateNotifierProvider.select((v) => v.themeMode)); setState(() { _isDarkTheme = checkDarkTheme(); }); @@ -70,8 +65,7 @@ class _MapThemeOverrideState extends ConsumerState @override Widget build(BuildContext context) { - _theme = widget.themeMode ?? - ref.watch(mapStateNotifierProvider.select((v) => v.themeMode)); + _theme = widget.themeMode ?? ref.watch(mapStateNotifierProvider.select((v) => v.themeMode)); var appTheme = ref.watch(immichThemeProvider); final locale = ref.watch(localeProvider); @@ -91,11 +85,7 @@ class _MapThemeOverrideState extends ConsumerState ? getThemeData(colorScheme: appTheme.dark, locale: locale) : getThemeData(colorScheme: appTheme.light, locale: locale), child: widget.mapBuilder.call( - ref.watch( - mapStateNotifierProvider.select( - (v) => _isDarkTheme ? v.darkStyleFetched : v.lightStyleFetched, - ), - ), + ref.watch(mapStateNotifierProvider.select((v) => _isDarkTheme ? v.darkStyleFetched : v.lightStyleFetched)), ), ); } diff --git a/mobile/lib/widgets/map/map_thumbnail.dart b/mobile/lib/widgets/map/map_thumbnail.dart index 1e4b061be6..55f5ff77c6 100644 --- a/mobile/lib/widgets/map/map_thumbnail.dart +++ b/mobile/lib/widgets/map/map_thumbnail.dart @@ -55,8 +55,7 @@ class MapThumbnail extends HookConsumerWidget { // The iOS impl returns wrong toScreenLocation without the delay Future.delayed( const Duration(milliseconds: 100), - () async => - position.value = await mapController.toScreenLocation(centre), + () async => position.value = await mapController.toScreenLocation(centre), ); } onCreated?.call(mapController); @@ -81,8 +80,7 @@ class MapThumbnail extends HookConsumerWidget { duration: Durations.medium2, curve: Curves.easeOut, foregroundDecoration: BoxDecoration( - color: context.colorScheme.inverseSurface - .withAlpha(styleLoaded.value ? 0 : 200), + color: context.colorScheme.inverseSurface.withAlpha(styleLoaded.value ? 0 : 200), borderRadius: const BorderRadius.all(Radius.circular(15)), ), height: height, @@ -94,8 +92,7 @@ class MapThumbnail extends HookConsumerWidget { children: [ style.widgetWhen( onData: (style) => MapLibreMap( - initialCameraPosition: - CameraPosition(target: offsettedCentre, zoom: zoom), + initialCameraPosition: CameraPosition(target: offsettedCentre, zoom: zoom), styleString: style, onMapCreated: onMapCreated, onStyleLoadedCallback: onStyleLoaded, @@ -107,18 +104,13 @@ class MapThumbnail extends HookConsumerWidget { scrollGesturesEnabled: false, rotateGesturesEnabled: false, myLocationEnabled: false, - attributionButtonMargins: - showAttribution == false ? const Point(-100, 0) : null, + attributionButtonMargins: showAttribution == false ? const Point(-100, 0) : null, ), ), ValueListenableBuilder( valueListenable: position, - builder: (_, value, __) => value != null - ? PositionedAssetMarkerIcon( - size: height / 2, - point: value, - assetRemoteId: assetMarkerRemoteId!, - ) + builder: (_, value, __) => value != null && assetMarkerRemoteId != null + ? PositionedAssetMarkerIcon(size: height / 2, point: value, assetRemoteId: assetMarkerRemoteId!) : const SizedBox.shrink(), ), ], diff --git a/mobile/lib/widgets/map/positioned_asset_marker_icon.dart b/mobile/lib/widgets/map/positioned_asset_marker_icon.dart index 5ad49b42f8..0944f7ce3e 100644 --- a/mobile/lib/widgets/map/positioned_asset_marker_icon.dart +++ b/mobile/lib/widgets/map/positioned_asset_marker_icon.dart @@ -35,10 +35,7 @@ class PositionedAssetMarkerIcon extends StatelessWidget { onTap: () => onTap?.call(), child: SizedBox.square( dimension: size, - child: _AssetMarkerIcon( - id: assetRemoteId, - key: Key(assetRemoteId), - ), + child: _AssetMarkerIcon(id: assetRemoteId, key: Key(assetRemoteId)), ), ), ); @@ -46,10 +43,7 @@ class PositionedAssetMarkerIcon extends StatelessWidget { } class _AssetMarkerIcon extends StatelessWidget { - const _AssetMarkerIcon({ - required this.id, - super.key, - }); + const _AssetMarkerIcon({required this.id, super.key}); final String id; @@ -71,10 +65,7 @@ class _AssetMarkerIcon extends StatelessWidget { primaryRadius: constraints.maxHeight * 0.06, secondaryRadius: constraints.maxHeight * 0.038, ), - child: SizedBox( - height: constraints.maxHeight * 0.14, - width: constraints.maxWidth * 0.14, - ), + child: SizedBox(height: constraints.maxHeight * 0.14, width: constraints.maxWidth * 0.14), ), ), Positioned( @@ -89,8 +80,7 @@ class _AssetMarkerIcon extends StatelessWidget { imageUrl, cacheKey: cacheKey, headers: ApiService.getRequestHeaders(), - errorListener: (_) => - const Icon(Icons.image_not_supported_outlined), + errorListener: (_) => const Icon(Icons.image_not_supported_outlined), ), ), ), @@ -130,26 +120,11 @@ class _PinPainter extends CustomPainter { ..style = PaintingStyle.stroke ..strokeWidth = 2; - canvas.drawCircle( - Offset(size.width / 2, size.height), - primaryRadius, - primaryBrush, - ); - canvas.drawCircle( - Offset(size.width / 2, size.height), - secondaryRadius, - secondaryBrush, - ); + canvas.drawCircle(Offset(size.width / 2, size.height), primaryRadius, primaryBrush); + canvas.drawCircle(Offset(size.width / 2, size.height), secondaryRadius, secondaryBrush); canvas.drawPath(getTrianglePath(size.width, size.height), primaryBrush); // The line is to make the above triangluar path more prominent since it has a slight curve - canvas.drawLine( - Offset(size.width / 2, 0), - Offset( - size.width / 2, - size.height, - ), - lineBrush, - ); + canvas.drawLine(Offset(size.width / 2, 0), Offset(size.width / 2, size.height), lineBrush); } Path getTrianglePath(double x, double y) { @@ -158,24 +133,13 @@ class _PinPainter extends CustomPainter { final secondEndPoint = Offset(x, 0); return Path() - ..quadraticBezierTo( - controlPoint.dx, - controlPoint.dy, - firstEndPoint.dx, - firstEndPoint.dy, - ) - ..quadraticBezierTo( - controlPoint.dx, - controlPoint.dy, - secondEndPoint.dx, - secondEndPoint.dy, - ) + ..quadraticBezierTo(controlPoint.dx, controlPoint.dy, firstEndPoint.dx, firstEndPoint.dy) + ..quadraticBezierTo(controlPoint.dx, controlPoint.dy, secondEndPoint.dx, secondEndPoint.dy) ..lineTo(0, 0); } @override bool shouldRepaint(_PinPainter old) { - return old.primaryColor != primaryColor || - old.secondaryColor != secondaryColor; + return old.primaryColor != primaryColor || old.secondaryColor != secondaryColor; } } diff --git a/mobile/lib/widgets/memories/memory_bottom_info.dart b/mobile/lib/widgets/memories/memory_bottom_info.dart index 6adf1d46b0..4b43821782 100644 --- a/mobile/lib/widgets/memories/memory_bottom_info.dart +++ b/mobile/lib/widgets/memories/memory_bottom_info.dart @@ -16,46 +16,35 @@ class MemoryBottomInfo extends StatelessWidget { final df = DateFormat.yMMMMd(); return Padding( padding: const EdgeInsets.all(16.0), - child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - memory.title, - style: TextStyle( - color: Colors.grey[400], - fontSize: 13.0, - fontWeight: FontWeight.w500, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + memory.title, + style: TextStyle(color: Colors.grey[400], fontSize: 13.0, fontWeight: FontWeight.w500), ), - ), - Text( - df.format( - memory.assets[0].fileCreatedAt, + Text( + df.format(memory.assets[0].fileCreatedAt), + style: const TextStyle(color: Colors.white, fontSize: 15.0, fontWeight: FontWeight.w500), ), - style: const TextStyle( - color: Colors.white, - fontSize: 15.0, - fontWeight: FontWeight.w500, - ), - ), - ], - ), - MaterialButton( - minWidth: 0, - onPressed: () { - context.maybePop(); - scrollToDateNotifierProvider - .scrollToDate(memory.assets[0].fileCreatedAt); - }, - shape: const CircleBorder(), - color: Colors.white.withValues(alpha: 0.2), - elevation: 0, - child: const Icon( - Icons.open_in_new, - color: Colors.white, + ], ), - ), - ]), + MaterialButton( + minWidth: 0, + onPressed: () { + context.maybePop(); + scrollToDateNotifierProvider.scrollToDate(memory.assets[0].fileCreatedAt); + }, + shape: const CircleBorder(), + color: Colors.white.withValues(alpha: 0.2), + elevation: 0, + child: const Icon(Icons.open_in_new, color: Colors.white), + ), + ], + ), ); } } diff --git a/mobile/lib/widgets/memories/memory_card.dart b/mobile/lib/widgets/memories/memory_card.dart index 31f4d5ed94..189cc67428 100644 --- a/mobile/lib/widgets/memories/memory_card.dart +++ b/mobile/lib/widgets/memories/memory_card.dart @@ -14,13 +14,7 @@ class MemoryCard extends StatelessWidget { final bool showTitle; final Function()? onVideoEnded; - const MemoryCard({ - required this.asset, - required this.title, - required this.showTitle, - this.onVideoEnded, - super.key, - }); + const MemoryCard({required this.asset, required this.title, required this.showTitle, this.onVideoEnded, super.key}); @override Widget build(BuildContext context) { @@ -28,28 +22,21 @@ class MemoryCard extends StatelessWidget { color: Colors.black, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(25.0)), - side: BorderSide( - color: Colors.black, - width: 1.0, - ), + side: BorderSide(color: Colors.black, width: 1.0), ), clipBehavior: Clip.hardEdge, child: Stack( children: [ - SizedBox.expand( - child: _BlurredBackdrop(asset: asset), - ), + SizedBox.expand(child: _BlurredBackdrop(asset: asset)), LayoutBuilder( builder: (context, constraints) { // Determine the fit using the aspect ratio BoxFit fit = BoxFit.contain; if (asset.width != null && asset.height != null) { final aspectRatio = asset.width! / asset.height!; - final phoneAspectRatio = - constraints.maxWidth / constraints.maxHeight; + final phoneAspectRatio = constraints.maxWidth / constraints.maxHeight; // Look for a 25% difference in either direction - if (phoneAspectRatio * .75 < aspectRatio && - phoneAspectRatio * 1.25 > aspectRatio) { + if (phoneAspectRatio * .75 < aspectRatio && phoneAspectRatio * 1.25 > aspectRatio) { // Cover to look nice if we have nearly the same aspect ratio fit = BoxFit.cover; } @@ -58,12 +45,7 @@ class MemoryCard extends StatelessWidget { if (asset.isImage) { return Hero( tag: 'memory-${asset.id}', - child: ImmichImage( - asset, - fit: fit, - height: double.infinity, - width: double.infinity, - ), + child: ImmichImage(asset, fit: fit, height: double.infinity, width: double.infinity), ); } else { return Hero( @@ -76,12 +58,7 @@ class MemoryCard extends StatelessWidget { asset: asset, showControls: false, playbackDelayFactor: 2, - image: ImmichImage( - asset, - width: context.width, - height: context.height, - fit: BoxFit.contain, - ), + image: ImmichImage(asset, width: context.width, height: context.height, fit: BoxFit.contain), ), ), ); @@ -94,10 +71,7 @@ class MemoryCard extends StatelessWidget { bottom: 18.0, child: Text( title, - style: context.textTheme.headlineMedium?.copyWith( - color: Colors.white, - fontWeight: FontWeight.w500, - ), + style: context.textTheme.headlineMedium?.copyWith(color: Colors.white, fontWeight: FontWeight.w500), ), ), ], @@ -118,16 +92,9 @@ class _BlurredBackdrop extends HookWidget { // Use a nice cheap blur hash image decoration return Container( decoration: BoxDecoration( - image: DecorationImage( - image: MemoryImage( - blurhash, - ), - fit: BoxFit.cover, - ), - ), - child: Container( - color: Colors.black.withValues(alpha: 0.2), + image: DecorationImage(image: MemoryImage(blurhash), fit: BoxFit.cover), ), + child: Container(color: Colors.black.withValues(alpha: 0.2)), ); } else { // Fall back to using a more expensive image filtered @@ -138,17 +105,11 @@ class _BlurredBackdrop extends HookWidget { child: Container( decoration: BoxDecoration( image: DecorationImage( - image: ImmichImage.imageProvider( - asset: asset, - height: context.height, - width: context.width, - ), + image: ImmichImage.imageProvider(asset: asset, height: context.height, width: context.width), fit: BoxFit.cover, ), ), - child: Container( - color: Colors.black.withValues(alpha: 0.2), - ), + child: Container(color: Colors.black.withValues(alpha: 0.2)), ), ); } diff --git a/mobile/lib/widgets/memories/memory_epilogue.dart b/mobile/lib/widgets/memories/memory_epilogue.dart index 9796bee6b1..b866ed049a 100644 --- a/mobile/lib/widgets/memories/memory_epilogue.dart +++ b/mobile/lib/widgets/memories/memory_epilogue.dart @@ -11,26 +11,16 @@ class MemoryEpilogue extends StatefulWidget { State createState() => _MemoryEpilogueState(); } -class _MemoryEpilogueState extends State - with TickerProviderStateMixin { - late final _animationController = AnimationController( - vsync: this, - duration: const Duration( - seconds: 2, - ), - )..repeat( - reverse: true, - ); +class _MemoryEpilogueState extends State with TickerProviderStateMixin { + late final _animationController = AnimationController(vsync: this, duration: const Duration(seconds: 2)) + ..repeat(reverse: true); late final Animation _animation; @override void initState() { super.initState(); - _animation = CurvedAnimation( - parent: _animationController, - curve: Curves.easeIn, - ); + _animation = CurvedAnimation(parent: _animationController, curve: Curves.easeIn); } @override @@ -50,24 +40,18 @@ class _MemoryEpilogueState extends State children: [ Icon( Icons.check_circle_outline_sharp, - color: context.isDarkTheme - ? context.colorScheme.primary - : context.colorScheme.inversePrimary, + color: context.isDarkTheme ? context.colorScheme.primary : context.colorScheme.inversePrimary, size: 64.0, ), const SizedBox(height: 16.0), Text( "memories_all_caught_up", - style: context.textTheme.headlineMedium?.copyWith( - color: Colors.white, - ), + style: context.textTheme.headlineMedium?.copyWith(color: Colors.white), ).tr(), const SizedBox(height: 16.0), Text( "memories_check_back_tomorrow", - style: context.textTheme.bodyMedium?.copyWith( - color: Colors.white, - ), + style: context.textTheme.bodyMedium?.copyWith(color: Colors.white), ).tr(), const SizedBox(height: 16.0), TextButton( @@ -75,9 +59,7 @@ class _MemoryEpilogueState extends State child: Text( "memories_start_over", style: context.textTheme.displayMedium?.copyWith( - color: context.isDarkTheme - ? context.colorScheme.primary - : context.colorScheme.inversePrimary, + color: context.isDarkTheme ? context.colorScheme.primary : context.colorScheme.inversePrimary, ), ).tr(), ), @@ -97,23 +79,14 @@ class _MemoryEpilogueState extends State child: AnimatedBuilder( animation: _animation, builder: (context, child) { - return Transform.translate( - offset: Offset(0, 8 * _animationController.value), - child: child, - ); + return Transform.translate(offset: Offset(0, 8 * _animationController.value), child: child); }, - child: const Icon( - size: 32, - Icons.expand_less_sharp, - color: Colors.white, - ), + child: const Icon(size: 32, Icons.expand_less_sharp, color: Colors.white), ), ), Text( "memories_swipe_to_close", - style: context.textTheme.bodyMedium?.copyWith( - color: Colors.white, - ), + style: context.textTheme.bodyMedium?.copyWith(color: Colors.white), ).tr(), ], ), diff --git a/mobile/lib/widgets/memories/memory_lane.dart b/mobile/lib/widgets/memories/memory_lane.dart index 3f97bd1ea4..727950fd86 100644 --- a/mobile/lib/widgets/memories/memory_lane.dart +++ b/mobile/lib/widgets/memories/memory_lane.dart @@ -22,17 +22,13 @@ class MemoryLane extends HookConsumerWidget { .whenData( (memories) => memories != null ? ConstrainedBox( - constraints: const BoxConstraints( - maxHeight: 200, - ), + constraints: const BoxConstraints(maxHeight: 200), child: CarouselView( itemExtent: 145.0, shrinkExtent: 1.0, elevation: 2, backgroundColor: Colors.black, - overlayColor: WidgetStateProperty.all( - Colors.white.withValues(alpha: 0.1), - ), + overlayColor: WidgetStateProperty.all(Colors.white.withValues(alpha: 0.1)), onTap: (memoryIndex) { ref.read(hapticFeedbackProvider.notifier).heavyImpact(); if (memories[memoryIndex].assets.isNotEmpty) { @@ -42,20 +38,10 @@ class MemoryLane extends HookConsumerWidget { ref.read(videoPlaybackValueProvider.notifier).reset(); } } - context.pushRoute( - MemoryRoute( - memories: memories, - memoryIndex: memoryIndex, - ), - ); + context.pushRoute(MemoryRoute(memories: memories, memoryIndex: memoryIndex)); }, children: memories - .mapIndexed( - (index, memory) => MemoryCard( - index: index, - memory: memory, - ), - ) + .mapIndexed((index, memory) => MemoryCard(index: index, memory: memory)) .toList(), ), ) @@ -68,11 +54,7 @@ class MemoryLane extends HookConsumerWidget { } class MemoryCard extends ConsumerWidget { - const MemoryCard({ - super.key, - required this.index, - required this.memory, - }); + const MemoryCard({super.key, required this.index, required this.memory}); final int index; final Memory memory; @@ -83,10 +65,7 @@ class MemoryCard extends ConsumerWidget { child: Stack( children: [ ColorFiltered( - colorFilter: ColorFilter.mode( - Colors.black.withValues(alpha: 0.2), - BlendMode.darken, - ), + colorFilter: ColorFilter.mode(Colors.black.withValues(alpha: 0.2), BlendMode.darken), child: Hero( tag: 'memory-${memory.assets[0].id}', child: ImmichImage( @@ -94,10 +73,7 @@ class MemoryCard extends ConsumerWidget { fit: BoxFit.cover, width: 205, height: 200, - placeholder: const ThumbnailPlaceholder( - width: 105, - height: 200, - ), + placeholder: const ThumbnailPlaceholder(width: 105, height: 200), ), ), ), @@ -105,16 +81,10 @@ class MemoryCard extends ConsumerWidget { bottom: 16, left: 16, child: ConstrainedBox( - constraints: const BoxConstraints( - maxWidth: 114, - ), + constraints: const BoxConstraints(maxWidth: 114), child: Text( memory.title, - style: const TextStyle( - fontWeight: FontWeight.w600, - color: Colors.white, - fontSize: 15, - ), + style: const TextStyle(fontWeight: FontWeight.w600, color: Colors.white, fontSize: 15), ), ), ), diff --git a/mobile/lib/widgets/memories/memory_progress_indicator.dart b/mobile/lib/widgets/memories/memory_progress_indicator.dart index 438816d99c..aab1dc1a97 100644 --- a/mobile/lib/widgets/memories/memory_progress_indicator.dart +++ b/mobile/lib/widgets/memories/memory_progress_indicator.dart @@ -8,11 +8,7 @@ class MemoryProgressIndicator extends StatelessWidget { /// The current value of the indicator final double value; - const MemoryProgressIndicator({ - super.key, - required this.ticks, - required this.value, - }); + const MemoryProgressIndicator({super.key, required this.ticks, required this.value}); @override Widget build(BuildContext context) { @@ -27,9 +23,7 @@ class MemoryProgressIndicator extends StatelessWidget { value: value, borderRadius: const BorderRadius.all(Radius.circular(10.0)), backgroundColor: Colors.grey[800], - color: context.isDarkTheme - ? context.colorScheme.primary - : context.colorScheme.inversePrimary, + color: context.isDarkTheme ? context.colorScheme.primary : context.colorScheme.inversePrimary, ), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, @@ -39,14 +33,7 @@ class MemoryProgressIndicator extends StatelessWidget { width: tickWidth, height: 4, decoration: BoxDecoration( - border: i == 0 - ? null - : const Border( - left: BorderSide( - color: Colors.black, - width: 1, - ), - ), + border: i == 0 ? null : const Border(left: BorderSide(color: Colors.black, width: 1)), ), ), ), diff --git a/mobile/lib/widgets/photo_view/photo_view.dart b/mobile/lib/widgets/photo_view/photo_view.dart index 30e08748b8..69be96ed53 100644 --- a/mobile/lib/widgets/photo_view/photo_view.dart +++ b/mobile/lib/widgets/photo_view/photo_view.dart @@ -9,16 +9,13 @@ import 'package:immich_mobile/widgets/photo_view/src/utils/photo_view_hero_attri export 'src/controller/photo_view_controller.dart'; export 'src/controller/photo_view_scalestate_controller.dart'; -export 'src/core/photo_view_gesture_detector.dart' - show PhotoViewGestureDetectorScope, PhotoViewPageViewScrollPhysics; +export 'src/core/photo_view_gesture_detector.dart' show PhotoViewGestureDetectorScope, PhotoViewPageViewScrollPhysics; export 'src/photo_view_computed_scale.dart'; export 'src/photo_view_scale_state.dart'; export 'src/utils/photo_view_hero_attributes.dart'; typedef PhotoViewControllerCallback = PhotoViewControllerBase Function(); -typedef PhotoViewControllerCallbackBuilder = void Function( - PhotoViewControllerCallback photoViewMethod, -); +typedef PhotoViewControllerCallbackBuilder = void Function(PhotoViewControllerCallback photoViewMethod); /// A [StatefulWidget] that contains all the photo view rendering elements. /// @@ -270,8 +267,8 @@ class PhotoView extends StatefulWidget { this.disableScaleGestures, this.errorBuilder, this.enablePanAlways, - }) : child = null, - childSize = null; + }) : child = null, + childSize = null; /// Creates a widget that displays a zoomable child. /// @@ -311,12 +308,12 @@ class PhotoView extends StatefulWidget { this.disableScaleGestures, this.disableGestures, this.enablePanAlways, - }) : semanticLabel = null, - errorBuilder = null, - imageProvider = null, - gaplessPlayback = false, - loadingBuilder = null, - index = 0; + }) : semanticLabel = null, + errorBuilder = null, + imageProvider = null, + gaplessPlayback = false, + loadingBuilder = null, + index = 0; /// Given a [imageProvider] it resolves into an zoomable image widget using. It /// is required @@ -461,8 +458,7 @@ class PhotoView extends StatefulWidget { } } -class _PhotoViewState extends State - with AutomaticKeepAliveClientMixin { +class _PhotoViewState extends State with AutomaticKeepAliveClientMixin { // image retrieval // controller @@ -545,13 +541,9 @@ class _PhotoViewState extends State Widget build(BuildContext context) { super.build(context); return LayoutBuilder( - builder: ( - BuildContext context, - BoxConstraints constraints, - ) { + builder: (BuildContext context, BoxConstraints constraints) { final computedOuterSize = widget.customSize ?? constraints.biggest; - final backgroundDecoration = widget.backgroundDecoration ?? - const BoxDecoration(color: Colors.black); + final backgroundDecoration = widget.backgroundDecoration ?? const BoxDecoration(color: Colors.black); return widget._isCustomChild ? CustomChildWrapper( @@ -625,76 +617,50 @@ class _PhotoViewState extends State } /// The default [ScaleStateCycle] -PhotoViewScaleState defaultScaleStateCycle(PhotoViewScaleState actual) => - switch (actual) { - PhotoViewScaleState.initial => PhotoViewScaleState.covering, - PhotoViewScaleState.covering => PhotoViewScaleState.originalSize, - PhotoViewScaleState.originalSize => PhotoViewScaleState.initial, - PhotoViewScaleState.zoomedIn || - PhotoViewScaleState.zoomedOut => - PhotoViewScaleState.initial, - }; +PhotoViewScaleState defaultScaleStateCycle(PhotoViewScaleState actual) => switch (actual) { + PhotoViewScaleState.initial => PhotoViewScaleState.covering, + PhotoViewScaleState.covering => PhotoViewScaleState.originalSize, + PhotoViewScaleState.originalSize => PhotoViewScaleState.initial, + PhotoViewScaleState.zoomedIn || PhotoViewScaleState.zoomedOut => PhotoViewScaleState.initial, +}; /// A type definition for a [Function] that receives the actual [PhotoViewScaleState] and returns the next one /// It is used internally to walk in the "doubletap gesture cycle". /// It is passed to [PhotoView.scaleStateCycle] -typedef ScaleStateCycle = PhotoViewScaleState Function( - PhotoViewScaleState actual, -); +typedef ScaleStateCycle = PhotoViewScaleState Function(PhotoViewScaleState actual); /// A type definition for a callback when the user taps up the photoview region -typedef PhotoViewImageTapUpCallback = Function( - BuildContext context, - TapUpDetails details, - PhotoViewControllerValue controllerValue, -); +typedef PhotoViewImageTapUpCallback = + Function(BuildContext context, TapUpDetails details, PhotoViewControllerValue controllerValue); /// A type definition for a callback when the user taps down the photoview region -typedef PhotoViewImageTapDownCallback = Function( - BuildContext context, - TapDownDetails details, - PhotoViewControllerValue controllerValue, -); +typedef PhotoViewImageTapDownCallback = + Function(BuildContext context, TapDownDetails details, PhotoViewControllerValue controllerValue); /// A type definition for a callback when the user drags up -typedef PhotoViewImageDragStartCallback = Function( - BuildContext context, - DragStartDetails details, - PhotoViewControllerBase controllerValue, - PhotoViewScaleStateController scaleStateController, -); +typedef PhotoViewImageDragStartCallback = + Function( + BuildContext context, + DragStartDetails details, + PhotoViewControllerBase controllerValue, + PhotoViewScaleStateController scaleStateController, + ); /// A type definition for a callback when the user drags -typedef PhotoViewImageDragUpdateCallback = Function( - BuildContext context, - DragUpdateDetails details, - PhotoViewControllerValue controllerValue, -); +typedef PhotoViewImageDragUpdateCallback = + Function(BuildContext context, DragUpdateDetails details, PhotoViewControllerValue controllerValue); /// A type definition for a callback when the user taps down the photoview region -typedef PhotoViewImageDragEndCallback = Function( - BuildContext context, - DragEndDetails details, - PhotoViewControllerValue controllerValue, -); +typedef PhotoViewImageDragEndCallback = + Function(BuildContext context, DragEndDetails details, PhotoViewControllerValue controllerValue); /// A type definition for a callback when a user finished scale -typedef PhotoViewImageScaleEndCallback = Function( - BuildContext context, - ScaleEndDetails details, - PhotoViewControllerValue controllerValue, -); +typedef PhotoViewImageScaleEndCallback = + Function(BuildContext context, ScaleEndDetails details, PhotoViewControllerValue controllerValue); /// A type definition for a callback when the user long press start -typedef PhotoViewImageLongPressStartCallback = Function( - BuildContext context, - LongPressStartDetails details, - PhotoViewControllerValue controllerValue, -); +typedef PhotoViewImageLongPressStartCallback = + Function(BuildContext context, LongPressStartDetails details, PhotoViewControllerValue controllerValue); /// A type definition for a callback to show a widget while the image is loading, a [ImageChunkEvent] is passed to inform progress -typedef LoadingBuilder = Widget Function( - BuildContext context, - ImageChunkEvent? event, - int index, -); +typedef LoadingBuilder = Widget Function(BuildContext context, ImageChunkEvent? event, int index); diff --git a/mobile/lib/widgets/photo_view/photo_view_gallery.dart b/mobile/lib/widgets/photo_view/photo_view_gallery.dart index 1cd4d4b217..af5b9a7ce7 100644 --- a/mobile/lib/widgets/photo_view/photo_view_gallery.dart +++ b/mobile/lib/widgets/photo_view/photo_view_gallery.dart @@ -20,16 +20,10 @@ import 'package:immich_mobile/widgets/photo_view/src/photo_view_scale_state.dart import 'package:immich_mobile/widgets/photo_view/src/utils/photo_view_hero_attributes.dart'; /// A type definition for a [Function] that receives a index after a page change in [PhotoViewGallery] -typedef PhotoViewGalleryPageChangedCallback = void Function( - int index, - PhotoViewControllerBase? controller, -); +typedef PhotoViewGalleryPageChangedCallback = void Function(int index, PhotoViewControllerBase? controller); /// A type definition for a [Function] that defines a page in [PhotoViewGallery.build] -typedef PhotoViewGalleryBuilder = PhotoViewGalleryPageOptions Function( - BuildContext context, - int index, -); +typedef PhotoViewGalleryBuilder = PhotoViewGalleryPageOptions Function(BuildContext context, int index); /// A [StatefulWidget] that shows multiple [PhotoView] widgets in a [PageView] /// @@ -126,8 +120,8 @@ class PhotoViewGallery extends StatefulWidget { this.customSize, this.allowImplicitScrolling = false, this.enablePanAlways = false, - }) : itemCount = null, - builder = null; + }) : itemCount = null, + builder = null; /// Construct a gallery with dynamic items. /// @@ -151,9 +145,9 @@ class PhotoViewGallery extends StatefulWidget { this.customSize, this.allowImplicitScrolling = false, this.enablePanAlways = false, - }) : pageOptions = null, - assert(itemCount != null), - assert(builder != null); + }) : pageOptions = null, + assert(itemCount != null), + assert(builder != null); /// A list of options to describe the items in the gallery final List? pageOptions; @@ -218,8 +212,7 @@ class PhotoViewGallery extends StatefulWidget { } class _PhotoViewGalleryState extends State { - late final PageController _controller = - widget.pageController ?? PageController(); + late final PageController _controller = widget.pageController ?? PageController(); PhotoViewControllerCallback? _getController; void scaleStateChangedCallback(PhotoViewScaleState scaleState) { @@ -274,7 +267,7 @@ class _PhotoViewGalleryState extends State { key: pageOption.key ?? ObjectKey(index), childSize: pageOption.childSize, backgroundDecoration: widget.backgroundDecoration, - wantKeepAlive: widget.wantKeepAlive, + wantKeepAlive: false, controller: pageOption.controller, scaleStateController: pageOption.scaleStateController, customSize: widget.customSize, @@ -310,7 +303,7 @@ class _PhotoViewGalleryState extends State { loadingBuilder: widget.loadingBuilder, backgroundDecoration: widget.backgroundDecoration, semanticLabel: pageOption.semanticLabel, - wantKeepAlive: widget.wantKeepAlive, + wantKeepAlive: false, controller: pageOption.controller, onPageBuild: widget.onPageBuild, controllerCallbackBuilder: _getControllerCallbackBuilder, @@ -341,15 +334,10 @@ class _PhotoViewGalleryState extends State { heroAttributes: pageOption.heroAttributes, ); - return ClipRect( - child: photoView, - ); + return ClipRect(child: photoView); } - PhotoViewGalleryPageOptions _buildPageOption( - BuildContext context, - int index, - ) { + PhotoViewGalleryPageOptions _buildPageOption(BuildContext context, int index) { if (widget._isBuilder) { return widget.builder!(context, index); } @@ -387,9 +375,9 @@ class PhotoViewGalleryPageOptions { this.disableScaleGestures, this.disableGestures, this.errorBuilder, - }) : child = null, - childSize = null, - assert(imageProvider != null); + }) : child = null, + childSize = null, + assert(imageProvider != null); const PhotoViewGalleryPageOptions.customChild({ this.key, @@ -416,8 +404,8 @@ class PhotoViewGalleryPageOptions { this.filterQuality, this.disableScaleGestures, this.disableGestures, - }) : errorBuilder = null, - imageProvider = null; + }) : errorBuilder = null, + imageProvider = null; final Key? key; diff --git a/mobile/lib/widgets/photo_view/src/controller/photo_view_controller.dart b/mobile/lib/widgets/photo_view/src/controller/photo_view_controller.dart index 37d1c78de1..2c8b406385 100644 --- a/mobile/lib/widgets/photo_view/src/controller/photo_view_controller.dart +++ b/mobile/lib/widgets/photo_view/src/controller/photo_view_controller.dart @@ -72,12 +72,7 @@ abstract class PhotoViewControllerBase { Offset? rotationFocusPoint; /// Update multiple fields of the state with only one update streamed. - void updateMultiple({ - Offset? position, - double? scale, - double? rotation, - Offset? rotationFocusPoint, - }); + void updateMultiple({Offset? position, double? scale, double? rotation, Offset? rotationFocusPoint}); } /// The state value stored and streamed by [PhotoViewController]. @@ -106,11 +101,7 @@ class PhotoViewControllerValue { rotationFocusPoint == other.rotationFocusPoint; @override - int get hashCode => - position.hashCode ^ - scale.hashCode ^ - rotation.hashCode ^ - rotationFocusPoint.hashCode; + int get hashCode => position.hashCode ^ scale.hashCode ^ rotation.hashCode ^ rotationFocusPoint.hashCode; @override String toString() { @@ -125,21 +116,17 @@ class PhotoViewControllerValue { /// /// For details of fields and methods, check [PhotoViewControllerBase]. /// -class PhotoViewController - implements PhotoViewControllerBase { - PhotoViewController({ - Offset initialPosition = Offset.zero, - double initialRotation = 0.0, - double? initialScale, - }) : _valueNotifier = IgnorableValueNotifier( - PhotoViewControllerValue( - position: initialPosition, - rotation: initialRotation, - scale: initialScale, - rotationFocusPoint: null, - ), +class PhotoViewController implements PhotoViewControllerBase { + PhotoViewController({Offset initialPosition = Offset.zero, double initialRotation = 0.0, double? initialScale}) + : _valueNotifier = IgnorableValueNotifier( + PhotoViewControllerValue( + position: initialPosition, + rotation: initialRotation, + scale: initialScale, + rotationFocusPoint: null, ), - super() { + ), + super() { initial = value; prevValue = initial; @@ -304,12 +291,7 @@ class PhotoViewController Offset? get rotationFocusPoint => value.rotationFocusPoint; @override - void updateMultiple({ - Offset? position, - double? scale, - double? rotation, - Offset? rotationFocusPoint, - }) { + void updateMultiple({Offset? position, double? scale, double? rotation, Offset? rotationFocusPoint}) { prevValue = value; value = PhotoViewControllerValue( position: position ?? value.position, diff --git a/mobile/lib/widgets/photo_view/src/controller/photo_view_controller_delegate.dart b/mobile/lib/widgets/photo_view/src/controller/photo_view_controller_delegate.dart index e2e668199a..6825bf8ee1 100644 --- a/mobile/lib/widgets/photo_view/src/controller/photo_view_controller_delegate.dart +++ b/mobile/lib/widgets/photo_view/src/controller/photo_view_controller_delegate.dart @@ -1,10 +1,6 @@ import 'package:flutter/widgets.dart'; import 'package:immich_mobile/widgets/photo_view/photo_view.dart' - show - PhotoViewControllerBase, - PhotoViewScaleState, - PhotoViewScaleStateController, - ScaleStateCycle; + show PhotoViewControllerBase, PhotoViewScaleState, PhotoViewScaleStateController, ScaleStateCycle; import 'package:immich_mobile/widgets/photo_view/src/core/photo_view_core.dart'; import 'package:immich_mobile/widgets/photo_view/src/utils/photo_view_utils.dart'; @@ -14,8 +10,7 @@ import 'package:immich_mobile/widgets/photo_view/src/utils/photo_view_utils.dart mixin PhotoViewControllerDelegate on State { PhotoViewControllerBase get controller => widget.controller; - PhotoViewScaleStateController get scaleStateController => - widget.scaleStateController; + PhotoViewScaleStateController get scaleStateController => widget.scaleStateController; ScaleBoundaries get scaleBoundaries => widget.scaleBoundaries; @@ -40,23 +35,15 @@ mixin PhotoViewControllerDelegate on State { controller.setScaleInvisibly(scale); return; } - final double prevScale = controller.scale ?? - getScaleForScaleState( - scaleStateController.prevScaleState, - scaleBoundaries, - ); + final double prevScale = + controller.scale ?? getScaleForScaleState(scaleStateController.prevScaleState, scaleBoundaries); - final double nextScale = getScaleForScaleState( - scaleStateController.scaleState, - scaleBoundaries, - ); + final double nextScale = getScaleForScaleState(scaleStateController.scaleState, scaleBoundaries); _animateScale!(prevScale, nextScale); } - void addAnimateOnScaleStateUpdate( - void Function(double prevScale, double nextScale) animateScale, - ) { + void addAnimateOnScaleStateUpdate(void Function(double prevScale, double nextScale) animateScale) { _animateScale = animateScale; } @@ -67,10 +54,9 @@ mixin PhotoViewControllerDelegate on State { if (controller.scale == controller.prevValue.scale) { return; } - final PhotoViewScaleState newScaleState = - (scale > scaleBoundaries.initialScale) - ? PhotoViewScaleState.zoomedIn - : PhotoViewScaleState.zoomedOut; + final PhotoViewScaleState newScaleState = (scale > scaleBoundaries.initialScale) + ? PhotoViewScaleState.zoomedIn + : PhotoViewScaleState.zoomedOut; scaleStateController.setInvisibly(newScaleState); } @@ -79,15 +65,11 @@ mixin PhotoViewControllerDelegate on State { double get scale { // for figuring out initial scale - final needsRecalc = markNeedsScaleRecalc && - !scaleStateController.scaleState.isScaleStateZooming; + final needsRecalc = markNeedsScaleRecalc && !scaleStateController.scaleState.isScaleStateZooming; final scaleExistsOnController = controller.scale != null; if (needsRecalc || !scaleExistsOnController) { - final newScale = getScaleForScaleState( - scaleStateController.scaleState, - scaleBoundaries, - ); + final newScale = getScaleForScaleState(scaleStateController.scaleState, scaleBoundaries); markNeedsScaleRecalc = false; scale = newScale; return newScale; @@ -97,12 +79,7 @@ mixin PhotoViewControllerDelegate on State { set scale(double scale) => controller.setScaleInvisibly(scale); - void updateMultiple({ - Offset? position, - double? scale, - double? rotation, - Offset? rotationFocusPoint, - }) { + void updateMultiple({Offset? position, double? scale, double? rotation, Offset? rotationFocusPoint}) { controller.updateMultiple( position: position, scale: scale, @@ -133,15 +110,11 @@ mixin PhotoViewControllerDelegate on State { void nextScaleState() { final PhotoViewScaleState scaleState = scaleStateController.scaleState; - if (scaleState == PhotoViewScaleState.zoomedIn || - scaleState == PhotoViewScaleState.zoomedOut) { + if (scaleState == PhotoViewScaleState.zoomedIn || scaleState == PhotoViewScaleState.zoomedOut) { scaleStateController.scaleState = scaleStateCycle(scaleState); return; } - final double originalScale = getScaleForScaleState( - scaleState, - scaleBoundaries, - ); + final double originalScale = getScaleForScaleState(scaleState, scaleBoundaries); double prevScale = originalScale; PhotoViewScaleState prevScaleState = scaleState; diff --git a/mobile/lib/widgets/photo_view/src/controller/photo_view_scalestate_controller.dart b/mobile/lib/widgets/photo_view/src/controller/photo_view_scalestate_controller.dart index dea8be1a0f..8d078db2c8 100644 --- a/mobile/lib/widgets/photo_view/src/controller/photo_view_scalestate_controller.dart +++ b/mobile/lib/widgets/photo_view/src/controller/photo_view_scalestate_controller.dart @@ -19,18 +19,16 @@ typedef ScaleStateListener = void Function(double prevScale, double nextScale); /// The updates should be done via [scaleState] setter and the updated listened via [outputScaleStateStream] /// class PhotoViewScaleStateController { - late final IgnorableValueNotifier _scaleStateNotifier = - IgnorableValueNotifier(PhotoViewScaleState.initial) - ..addListener(_scaleStateChangeListener); - final StreamController _outputScaleStateCtrl = - StreamController.broadcast() - ..sink.add(PhotoViewScaleState.initial); + late final IgnorableValueNotifier _scaleStateNotifier = IgnorableValueNotifier( + PhotoViewScaleState.initial, + )..addListener(_scaleStateChangeListener); + final StreamController _outputScaleStateCtrl = StreamController.broadcast() + ..sink.add(PhotoViewScaleState.initial); bool _hasZoomedOutManually = false; /// The output for state/value updates - Stream get outputScaleStateStream => - _outputScaleStateCtrl.stream; + Stream get outputScaleStateStream => _outputScaleStateCtrl.stream; /// The state value before the last change or the initial state if the state has not been changed. PhotoViewScaleState prevScaleState = PhotoViewScaleState.initial; @@ -62,9 +60,7 @@ class PhotoViewScaleStateController { bool get hasChanged => prevScaleState != scaleState; /// Check if is `zoomedIn` & `zoomedOut` - bool get isZooming => - scaleState == PhotoViewScaleState.zoomedIn || - scaleState == PhotoViewScaleState.zoomedOut; + bool get isZooming => scaleState == PhotoViewScaleState.zoomedIn || scaleState == PhotoViewScaleState.zoomedOut; /// Resets the state to the initial value; void reset() { diff --git a/mobile/lib/widgets/photo_view/src/core/photo_view_core.dart b/mobile/lib/widgets/photo_view/src/core/photo_view_core.dart index 6b6e5067c5..944e5ba7e6 100644 --- a/mobile/lib/widgets/photo_view/src/core/photo_view_core.dart +++ b/mobile/lib/widgets/photo_view/src/core/photo_view_core.dart @@ -18,9 +18,7 @@ import 'package:immich_mobile/widgets/photo_view/src/core/photo_view_gesture_det import 'package:immich_mobile/widgets/photo_view/src/core/photo_view_hit_corners.dart'; import 'package:immich_mobile/widgets/photo_view/src/utils/photo_view_utils.dart'; -const _defaultDecoration = BoxDecoration( - color: Color.fromRGBO(0, 0, 0, 1.0), -); +const _defaultDecoration = BoxDecoration(color: Color.fromRGBO(0, 0, 0, 1.0)); /// Internal widget in which controls all animations lifecycle, core responses /// to user gestures, updates to the controller state and mounts the entire PhotoView Layout @@ -77,9 +75,9 @@ class PhotoViewCore extends StatefulWidget { required this.disableGestures, required this.disableScaleGestures, required this.enablePanAlways, - }) : semanticLabel = null, - imageProvider = null, - gaplessPlayback = false; + }) : semanticLabel = null, + imageProvider = null, + gaplessPlayback = false; final Decoration? backgroundDecoration; final ImageProvider? imageProvider; @@ -122,10 +120,7 @@ class PhotoViewCore extends StatefulWidget { } class PhotoViewCoreState extends State - with - TickerProviderStateMixin, - PhotoViewControllerDelegate, - HitCornersDetector { + with TickerProviderStateMixin, PhotoViewControllerDelegate, HitCornersDetector { Offset? _normalizedPosition; double? _scaleBefore; double? _rotationBefore; @@ -136,8 +131,8 @@ class PhotoViewCoreState extends State late final AnimationController _positionAnimationController; Animation? _positionAnimation; - late final AnimationController _rotationAnimationController = - AnimationController(vsync: this)..addListener(handleRotationAnimation); + late final AnimationController _rotationAnimationController = AnimationController(vsync: this) + ..addListener(handleRotationAnimation); Animation? _rotationAnimation; PhotoViewHeroAttributes? get heroAttributes => widget.heroAttributes; @@ -166,10 +161,9 @@ class PhotoViewCoreState extends State } bool _shouldAllowPanRotate() => switch (scaleStateController.scaleState) { - PhotoViewScaleState.zoomedIn => - scaleStateController.hasZoomedOutManually, - _ => true, - }; + PhotoViewScaleState.zoomedIn => scaleStateController.hasZoomedOutManually, + _ => true, + }; void onScaleUpdate(ScaleUpdateDetails details) { final double newScale = _scaleBefore! * details.scale; @@ -182,8 +176,7 @@ class PhotoViewCoreState extends State updateMultiple( scale: newScale, - position: - panEnabled ? delta : clampPosition(position: delta * details.scale), + position: panEnabled ? delta : clampPosition(position: delta * details.scale), rotation: rotationEnabled ? _rotationBefore! + details.rotation : null, rotationFocusPoint: rotationEnabled ? details.focalPoint : null, ); @@ -211,10 +204,7 @@ class PhotoViewCoreState extends State if (s > maxScale) { final double scaleComebackRatio = maxScale / s; animateScale(s, maxScale); - final Offset clampedPosition = clampPosition( - position: p * scaleComebackRatio, - scale: maxScale, - ); + final Offset clampedPosition = clampPosition(position: p * scaleComebackRatio, scale: maxScale); animatePosition(p, clampedPosition); return; } @@ -223,13 +213,7 @@ class PhotoViewCoreState extends State if (s < minScale) { final double scaleComebackRatio = minScale / s; animateScale(s, minScale); - animatePosition( - p, - clampPosition( - position: p * scaleComebackRatio, - scale: minScale, - ), - ); + animatePosition(p, clampPosition(position: p * scaleComebackRatio, scale: minScale)); return; } // get magnitude from gesture velocity @@ -238,10 +222,7 @@ class PhotoViewCoreState extends State // animate velocity only if there is no scale change and a significant magnitude if (_scaleBefore! / s == 1.0 && magnitude >= 400.0) { final Offset direction = details.velocity.pixelsPerSecond / magnitude; - animatePosition( - p, - clampPosition(position: p + direction * 100.0), - ); + animatePosition(p, clampPosition(position: p + direction * 100.0)); } } @@ -253,10 +234,7 @@ class PhotoViewCoreState extends State if (!mounted) { return; } - _scaleAnimation = Tween( - begin: from, - end: to, - ).animate(_scaleAnimationController); + _scaleAnimation = Tween(begin: from, end: to).animate(_scaleAnimationController); _scaleAnimationController ..value = 0.0 ..fling(velocity: 0.4); @@ -266,8 +244,7 @@ class PhotoViewCoreState extends State if (!mounted) { return; } - _positionAnimation = Tween(begin: from, end: to) - .animate(_positionAnimationController); + _positionAnimation = Tween(begin: from, end: to).animate(_positionAnimationController); _positionAnimationController ..value = 0.0 ..fling(velocity: 0.4); @@ -277,8 +254,7 @@ class PhotoViewCoreState extends State if (!mounted) { return; } - _rotationAnimation = Tween(begin: from, end: to) - .animate(_rotationAnimationController); + _rotationAnimation = Tween(begin: from, end: to).animate(_rotationAnimationController); _rotationAnimationController ..value = 0.0 ..fling(velocity: 0.4); @@ -292,8 +268,7 @@ class PhotoViewCoreState extends State /// Check if scale is equal to initial after scale animation update void onAnimationStatusCompleted() { - if (scaleStateController.scaleState != PhotoViewScaleState.initial && - scale == scaleBoundaries.initialScale) { + if (scaleStateController.scaleState != PhotoViewScaleState.initial && scale == scaleBoundaries.initialScale) { scaleStateController.setInvisibly(PhotoViewScaleState.initial); } } @@ -326,8 +301,7 @@ class PhotoViewCoreState extends State _scaleAnimationController = AnimationController(vsync: this) ..addListener(handleScaleAnimation) ..addStatusListener(onAnimationStatus); - _positionAnimationController = AnimationController(vsync: this) - ..addListener(handlePositionAnimate); + _positionAnimationController = AnimationController(vsync: this)..addListener(handlePositionAnimate); } void animateOnScaleStateUpdate(double prevScale, double nextScale) { @@ -364,10 +338,7 @@ class PhotoViewCoreState extends State return StreamBuilder( stream: controller.outputStateStream, initialData: controller.prevValue, - builder: ( - BuildContext context, - AsyncSnapshot snapshot, - ) { + builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.hasData) { final PhotoViewControllerValue value = snapshot.data!; final useImageScale = widget.filterQuality != FilterQuality.none; @@ -380,25 +351,15 @@ class PhotoViewCoreState extends State ..rotateZ(value.rotation); final Widget customChildLayout = CustomSingleChildLayout( - delegate: _CenterWithOriginalSizeDelegate( - scaleBoundaries.childSize, - basePosition, - useImageScale, - ), + delegate: _CenterWithOriginalSizeDelegate(scaleBoundaries.childSize, basePosition, useImageScale), child: _buildHero(_buildChild()), ); final child = Container( - constraints: widget.tightMode - ? BoxConstraints.tight(scaleBoundaries.childSize * scale) - : null, + constraints: widget.tightMode ? BoxConstraints.tight(scaleBoundaries.childSize * scale) : null, decoration: widget.backgroundDecoration ?? _defaultDecoration, child: Center( - child: Transform( - transform: matrix, - alignment: basePosition, - child: customChildLayout, - ), + child: Transform(transform: matrix, alignment: basePosition, child: customChildLayout), ), ); @@ -413,31 +374,17 @@ class PhotoViewCoreState extends State onScaleUpdate: widget.disableScaleGestures ? null : onScaleUpdate, onScaleEnd: widget.disableScaleGestures ? null : onScaleEnd, onDragStart: widget.onDragStart != null - ? (details) => widget.onDragStart!( - context, - details, - widget.controller, - widget.scaleStateController, - ) + ? (details) => widget.onDragStart!(context, details, widget.controller, widget.scaleStateController) : null, onDragEnd: widget.onDragEnd != null - ? (details) => - widget.onDragEnd!(context, details, widget.controller.value) + ? (details) => widget.onDragEnd!(context, details, widget.controller.value) : null, onDragUpdate: widget.onDragUpdate != null - ? (details) => widget.onDragUpdate!( - context, - details, - widget.controller.value, - ) + ? (details) => widget.onDragUpdate!(context, details, widget.controller.value) : null, hitDetector: this, - onTapUp: widget.onTapUp != null - ? (details) => widget.onTapUp!(context, details, value) - : null, - onTapDown: widget.onTapDown != null - ? (details) => widget.onTapDown!(context, details, value) - : null, + onTapUp: widget.onTapUp != null ? (details) => widget.onTapUp!(context, details, value) : null, + onTapDown: widget.onTapDown != null ? (details) => widget.onTapDown!(context, details, value) : null, onLongPressStart: widget.onLongPressStart != null ? (details) => widget.onLongPressStart!(context, details, value) : null, @@ -467,9 +414,7 @@ class PhotoViewCoreState extends State return widget.hasCustomChild ? widget.customChild! : Image( - key: widget.heroAttributes?.tag != null - ? ObjectKey(widget.heroAttributes!.tag) - : null, + key: widget.heroAttributes?.tag != null ? ObjectKey(widget.heroAttributes!.tag) : null, image: widget.imageProvider!, semanticLabel: widget.semanticLabel, gaplessPlayback: widget.gaplessPlayback ?? false, @@ -481,11 +426,7 @@ class PhotoViewCoreState extends State } class _CenterWithOriginalSizeDelegate extends SingleChildLayoutDelegate { - const _CenterWithOriginalSizeDelegate( - this.subjectSize, - this.basePosition, - this.useImageScale, - ); + const _CenterWithOriginalSizeDelegate(this.subjectSize, this.basePosition, this.useImageScale); final Size subjectSize; final Alignment basePosition; @@ -507,9 +448,7 @@ class _CenterWithOriginalSizeDelegate extends SingleChildLayoutDelegate { @override BoxConstraints getConstraintsForChild(BoxConstraints constraints) { - return useImageScale - ? const BoxConstraints() - : BoxConstraints.tight(subjectSize); + return useImageScale ? const BoxConstraints() : BoxConstraints.tight(subjectSize); } @override @@ -527,6 +466,5 @@ class _CenterWithOriginalSizeDelegate extends SingleChildLayoutDelegate { useImageScale == other.useImageScale; @override - int get hashCode => - subjectSize.hashCode ^ basePosition.hashCode ^ useImageScale.hashCode; + int get hashCode => subjectSize.hashCode ^ basePosition.hashCode ^ useImageScale.hashCode; } diff --git a/mobile/lib/widgets/photo_view/src/core/photo_view_gesture_detector.dart b/mobile/lib/widgets/photo_view/src/core/photo_view_gesture_detector.dart index 93fd1526da..7a5406c675 100644 --- a/mobile/lib/widgets/photo_view/src/core/photo_view_gesture_detector.dart +++ b/mobile/lib/widgets/photo_view/src/core/photo_view_gesture_detector.dart @@ -53,12 +53,10 @@ class PhotoViewGestureDetector extends StatelessWidget { final Axis? axis = scope?.axis; final touchSlopFactor = scope?.touchSlopFactor ?? 2; - final Map gestures = - {}; + final Map gestures = {}; if (onTapDown != null || onTapUp != null) { - gestures[TapGestureRecognizer] = - GestureRecognizerFactoryWithHandlers( + gestures[TapGestureRecognizer] = GestureRecognizerFactoryWithHandlers( () => TapGestureRecognizer(debugOwner: this), (TapGestureRecognizer instance) { instance @@ -69,8 +67,7 @@ class PhotoViewGestureDetector extends StatelessWidget { } if (onDragStart != null || onDragEnd != null || onDragUpdate != null) { - gestures[VerticalDragGestureRecognizer] = - GestureRecognizerFactoryWithHandlers( + gestures[VerticalDragGestureRecognizer] = GestureRecognizerFactoryWithHandlers( () => VerticalDragGestureRecognizer(debugOwner: this), (VerticalDragGestureRecognizer instance) { instance @@ -81,16 +78,14 @@ class PhotoViewGestureDetector extends StatelessWidget { ); } - gestures[DoubleTapGestureRecognizer] = - GestureRecognizerFactoryWithHandlers( + gestures[DoubleTapGestureRecognizer] = GestureRecognizerFactoryWithHandlers( () => DoubleTapGestureRecognizer(debugOwner: this), (DoubleTapGestureRecognizer instance) { instance.onDoubleTap = onDoubleTap; }, ); - gestures[PhotoViewGestureRecognizer] = - GestureRecognizerFactoryWithHandlers( + gestures[PhotoViewGestureRecognizer] = GestureRecognizerFactoryWithHandlers( () => PhotoViewGestureRecognizer( hitDetector: hitDetector, debugOwner: this, @@ -107,18 +102,14 @@ class PhotoViewGestureDetector extends StatelessWidget { }, ); - gestures[LongPressGestureRecognizer] = - GestureRecognizerFactoryWithHandlers( - () => LongPressGestureRecognizer(debugOwner: this), - (LongPressGestureRecognizer instance) { - instance.onLongPressStart = onLongPressStart; - }); - - return RawGestureDetector( - behavior: behavior, - gestures: gestures, - child: child, + gestures[LongPressGestureRecognizer] = GestureRecognizerFactoryWithHandlers( + () => LongPressGestureRecognizer(debugOwner: this), + (LongPressGestureRecognizer instance) { + instance.onLongPressStart = onLongPressStart; + }, ); + + return RawGestureDetector(behavior: behavior, gestures: gestures, child: child); } } @@ -198,16 +189,14 @@ class PhotoViewGestureRecognizer extends ScaleGestureRecognizer { for (final int pointer in _pointerLocations.keys) { focalPoint += _pointerLocations[pointer]!; } - _currentFocalPoint = - count > 0 ? focalPoint / count.toDouble() : Offset.zero; + _currentFocalPoint = count > 0 ? focalPoint / count.toDouble() : Offset.zero; // Span is the average deviation from focal point. Horizontal and vertical // spans are the average deviations from the focal point's horizontal and // vertical coordinates, respectively. double totalDeviation = 0.0; for (final int pointer in _pointerLocations.keys) { - totalDeviation += - (_currentFocalPoint! - _pointerLocations[pointer]!).distance; + totalDeviation += (_currentFocalPoint! - _pointerLocations[pointer]!).distance; } _currentSpan = count > 0 ? totalDeviation / count : 0.0; } @@ -219,15 +208,13 @@ class PhotoViewGestureRecognizer extends ScaleGestureRecognizer { : hitDetector!.shouldMove(move, Axis.horizontal); if (shouldMove || _pointerLocations.keys.length > 1) { final double spanDelta = (_currentSpan! - _initialSpan!).abs(); - final double focalPointDelta = - (_currentFocalPoint! - _initialFocalPoint!).distance; + final double focalPointDelta = (_currentFocalPoint! - _initialFocalPoint!).distance; // warning: do not compare `focalPointDelta` to `kPanSlop` // `ScaleGestureRecognizer` uses `kPanSlop`, but `HorizontalDragGestureRecognizer` uses `kTouchSlop` // and PhotoView recognizer may compete with the `HorizontalDragGestureRecognizer` from a containing `PageView` // setting `touchSlopFactor` to 2 restores default `ScaleGestureRecognizer` behaviour as `kPanSlop = kTouchSlop * 2.0` // setting `touchSlopFactor` in [0, 1] will allow this recognizer to accept the gesture before the one from `PageView` - if (spanDelta > kScaleSlop || - focalPointDelta > kTouchSlop * touchSlopFactor) { + if (spanDelta > kScaleSlop || focalPointDelta > kTouchSlop * touchSlopFactor) { acceptGesture(event.pointer); } } @@ -252,12 +239,7 @@ class PhotoViewGestureRecognizer extends ScaleGestureRecognizer { /// ); /// ``` class PhotoViewGestureDetectorScope extends InheritedWidget { - const PhotoViewGestureDetectorScope({ - super.key, - this.axis, - this.touchSlopFactor = .2, - required super.child, - }); + const PhotoViewGestureDetectorScope({super.key, this.axis, this.touchSlopFactor = .2, required super.child}); static PhotoViewGestureDetectorScope? of(BuildContext context) { final PhotoViewGestureDetectorScope? scope = context @@ -275,8 +257,7 @@ class PhotoViewGestureDetectorScope extends InheritedWidget { @override bool updateShouldNotify(PhotoViewGestureDetectorScope oldWidget) { - return axis != oldWidget.axis && - touchSlopFactor != oldWidget.touchSlopFactor; + return axis != oldWidget.axis && touchSlopFactor != oldWidget.touchSlopFactor; } } @@ -285,10 +266,7 @@ class PhotoViewGestureDetectorScope extends InheritedWidget { // we cannot change that, but we can prevent the scrollable from panning until this threshold is reached // and let other recognizers accept the gesture instead class PhotoViewPageViewScrollPhysics extends ScrollPhysics { - const PhotoViewPageViewScrollPhysics({ - this.touchSlopFactor = 0.1, - super.parent, - }); + const PhotoViewPageViewScrollPhysics({this.touchSlopFactor = 0.1, super.parent}); // in [0, 1] // 0: most reactive but will not let PhotoView recognizers accept gestures @@ -297,10 +275,7 @@ class PhotoViewPageViewScrollPhysics extends ScrollPhysics { @override PhotoViewPageViewScrollPhysics applyTo(ScrollPhysics? ancestor) { - return PhotoViewPageViewScrollPhysics( - touchSlopFactor: touchSlopFactor, - parent: buildParent(ancestor), - ); + return PhotoViewPageViewScrollPhysics(touchSlopFactor: touchSlopFactor, parent: buildParent(ancestor)); } @override diff --git a/mobile/lib/widgets/photo_view/src/core/photo_view_hit_corners.dart b/mobile/lib/widgets/photo_view/src/core/photo_view_hit_corners.dart index 768e5d9cc7..eac0cb50ce 100644 --- a/mobile/lib/widgets/photo_view/src/core/photo_view_hit_corners.dart +++ b/mobile/lib/widgets/photo_view/src/core/photo_view_hit_corners.dart @@ -25,18 +25,14 @@ mixin HitCornersDetector on PhotoViewControllerDelegate { return HitCorners(y <= cornersY.min, y >= cornersY.max); } - bool _shouldMoveAxis( - HitCorners hitCorners, - double mainAxisMove, - ) { + bool _shouldMoveAxis(HitCorners hitCorners, double mainAxisMove) { if (mainAxisMove == 0) { return false; } if (!hitCorners.hasHitAny) { return true; } - final axisBlocked = hitCorners.hasHitBoth || - (hitCorners.hasHitMax ? mainAxisMove > 0 : mainAxisMove < 0); + final axisBlocked = hitCorners.hasHitBoth || (hitCorners.hasHitMax ? mainAxisMove > 0 : mainAxisMove < 0); if (axisBlocked) { return false; } diff --git a/mobile/lib/widgets/photo_view/src/photo_view_computed_scale.dart b/mobile/lib/widgets/photo_view/src/photo_view_computed_scale.dart index a01db562c7..52bb8a0a50 100644 --- a/mobile/lib/widgets/photo_view/src/photo_view_computed_scale.dart +++ b/mobile/lib/widgets/photo_view/src/photo_view_computed_scale.dart @@ -27,9 +27,7 @@ class PhotoViewComputedScale { @override bool operator ==(Object other) => identical(this, other) || - other is PhotoViewComputedScale && - runtimeType == other.runtimeType && - _value == other._value; + other is PhotoViewComputedScale && runtimeType == other.runtimeType && _value == other._value; @override int get hashCode => _value.hashCode; diff --git a/mobile/lib/widgets/photo_view/src/photo_view_default_widgets.dart b/mobile/lib/widgets/photo_view/src/photo_view_default_widgets.dart index a843087bad..fac0550c3b 100644 --- a/mobile/lib/widgets/photo_view/src/photo_view_default_widgets.dart +++ b/mobile/lib/widgets/photo_view/src/photo_view_default_widgets.dart @@ -9,13 +9,7 @@ class PhotoViewDefaultError extends StatelessWidget { Widget build(BuildContext context) { return DecoratedBox( decoration: decoration, - child: Center( - child: Icon( - Icons.broken_image, - color: Colors.grey[400], - size: 40.0, - ), - ), + child: Center(child: Icon(Icons.broken_image, color: Colors.grey[400], size: 40.0)), ); } } @@ -29,16 +23,10 @@ class PhotoViewDefaultLoading extends StatelessWidget { Widget build(BuildContext context) { final expectedBytes = event?.expectedTotalBytes; final loadedBytes = event?.cumulativeBytesLoaded; - final value = loadedBytes != null && expectedBytes != null - ? loadedBytes / expectedBytes - : null; + final value = loadedBytes != null && expectedBytes != null ? loadedBytes / expectedBytes : null; return Center( - child: SizedBox( - width: 20.0, - height: 20.0, - child: CircularProgressIndicator(value: value), - ), + child: SizedBox(width: 20.0, height: 20.0, child: CircularProgressIndicator(value: value)), ); } } diff --git a/mobile/lib/widgets/photo_view/src/photo_view_scale_state.dart b/mobile/lib/widgets/photo_view/src/photo_view_scale_state.dart index fc6d4db3f9..0d1d4715e8 100644 --- a/mobile/lib/widgets/photo_view/src/photo_view_scale_state.dart +++ b/mobile/lib/widgets/photo_view/src/photo_view_scale_state.dart @@ -6,7 +6,5 @@ enum PhotoViewScaleState { zoomedIn, zoomedOut; - bool get isScaleStateZooming => - this == PhotoViewScaleState.zoomedIn || - this == PhotoViewScaleState.zoomedOut; + bool get isScaleStateZooming => this == PhotoViewScaleState.zoomedIn || this == PhotoViewScaleState.zoomedOut; } diff --git a/mobile/lib/widgets/photo_view/src/photo_view_wrappers.dart b/mobile/lib/widgets/photo_view/src/photo_view_wrappers.dart index d4afe85d2b..4f283e55fd 100644 --- a/mobile/lib/widgets/photo_view/src/photo_view_wrappers.dart +++ b/mobile/lib/widgets/photo_view/src/photo_view_wrappers.dart @@ -109,9 +109,7 @@ class _ImageWrapperState extends State { // retrieve image from the provider void _resolveImage() { - final ImageStream newStream = widget.imageProvider.resolve( - const ImageConfiguration(), - ); + final ImageStream newStream = widget.imageProvider.resolve(const ImageConfiguration()); _updateSourceStream(newStream); } @@ -125,10 +123,7 @@ class _ImageWrapperState extends State { void handleImageFrame(ImageInfo info, bool synchronousCall) { setupCB() { - _imageSize = Size( - info.image.width.toDouble(), - info.image.height.toDouble(), - ); + _imageSize = Size(info.image.width.toDouble(), info.image.height.toDouble()); _loading = false; _imageInfo = _imageInfo; @@ -154,11 +149,7 @@ class _ImageWrapperState extends State { }()); } - _imageStreamListener = ImageStreamListener( - handleImageFrame, - onChunk: handleImageChunk, - onError: handleError, - ); + _imageStreamListener = ImageStreamListener(handleImageFrame, onChunk: handleImageChunk, onError: handleError); return _imageStreamListener!; } @@ -227,20 +218,14 @@ class _ImageWrapperState extends State { return widget.loadingBuilder!(context, _loadingProgress, widget.index); } - return PhotoViewDefaultLoading( - event: _loadingProgress, - ); + return PhotoViewDefaultLoading(event: _loadingProgress); } - Widget _buildError( - BuildContext context, - ) { + Widget _buildError(BuildContext context) { if (widget.errorBuilder != null) { return widget.errorBuilder!(context, _lastException!, _lastStack); } - return PhotoViewDefaultError( - decoration: widget.backgroundDecoration, - ); + return PhotoViewDefaultError(decoration: widget.backgroundDecoration); } } diff --git a/mobile/lib/widgets/photo_view/src/utils/ignorable_change_notifier.dart b/mobile/lib/widgets/photo_view/src/utils/ignorable_change_notifier.dart index d061b7b76c..3ca31cb8f9 100644 --- a/mobile/lib/widgets/photo_view/src/utils/ignorable_change_notifier.dart +++ b/mobile/lib/widgets/photo_view/src/utils/ignorable_change_notifier.dart @@ -8,8 +8,7 @@ import 'package:flutter/foundation.dart'; /// The common collection of listeners inherited from [ChangeNotifier] will be fired /// every time. class IgnorableChangeNotifier extends ChangeNotifier { - ObserverList? _ignorableListeners = - ObserverList(); + ObserverList? _ignorableListeners = ObserverList(); bool _debugAssertNotDisposed() { assert(() { @@ -51,8 +50,7 @@ class IgnorableChangeNotifier extends ChangeNotifier { void notifyListeners() { super.notifyListeners(); if (_ignorableListeners != null) { - final List localListeners = - List.from(_ignorableListeners!); + final List localListeners = List.from(_ignorableListeners!); for (VoidCallback listener in localListeners) { try { if (_ignorableListeners!.contains(listener)) { @@ -60,11 +58,7 @@ class IgnorableChangeNotifier extends ChangeNotifier { } } catch (exception, stack) { FlutterError.reportError( - FlutterErrorDetails( - exception: exception, - stack: stack, - library: 'Photoview library', - ), + FlutterErrorDetails(exception: exception, stack: stack, library: 'Photoview library'), ); } } @@ -80,8 +74,7 @@ class IgnorableChangeNotifier extends ChangeNotifier { /// Just like [ValueNotifier] except it extends [IgnorableChangeNotifier] which has /// listeners that wont fire when [updateIgnoring] is called. -class IgnorableValueNotifier extends IgnorableChangeNotifier - implements ValueListenable { +class IgnorableValueNotifier extends IgnorableChangeNotifier implements ValueListenable { IgnorableValueNotifier(this._value); @override diff --git a/mobile/lib/widgets/photo_view/src/utils/photo_view_utils.dart b/mobile/lib/widgets/photo_view/src/utils/photo_view_utils.dart index facd701725..d120955250 100644 --- a/mobile/lib/widgets/photo_view/src/utils/photo_view_utils.dart +++ b/mobile/lib/widgets/photo_view/src/utils/photo_view_utils.dart @@ -5,22 +5,15 @@ import "package:immich_mobile/widgets/photo_view/src/photo_view_computed_scale.d import 'package:immich_mobile/widgets/photo_view/src/photo_view_scale_state.dart'; /// Given a [PhotoViewScaleState], returns a scale value considering [scaleBoundaries]. -double getScaleForScaleState( - PhotoViewScaleState scaleState, - ScaleBoundaries scaleBoundaries, -) { +double getScaleForScaleState(PhotoViewScaleState scaleState, ScaleBoundaries scaleBoundaries) { return switch (scaleState) { PhotoViewScaleState.initial || PhotoViewScaleState.zoomedIn || - PhotoViewScaleState.zoomedOut => - _clampSize(scaleBoundaries.initialScale, scaleBoundaries), + PhotoViewScaleState.zoomedOut => _clampSize(scaleBoundaries.initialScale, scaleBoundaries), PhotoViewScaleState.covering => _clampSize( - _scaleForCovering( - scaleBoundaries.outerSize, - scaleBoundaries.childSize, - ), - scaleBoundaries, - ), + _scaleForCovering(scaleBoundaries.outerSize, scaleBoundaries.childSize), + scaleBoundaries, + ), PhotoViewScaleState.originalSize => _clampSize(1.0, scaleBoundaries), }; } @@ -28,13 +21,7 @@ double getScaleForScaleState( /// Internal class to wraps custom scale boundaries (min, max and initial) /// Also, stores values regarding the two sizes: the container and the child. class ScaleBoundaries { - const ScaleBoundaries( - this._minScale, - this._maxScale, - this._initialScale, - this.outerSize, - this.childSize, - ); + const ScaleBoundaries(this._minScale, this._maxScale, this._initialScale, this.outerSize, this.childSize); final dynamic _minScale; final dynamic _maxScale; @@ -101,11 +88,7 @@ class ScaleBoundaries { @override int get hashCode => - _minScale.hashCode ^ - _maxScale.hashCode ^ - _initialScale.hashCode ^ - outerSize.hashCode ^ - childSize.hashCode; + _minScale.hashCode ^ _maxScale.hashCode ^ _initialScale.hashCode ^ outerSize.hashCode ^ childSize.hashCode; } double _scaleForContained(Size size, Size childSize) { diff --git a/mobile/lib/widgets/search/curated_people_row.dart b/mobile/lib/widgets/search/curated_people_row.dart index 10c19c7e60..74fc3e1c34 100644 --- a/mobile/lib/widgets/search/curated_people_row.dart +++ b/mobile/lib/widgets/search/curated_people_row.dart @@ -15,13 +15,7 @@ class CuratedPeopleRow extends StatelessWidget { final Function(SearchCuratedContent, int)? onTap; final Function(SearchCuratedContent, int)? onNameTap; - const CuratedPeopleRow({ - super.key, - required this.content, - this.onTap, - this.padding, - required this.onNameTap, - }); + const CuratedPeopleRow({super.key, required this.content, this.onTap, this.padding, required this.onNameTap}); @override Widget build(BuildContext context) { @@ -50,19 +44,13 @@ class CuratedPeopleRow extends StatelessWidget { elevation: 3, child: CircleAvatar( maxRadius: imageSize / 2, - backgroundImage: NetworkImage( - getFaceThumbnailUrl(person.id), - headers: headers, - ), + backgroundImage: NetworkImage(getFaceThumbnailUrl(person.id), headers: headers), ), ), ), ), const SizedBox(height: 8), - SizedBox( - width: imageSize, - child: _buildPersonLabel(context, person, index), - ), + SizedBox(width: imageSize, child: _buildPersonLabel(context, person, index)), ], ), ); @@ -72,19 +60,13 @@ class CuratedPeopleRow extends StatelessWidget { ); } - Widget _buildPersonLabel( - BuildContext context, - SearchCuratedContent person, - int index, - ) { + Widget _buildPersonLabel(BuildContext context, SearchCuratedContent person, int index) { if (person.label.isEmpty) { return GestureDetector( onTap: () => onNameTap?.call(person, index), child: Text( "exif_bottom_sheet_person_add_person", - style: context.textTheme.labelLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor), maxLines: 2, overflow: TextOverflow.ellipsis, textAlign: TextAlign.center, @@ -101,11 +83,7 @@ class CuratedPeopleRow extends StatelessWidget { style: context.textTheme.labelLarge, maxLines: 2, ), - if (person.subtitle != null) - Text( - person.subtitle!, - textAlign: TextAlign.center, - ), + if (person.subtitle != null) Text(person.subtitle!, textAlign: TextAlign.center), ], ); } diff --git a/mobile/lib/widgets/search/curated_places_row.dart b/mobile/lib/widgets/search/curated_places_row.dart index 502b09bc4b..9d21292bde 100644 --- a/mobile/lib/widgets/search/curated_places_row.dart +++ b/mobile/lib/widgets/search/curated_places_row.dart @@ -31,9 +31,7 @@ class CuratedPlacesRow extends StatelessWidget { height: imageSize, child: ListView.separated( scrollDirection: Axis.horizontal, - padding: const EdgeInsets.symmetric( - horizontal: 16, - ), + padding: const EdgeInsets.symmetric(horizontal: 16), separatorBuilder: (context, index) => const SizedBox(width: 10), itemBuilder: (context, index) { // Injecting Map thumbnail as the first element @@ -45,8 +43,7 @@ class CuratedPlacesRow extends StatelessWidget { } final actualIndex = index - actualContentIndex; final object = content[actualIndex]; - final thumbnailRequestUrl = - '${Store.get(StoreKey.serverEndpoint)}/assets/${object.id}/thumbnail'; + final thumbnailRequestUrl = '${Store.get(StoreKey.serverEndpoint)}/assets/${object.id}/thumbnail'; return SizedBox.square( dimension: imageSize, child: ThumbnailWithInfo( diff --git a/mobile/lib/widgets/search/explore_grid.dart b/mobile/lib/widgets/search/explore_grid.dart index 1841f7f051..a6e1cf5aac 100644 --- a/mobile/lib/widgets/search/explore_grid.dart +++ b/mobile/lib/widgets/search/explore_grid.dart @@ -13,11 +13,7 @@ class ExploreGrid extends StatelessWidget { final List curatedContent; final bool isPeople; - const ExploreGrid({ - super.key, - required this.curatedContent, - this.isPeople = false, - }); + const ExploreGrid({super.key, required this.curatedContent, this.isPeople = false}); @override Widget build(BuildContext context) { @@ -27,10 +23,7 @@ class ExploreGrid extends StatelessWidget { child: SizedBox( height: 100, width: 100, - child: ThumbnailWithInfo( - textInfo: '', - onTap: () {}, - ), + child: ThumbnailWithInfo(textInfo: '', onTap: () {}), ), ); } @@ -53,26 +46,15 @@ class ExploreGrid extends StatelessWidget { borderRadius: 0, onTap: () { isPeople - ? context.pushRoute( - PersonResultRoute( - personId: content.id, - personName: content.label, - ), - ) + ? context.pushRoute(PersonResultRoute(personId: content.id, personName: content.label)) : context.pushRoute( SearchRoute( prefilter: SearchFilter( people: {}, - location: SearchLocationFilter( - city: content.label, - ), + location: SearchLocationFilter(city: content.label), camera: SearchCameraFilter(), date: SearchDateFilter(), - display: SearchDisplayFilters( - isNotInAlbum: false, - isArchive: false, - isFavorite: false, - ), + display: SearchDisplayFilters(isNotInAlbum: false, isArchive: false, isFavorite: false), mediaType: AssetType.other, ), ), diff --git a/mobile/lib/widgets/search/person_name_edit_form.dart b/mobile/lib/widgets/search/person_name_edit_form.dart index 886f17b2cc..d95d7c7483 100644 --- a/mobile/lib/widgets/search/person_name_edit_form.dart +++ b/mobile/lib/widgets/search/person_name_edit_form.dart @@ -16,11 +16,7 @@ class PersonNameEditForm extends HookConsumerWidget { final String personId; final String personName; - const PersonNameEditForm({ - super.key, - required this.personId, - required this.personName, - }); + const PersonNameEditForm({super.key, required this.personId, required this.personName}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -28,10 +24,7 @@ class PersonNameEditForm extends HookConsumerWidget { final isError = useState(false); return AlertDialog( - title: const Text( - "add_a_name", - style: TextStyle(fontWeight: FontWeight.bold), - ).tr(), + title: const Text("add_a_name", style: TextStyle(fontWeight: FontWeight.bold)).tr(), content: SingleChildScrollView( child: TextFormField( controller: controller, @@ -46,23 +39,16 @@ class PersonNameEditForm extends HookConsumerWidget { ), actions: [ TextButton( - onPressed: () => context.pop( - const PersonNameEditFormResult(false, ''), - ), + onPressed: () => context.pop(const PersonNameEditFormResult(false, '')), child: Text( "cancel", - style: TextStyle( - color: Colors.red[300], - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: Colors.red[300], fontWeight: FontWeight.bold), ).tr(), ), TextButton( onPressed: () async { isError.value = false; - final result = await ref.read( - updatePersonNameProvider(personId, controller.text).future, - ); + final result = await ref.read(updatePersonNameProvider(personId, controller.text).future); isError.value = !result; if (result) { context.pop(PersonNameEditFormResult(true, controller.text)); @@ -70,10 +56,7 @@ class PersonNameEditForm extends HookConsumerWidget { }, child: Text( "save", - style: TextStyle( - color: context.primaryColor, - fontWeight: FontWeight.bold, - ), + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold), ).tr(), ), ], diff --git a/mobile/lib/widgets/search/search_filter/camera_picker.dart b/mobile/lib/widgets/search/search_filter/camera_picker.dart index a7c0bb89af..a5204c2fbc 100644 --- a/mobile/lib/widgets/search/search_filter/camera_picker.dart +++ b/mobile/lib/widgets/search/search_filter/camera_picker.dart @@ -21,30 +21,14 @@ class CameraPicker extends HookConsumerWidget { final selectedMake = useState(filter?.make); final selectedModel = useState(filter?.model); - final make = ref.watch( - getSearchSuggestionsProvider( - SearchSuggestionType.cameraMake, - ), - ); + final make = ref.watch(getSearchSuggestionsProvider(SearchSuggestionType.cameraMake)); - final models = ref.watch( - getSearchSuggestionsProvider( - SearchSuggestionType.cameraModel, - make: selectedMake.value, - ), - ); + final models = ref.watch(getSearchSuggestionsProvider(SearchSuggestionType.cameraModel, make: selectedMake.value)); final makeWidget = SearchDropdown( dropdownMenuEntries: switch (make) { AsyncError() => [], - AsyncData(:final value) => value - .map( - (e) => DropdownMenuEntry( - value: e, - label: e, - ), - ) - .toList(), + AsyncData(:final value) => value.map((e) => DropdownMenuEntry(value: e, label: e)).toList(), _ => [], }, label: const Text('make').tr(), @@ -56,24 +40,14 @@ class CameraPicker extends HookConsumerWidget { } selectedMake.value = value.toString(); modelTextController.value = TextEditingValue.empty; - onSelect({ - 'make': selectedMake.value, - 'model': null, - }); + onSelect({'make': selectedMake.value, 'model': null}); }, ); final modelWidget = SearchDropdown( dropdownMenuEntries: switch (models) { AsyncError() => [], - AsyncData(:final value) => value - .map( - (e) => DropdownMenuEntry( - value: e, - label: e, - ), - ) - .toList(), + AsyncData(:final value) => value.map((e) => DropdownMenuEntry(value: e, label: e)).toList(), _ => [], }, label: const Text('model').tr(), @@ -81,21 +55,12 @@ class CameraPicker extends HookConsumerWidget { leadingIcon: const Icon(Icons.camera), onSelected: (value) { selectedModel.value = value.toString(); - onSelect({ - 'make': selectedMake.value, - 'model': selectedModel.value, - }); + onSelect({'make': selectedMake.value, 'model': selectedModel.value}); }, ); if (context.isMobile) { - return Column( - children: [ - makeWidget, - const SizedBox(height: 8), - modelWidget, - ], - ); + return Column(children: [makeWidget, const SizedBox(height: 8), modelWidget]); } return Row( diff --git a/mobile/lib/widgets/search/search_filter/common/dropdown.dart b/mobile/lib/widgets/search/search_filter/common/dropdown.dart index dd0ec44e45..70cbfd2c15 100644 --- a/mobile/lib/widgets/search/search_filter/common/dropdown.dart +++ b/mobile/lib/widgets/search/search_filter/common/dropdown.dart @@ -20,9 +20,7 @@ class SearchDropdown extends StatelessWidget { Widget build(BuildContext context) { final menuStyle = const MenuStyle( shape: WidgetStatePropertyAll( - RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(15)), - ), + RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(15))), ), ); diff --git a/mobile/lib/widgets/search/search_filter/display_option_picker.dart b/mobile/lib/widgets/search/search_filter/display_option_picker.dart index 5deed5fe1b..a64eab7b71 100644 --- a/mobile/lib/widgets/search/search_filter/display_option_picker.dart +++ b/mobile/lib/widgets/search/search_filter/display_option_picker.dart @@ -3,18 +3,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:immich_mobile/models/search/search_filter.model.dart'; -enum DisplayOption { - notInAlbum, - favorite, - archive, -} +enum DisplayOption { notInAlbum, favorite, archive } class DisplayOptionPicker extends HookWidget { - const DisplayOptionPicker({ - super.key, - required this.onSelect, - this.filter, - }); + const DisplayOptionPicker({super.key, required this.onSelect, this.filter}); final Function(Map) onSelect; final SearchDisplayFilters? filter; @@ -34,10 +26,7 @@ class DisplayOptionPicker extends HookWidget { title: const Text('search_filter_display_option_not_in_album').tr(), value: options.value[DisplayOption.notInAlbum], onChanged: (bool? value) { - options.value = { - ...options.value, - DisplayOption.notInAlbum: value!, - }; + options.value = {...options.value, DisplayOption.notInAlbum: value!}; onSelect(options.value); }, ), @@ -45,10 +34,7 @@ class DisplayOptionPicker extends HookWidget { title: const Text('favorite').tr(), value: options.value[DisplayOption.favorite], onChanged: (value) { - options.value = { - ...options.value, - DisplayOption.favorite: value!, - }; + options.value = {...options.value, DisplayOption.favorite: value!}; onSelect(options.value); }, ), @@ -56,10 +42,7 @@ class DisplayOptionPicker extends HookWidget { title: const Text('archive').tr(), value: options.value[DisplayOption.archive], onChanged: (value) { - options.value = { - ...options.value, - DisplayOption.archive: value!, - }; + options.value = {...options.value, DisplayOption.archive: value!}; onSelect(options.value); }, ), diff --git a/mobile/lib/widgets/search/search_filter/filter_bottom_sheet_scaffold.dart b/mobile/lib/widgets/search/search_filter/filter_bottom_sheet_scaffold.dart index f534b94256..e8226b5b3a 100644 --- a/mobile/lib/widgets/search/search_filter/filter_bottom_sheet_scaffold.dart +++ b/mobile/lib/widgets/search/search_filter/filter_bottom_sheet_scaffold.dart @@ -33,10 +33,7 @@ class FilterBottomSheetScaffold extends StatelessWidget { children: [ Padding( padding: const EdgeInsets.all(16.0), - child: Text( - title, - style: context.textTheme.headlineSmall, - ), + child: Text(title, style: context.textTheme.headlineSmall), ), buildChildWidget(), Padding( diff --git a/mobile/lib/widgets/search/search_filter/location_picker.dart b/mobile/lib/widgets/search/search_filter/location_picker.dart index 499c1e6a50..608183a2f6 100644 --- a/mobile/lib/widgets/search/search_filter/location_picker.dart +++ b/mobile/lib/widgets/search/search_filter/location_picker.dart @@ -15,8 +15,7 @@ class LocationPicker extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final countryTextController = - useTextEditingController(text: filter?.country); + final countryTextController = useTextEditingController(text: filter?.country); final stateTextController = useTextEditingController(text: filter?.state); final cityTextController = useTextEditingController(text: filter?.city); @@ -53,14 +52,7 @@ class LocationPicker extends HookConsumerWidget { SearchDropdown( dropdownMenuEntries: switch (countries) { AsyncError() => [], - AsyncData(:final value) => value - .map( - (e) => DropdownMenuEntry( - value: e, - label: e, - ), - ) - .toList(), + AsyncData(:final value) => value.map((e) => DropdownMenuEntry(value: e, label: e)).toList(), _ => [], }, label: const Text('country').tr(), @@ -72,27 +64,14 @@ class LocationPicker extends HookConsumerWidget { selectedCountry.value = value.toString(); stateTextController.value = TextEditingValue.empty; cityTextController.value = TextEditingValue.empty; - onSelected({ - 'country': selectedCountry.value, - 'state': null, - 'city': null, - }); + onSelected({'country': selectedCountry.value, 'state': null, 'city': null}); }, ), - const SizedBox( - height: 16, - ), + const SizedBox(height: 16), SearchDropdown( dropdownMenuEntries: switch (states) { AsyncError() => [], - AsyncData(:final value) => value - .map( - (e) => DropdownMenuEntry( - value: e, - label: e, - ), - ) - .toList(), + AsyncData(:final value) => value.map((e) => DropdownMenuEntry(value: e, label: e)).toList(), _ => [], }, label: const Text('state').tr(), @@ -103,38 +82,21 @@ class LocationPicker extends HookConsumerWidget { } selectedState.value = value.toString(); cityTextController.value = TextEditingValue.empty; - onSelected({ - 'country': selectedCountry.value, - 'state': selectedState.value, - 'city': null, - }); + onSelected({'country': selectedCountry.value, 'state': selectedState.value, 'city': null}); }, ), - const SizedBox( - height: 16, - ), + const SizedBox(height: 16), SearchDropdown( dropdownMenuEntries: switch (cities) { AsyncError() => [], - AsyncData(:final value) => value - .map( - (e) => DropdownMenuEntry( - value: e, - label: e, - ), - ) - .toList(), + AsyncData(:final value) => value.map((e) => DropdownMenuEntry(value: e, label: e)).toList(), _ => [], }, label: const Text('city').tr(), controller: cityTextController, onSelected: (value) { selectedCity.value = value.toString(); - onSelected({ - 'country': selectedCountry.value, - 'state': selectedState.value, - 'city': selectedCity.value, - }); + onSelected({'country': selectedCountry.value, 'state': selectedState.value, 'city': selectedCity.value}); }, ), ], diff --git a/mobile/lib/widgets/search/search_filter/people_picker.dart b/mobile/lib/widgets/search/search_filter/people_picker.dart index 44d01d274e..b2a7a18c7c 100644 --- a/mobile/lib/widgets/search/search_filter/people_picker.dart +++ b/mobile/lib/widgets/search/search_filter/people_picker.dart @@ -14,8 +14,8 @@ import 'package:immich_mobile/widgets/common/search_field.dart'; class PeoplePicker extends HookConsumerWidget { const PeoplePicker({super.key, required this.onSelect, this.filter}); - final Function(Set) onSelect; - final Set? filter; + final Function(Set) onSelect; + final Set? filter; @override Widget build(BuildContext context, WidgetRef ref) { @@ -24,7 +24,7 @@ class PeoplePicker extends HookConsumerWidget { final searchQuery = useState(''); final people = ref.watch(getAllPeopleProvider); final headers = ApiService.getRequestHeaders(); - final selectedPeople = useState>(filter ?? {}); + final selectedPeople = useState>(filter ?? {}); return Column( children: [ @@ -40,10 +40,7 @@ class PeoplePicker extends HookConsumerWidget { ), Padding( padding: const EdgeInsets.only(left: 16.0, right: 16.0, bottom: 0), - child: Divider( - color: context.colorScheme.surfaceContainerHighest, - thickness: 1, - ), + child: Divider(color: context.colorScheme.surfaceContainerHighest, thickness: 1), ), Expanded( child: people.widgetWhen( @@ -51,20 +48,12 @@ class PeoplePicker extends HookConsumerWidget { return ListView.builder( shrinkWrap: true, itemCount: people - .where( - (person) => person.name - .toLowerCase() - .contains(searchQuery.value.toLowerCase()), - ) + .where((person) => person.name.toLowerCase().contains(searchQuery.value.toLowerCase())) .length, padding: const EdgeInsets.all(8), itemBuilder: (context, index) { final person = people - .where( - (person) => person.name - .toLowerCase() - .contains(searchQuery.value.toLowerCase()), - ) + .where((person) => person.name.toLowerCase().contains(searchQuery.value.toLowerCase())) .toList()[index]; final isSelected = selectedPeople.value.contains(person); @@ -76,9 +65,7 @@ class PeoplePicker extends HookConsumerWidget { style: context.textTheme.bodyLarge?.copyWith( fontSize: 20, fontWeight: FontWeight.w500, - color: isSelected - ? context.colorScheme.onPrimary - : context.colorScheme.onSurface, + color: isSelected ? context.colorScheme.onPrimary : context.colorScheme.onSurface, ), ), leading: SizedBox( @@ -88,10 +75,7 @@ class PeoplePicker extends HookConsumerWidget { elevation: 3, child: CircleAvatar( maxRadius: imageSize / 2, - backgroundImage: NetworkImage( - getFaceThumbnailUrl(person.id), - headers: headers, - ), + backgroundImage: NetworkImage(getFaceThumbnailUrl(person.id), headers: headers), ), ), ), diff --git a/mobile/lib/widgets/search/search_filter/search_filter_chip.dart b/mobile/lib/widgets/search/search_filter/search_filter_chip.dart index c1e628adeb..a72b4668dd 100644 --- a/mobile/lib/widgets/search/search_filter/search_filter_chip.dart +++ b/mobile/lib/widgets/search/search_filter/search_filter_chip.dart @@ -7,13 +7,7 @@ class SearchFilterChip extends StatelessWidget { final Widget? currentFilter; final IconData icon; - const SearchFilterChip({ - super.key, - required this.label, - required this.onTap, - required this.icon, - this.currentFilter, - }); + const SearchFilterChip({super.key, required this.label, required this.onTap, required this.icon, this.currentFilter}); @override Widget build(BuildContext context) { @@ -23,22 +17,10 @@ class SearchFilterChip extends StatelessWidget { child: Card( elevation: 0, color: context.primaryColor.withValues(alpha: .5), - shape: StadiumBorder( - side: BorderSide(color: context.colorScheme.secondaryContainer), - ), + shape: StadiumBorder(side: BorderSide(color: context.colorScheme.secondaryContainer)), child: Padding( - padding: - const EdgeInsets.symmetric(vertical: 2.0, horizontal: 14.0), - child: Row( - children: [ - Icon( - icon, - size: 18, - ), - const SizedBox(width: 4.0), - currentFilter!, - ], - ), + padding: const EdgeInsets.symmetric(vertical: 2.0, horizontal: 14.0), + child: Row(children: [Icon(icon, size: 18), const SizedBox(width: 4.0), currentFilter!]), ), ), ); @@ -47,21 +29,10 @@ class SearchFilterChip extends StatelessWidget { onTap: onTap, child: Card( elevation: 0, - shape: StadiumBorder( - side: BorderSide(color: context.colorScheme.outline.withAlpha(15)), - ), + shape: StadiumBorder(side: BorderSide(color: context.colorScheme.outline.withAlpha(15))), child: Padding( padding: const EdgeInsets.symmetric(vertical: 2.0, horizontal: 14.0), - child: Row( - children: [ - Icon( - icon, - size: 18, - ), - const SizedBox(width: 4.0), - Text(label), - ], - ), + child: Row(children: [Icon(icon, size: 18), const SizedBox(width: 4.0), Text(label)]), ), ), ); diff --git a/mobile/lib/widgets/search/search_map_thumbnail.dart b/mobile/lib/widgets/search/search_map_thumbnail.dart index 78af8f936b..7533e46f1a 100644 --- a/mobile/lib/widgets/search/search_map_thumbnail.dart +++ b/mobile/lib/widgets/search/search_map_thumbnail.dart @@ -7,10 +7,7 @@ import 'package:immich_mobile/widgets/search/thumbnail_with_info_container.dart' import 'package:maplibre_gl/maplibre_gl.dart'; class SearchMapThumbnail extends StatelessWidget { - const SearchMapThumbnail({ - super.key, - this.size = 60.0, - }); + const SearchMapThumbnail({super.key, this.size = 60.0}); final double size; final bool showTitle = true; @@ -23,16 +20,7 @@ class SearchMapThumbnail extends StatelessWidget { context.pushRoute(MapRoute()); }, child: IgnorePointer( - child: MapThumbnail( - zoom: 2, - centre: const LatLng( - 47, - 5, - ), - height: size, - width: size, - showAttribution: false, - ), + child: MapThumbnail(zoom: 2, centre: const LatLng(47, 5), height: size, width: size, showAttribution: false), ), ); } diff --git a/mobile/lib/widgets/search/search_row_section.dart b/mobile/lib/widgets/search/search_row_section.dart index 352c7f6a40..b8584fefef 100644 --- a/mobile/lib/widgets/search/search_row_section.dart +++ b/mobile/lib/widgets/search/search_row_section.dart @@ -25,10 +25,7 @@ class SearchRowSection extends StatelessWidget { children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 16), - child: SearchRowTitle( - onViewAllPressed: onViewAllPressed, - title: title, - ), + child: SearchRowTitle(onViewAllPressed: onViewAllPressed, title: title), ), child, ], diff --git a/mobile/lib/widgets/search/search_row_title.dart b/mobile/lib/widgets/search/search_row_title.dart index 4fa0d1f854..dc0a4ba6cb 100644 --- a/mobile/lib/widgets/search/search_row_title.dart +++ b/mobile/lib/widgets/search/search_row_title.dart @@ -3,11 +3,7 @@ import 'package:flutter/material.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; class SearchRowTitle extends StatelessWidget { - const SearchRowTitle({ - super.key, - required this.onViewAllPressed, - required this.title, - }); + const SearchRowTitle({super.key, required this.onViewAllPressed, required this.title}); final Function() onViewAllPressed; final String title; @@ -17,19 +13,12 @@ class SearchRowTitle extends StatelessWidget { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - title, - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - ), - ), + Text(title, style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500)), TextButton( onPressed: onViewAllPressed, child: Text( 'search_page_view_all_button', - style: context.textTheme.labelLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor), ).tr(), ), ], diff --git a/mobile/lib/widgets/search/thumbnail_with_info.dart b/mobile/lib/widgets/search/thumbnail_with_info.dart index 8722bf8db8..af9460f929 100644 --- a/mobile/lib/widgets/search/thumbnail_with_info.dart +++ b/mobile/lib/widgets/search/thumbnail_with_info.dart @@ -22,8 +22,7 @@ class ThumbnailWithInfo extends StatelessWidget { @override Widget build(BuildContext context) { - var textAndIconColor = - context.isDarkTheme ? Colors.grey[100] : Colors.grey[700]; + var textAndIconColor = context.isDarkTheme ? Colors.grey[100] : Colors.grey[700]; return ThumbnailWithInfoContainer( onTap: onTap, borderRadius: borderRadius, @@ -37,16 +36,10 @@ class ThumbnailWithInfo extends StatelessWidget { fit: BoxFit.cover, imageUrl: imageUrl!, httpHeaders: ApiService.getRequestHeaders(), - errorWidget: (context, url, error) => - const Icon(Icons.image_not_supported_outlined), + errorWidget: (context, url, error) => const Icon(Icons.image_not_supported_outlined), ), ) - : Center( - child: Icon( - noImageIcon ?? Icons.not_listed_location, - color: textAndIconColor, - ), - ), + : Center(child: Icon(noImageIcon ?? Icons.not_listed_location, color: textAndIconColor)), ); } } diff --git a/mobile/lib/widgets/search/thumbnail_with_info_container.dart b/mobile/lib/widgets/search/thumbnail_with_info_container.dart index 1f5f3c2d16..e4b8f69f83 100644 --- a/mobile/lib/widgets/search/thumbnail_with_info_container.dart +++ b/mobile/lib/widgets/search/thumbnail_with_info_container.dart @@ -27,10 +27,7 @@ class ThumbnailWithInfoContainer extends StatelessWidget { decoration: BoxDecoration( borderRadius: BorderRadius.circular(borderRadius), gradient: LinearGradient( - colors: [ - context.colorScheme.surfaceContainer, - context.colorScheme.surfaceContainer.darken(amount: .1), - ], + colors: [context.colorScheme.surfaceContainer, context.colorScheme.surfaceContainer.darken(amount: .1)], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), @@ -43,9 +40,7 @@ class ThumbnailWithInfoContainer extends StatelessWidget { end: FractionalOffset.bottomCenter, colors: [ Colors.transparent, - label == '' - ? Colors.black.withValues(alpha: 0.1) - : Colors.black.withValues(alpha: 0.5), + label == '' ? Colors.black.withValues(alpha: 0.1) : Colors.black.withValues(alpha: 0.5), ], stops: const [0.0, 1.0], ), @@ -53,15 +48,10 @@ class ThumbnailWithInfoContainer extends StatelessWidget { child: child, ), Padding( - padding: const EdgeInsets.symmetric(horizontal: 8) + - const EdgeInsets.only(bottom: 8), + padding: const EdgeInsets.symmetric(horizontal: 8) + const EdgeInsets.only(bottom: 8), child: Text( label, - style: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 14, - ), + style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 14), maxLines: 2, softWrap: false, overflow: TextOverflow.ellipsis, diff --git a/mobile/lib/widgets/settings/advanced_settings.dart b/mobile/lib/widgets/settings/advanced_settings.dart index bd501ffcf7..3f196b840b 100644 --- a/mobile/lib/widgets/settings/advanced_settings.dart +++ b/mobile/lib/widgets/settings/advanced_settings.dart @@ -25,24 +25,16 @@ class AdvancedSettings extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { bool isLoggedIn = ref.read(currentUserProvider) != null; - final advancedTroubleshooting = - useAppSettingsState(AppSettingsEnum.advancedTroubleshooting); - final manageLocalMediaAndroid = - useAppSettingsState(AppSettingsEnum.manageLocalMediaAndroid); + final advancedTroubleshooting = useAppSettingsState(AppSettingsEnum.advancedTroubleshooting); + final manageLocalMediaAndroid = useAppSettingsState(AppSettingsEnum.manageLocalMediaAndroid); final levelId = useAppSettingsState(AppSettingsEnum.logLevel); final preferRemote = useAppSettingsState(AppSettingsEnum.preferRemoteImage); - final allowSelfSignedSSLCert = - useAppSettingsState(AppSettingsEnum.allowSelfSignedSSLCert); - final useAlternatePMFilter = - useAppSettingsState(AppSettingsEnum.photoManagerCustomFilter); + final allowSelfSignedSSLCert = useAppSettingsState(AppSettingsEnum.allowSelfSignedSSLCert); + final useAlternatePMFilter = useAppSettingsState(AppSettingsEnum.photoManagerCustomFilter); final logLevel = Level.LEVELS[levelId.value].name; - useValueChanged( - levelId.value, - (_, __) => - LogService.I.setLogLevel(Level.LEVELS[levelId.value].toLogLevel()), - ); + useValueChanged(levelId.value, (_, __) => LogService.I.setLogLevel(Level.LEVELS[levelId.value].toLogLevel())); Future checkAndroidVersion() async { if (Platform.isAndroid) { @@ -72,9 +64,7 @@ class AdvancedSettings extends HookConsumerWidget { subtitle: "advanced_settings_sync_remote_deletions_subtitle".tr(), onChanged: (value) async { if (value) { - final result = await ref - .read(localFilesManagerRepositoryProvider) - .requestManageMediaPermission(); + final result = await ref.read(localFilesManagerRepositoryProvider).requestManageMediaPermission(); manageLocalMediaAndroid.value = result; } }, @@ -85,8 +75,7 @@ class AdvancedSettings extends HookConsumerWidget { }, ), SettingsSliderListTile( - text: "advanced_settings_log_level_title" - .tr(namedArgs: {'level': logLevel}), + text: "advanced_settings_log_level_title".tr(namedArgs: {'level': logLevel}), valueNotifier: levelId, maxValue: 8, minValue: 1, @@ -111,8 +100,7 @@ class AdvancedSettings extends HookConsumerWidget { SettingsSwitchListTile( valueNotifier: useAlternatePMFilter, title: "advanced_settings_enable_alternate_media_filter_title".tr(), - subtitle: - "advanced_settings_enable_alternate_media_filter_subtitle".tr(), + subtitle: "advanced_settings_enable_alternate_media_filter_subtitle".tr(), ), ]; diff --git a/mobile/lib/widgets/settings/asset_list_settings/asset_list_group_settings.dart b/mobile/lib/widgets/settings/asset_list_settings/asset_list_group_settings.dart index df974fff30..04786bf916 100644 --- a/mobile/lib/widgets/settings/asset_list_settings/asset_list_group_settings.dart +++ b/mobile/lib/widgets/settings/asset_list_settings/asset_list_group_settings.dart @@ -11,9 +11,7 @@ import 'package:immich_mobile/widgets/settings/settings_radio_list_tile.dart'; import 'package:immich_mobile/widgets/settings/settings_sub_title.dart'; class GroupSettings extends HookConsumerWidget { - const GroupSettings({ - super.key, - }); + const GroupSettings({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -21,10 +19,7 @@ class GroupSettings extends HookConsumerWidget { final groupBy = GroupAssetsBy.values[groupByIndex.value]; Future updateAppSettings(GroupAssetsBy groupBy) async { - await ref.watch(appSettingsServiceProvider).setSetting( - AppSettingsEnum.groupAssetsBy, - groupBy.index, - ); + await ref.watch(appSettingsServiceProvider).setSetting(AppSettingsEnum.groupAssetsBy, groupBy.index); ref.invalidate(appSettingsServiceProvider); } @@ -41,18 +36,9 @@ class GroupSettings extends HookConsumerWidget { SettingsSubTitle(title: "asset_list_group_by_sub_title".tr()), SettingsRadioListTile( groups: [ - SettingsRadioGroup( - title: 'asset_list_layout_settings_group_by_month_day'.tr(), - value: GroupAssetsBy.day, - ), - SettingsRadioGroup( - title: 'month'.tr(), - value: GroupAssetsBy.month, - ), - SettingsRadioGroup( - title: 'asset_list_layout_settings_group_automatically'.tr(), - value: GroupAssetsBy.auto, - ), + SettingsRadioGroup(title: 'asset_list_layout_settings_group_by_month_day'.tr(), value: GroupAssetsBy.day), + SettingsRadioGroup(title: 'month'.tr(), value: GroupAssetsBy.month), + SettingsRadioGroup(title: 'asset_list_layout_settings_group_automatically'.tr(), value: GroupAssetsBy.auto), ], groupBy: groupBy, onRadioChanged: changeGroupValue, diff --git a/mobile/lib/widgets/settings/asset_list_settings/asset_list_layout_settings.dart b/mobile/lib/widgets/settings/asset_list_settings/asset_list_layout_settings.dart index 72402c8d55..bcb4a5ec9c 100644 --- a/mobile/lib/widgets/settings/asset_list_settings/asset_list_layout_settings.dart +++ b/mobile/lib/widgets/settings/asset_list_settings/asset_list_layout_settings.dart @@ -9,9 +9,7 @@ import 'package:immich_mobile/widgets/settings/settings_sub_title.dart'; import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; class LayoutSettings extends HookConsumerWidget { - const LayoutSettings({ - super.key, - }); + const LayoutSettings({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -29,8 +27,7 @@ class LayoutSettings extends HookConsumerWidget { ), SettingsSliderListTile( valueNotifier: tilesPerRow, - text: 'theme_setting_asset_list_tiles_per_row_title' - .tr(namedArgs: {'count': "${tilesPerRow.value}"}), + text: 'theme_setting_asset_list_tiles_per_row_title'.tr(namedArgs: {'count': "${tilesPerRow.value}"}), label: "${tilesPerRow.value}", maxValue: 6, minValue: 2, diff --git a/mobile/lib/widgets/settings/asset_list_settings/asset_list_settings.dart b/mobile/lib/widgets/settings/asset_list_settings/asset_list_settings.dart index cd12ea3eb2..9f0ed0aa87 100644 --- a/mobile/lib/widgets/settings/asset_list_settings/asset_list_settings.dart +++ b/mobile/lib/widgets/settings/asset_list_settings/asset_list_settings.dart @@ -10,14 +10,11 @@ import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; import 'asset_list_layout_settings.dart'; class AssetListSettings extends HookConsumerWidget { - const AssetListSettings({ - super.key, - }); + const AssetListSettings({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { - final showStorageIndicator = - useAppSettingsState(AppSettingsEnum.storageIndicator); + final showStorageIndicator = useAppSettingsState(AppSettingsEnum.storageIndicator); final assetListSetting = [ SettingsSwitchListTile( @@ -29,9 +26,6 @@ class AssetListSettings extends HookConsumerWidget { const GroupSettings(), ]; - return SettingsSubPageScaffold( - settings: assetListSetting, - showDivider: true, - ); + return SettingsSubPageScaffold(settings: assetListSetting, showDivider: true); } } diff --git a/mobile/lib/widgets/settings/asset_viewer_settings/asset_viewer_settings.dart b/mobile/lib/widgets/settings/asset_viewer_settings/asset_viewer_settings.dart index 23dca85b6f..5dea38d85e 100644 --- a/mobile/lib/widgets/settings/asset_viewer_settings/asset_viewer_settings.dart +++ b/mobile/lib/widgets/settings/asset_viewer_settings/asset_viewer_settings.dart @@ -4,20 +4,12 @@ import 'package:immich_mobile/widgets/settings/settings_sub_page_scaffold.dart'; import 'video_viewer_settings.dart'; class AssetViewerSettings extends StatelessWidget { - const AssetViewerSettings({ - super.key, - }); + const AssetViewerSettings({super.key}); @override Widget build(BuildContext context) { - final assetViewerSetting = [ - const ImageViewerQualitySetting(), - const VideoViewerSettings(), - ]; + final assetViewerSetting = [const ImageViewerQualitySetting(), const VideoViewerSettings()]; - return SettingsSubPageScaffold( - settings: assetViewerSetting, - showDivider: true, - ); + return SettingsSubPageScaffold(settings: assetViewerSetting, showDivider: true); } } diff --git a/mobile/lib/widgets/settings/asset_viewer_settings/image_viewer_quality_setting.dart b/mobile/lib/widgets/settings/asset_viewer_settings/image_viewer_quality_setting.dart index 444977d9a2..aed88b90b0 100644 --- a/mobile/lib/widgets/settings/asset_viewer_settings/image_viewer_quality_setting.dart +++ b/mobile/lib/widgets/settings/asset_viewer_settings/image_viewer_quality_setting.dart @@ -9,9 +9,7 @@ import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; class ImageViewerQualitySetting extends HookConsumerWidget { - const ImageViewerQualitySetting({ - super.key, - }); + const ImageViewerQualitySetting({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -24,10 +22,7 @@ class ImageViewerQualitySetting extends HookConsumerWidget { SettingsSubTitle(title: "setting_image_viewer_title".tr()), ListTile( contentPadding: const EdgeInsets.symmetric(horizontal: 20), - title: Text( - 'setting_image_viewer_help', - style: context.textTheme.bodyMedium, - ).tr(), + title: Text('setting_image_viewer_help', style: context.textTheme.bodyMedium).tr(), ), SettingsSwitchListTile( valueNotifier: isPreview, diff --git a/mobile/lib/widgets/settings/asset_viewer_settings/video_viewer_settings.dart b/mobile/lib/widgets/settings/asset_viewer_settings/video_viewer_settings.dart index 4534b6ee09..1d8d9812be 100644 --- a/mobile/lib/widgets/settings/asset_viewer_settings/video_viewer_settings.dart +++ b/mobile/lib/widgets/settings/asset_viewer_settings/video_viewer_settings.dart @@ -8,15 +8,12 @@ import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; class VideoViewerSettings extends HookConsumerWidget { - const VideoViewerSettings({ - super.key, - }); + const VideoViewerSettings({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final useLoopVideo = useAppSettingsState(AppSettingsEnum.loopVideo); - final useOriginalVideo = - useAppSettingsState(AppSettingsEnum.loadOriginalVideo); + final useOriginalVideo = useAppSettingsState(AppSettingsEnum.loadOriginalVideo); return Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/mobile/lib/widgets/settings/backup_settings/background_settings.dart b/mobile/lib/widgets/settings/backup_settings/background_settings.dart index 619a410545..038a567dc2 100644 --- a/mobile/lib/widgets/settings/backup_settings/background_settings.dart +++ b/mobile/lib/widgets/settings/backup_settings/background_settings.dart @@ -19,18 +19,12 @@ class BackgroundBackupSettings extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final isBackgroundEnabled = - ref.watch(backupProvider.select((s) => s.backgroundBackup)); + final isBackgroundEnabled = ref.watch(backupProvider.select((s) => s.backgroundBackup)); final iosSettings = ref.watch(iOSBackgroundSettingsProvider); void showErrorToUser(String msg) { final snackBar = SnackBar( - content: Text( - msg.tr(), - style: context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - ), - ), + content: Text(msg.tr(), style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor)), backgroundColor: Colors.red, ); context.scaffoldMessenger.showSnackBar(snackBar); @@ -42,20 +36,14 @@ class BackgroundBackupSettings extends ConsumerWidget { barrierDismissible: false, builder: (BuildContext ctx) { return AlertDialog( - title: const Text( - 'backup_controller_page_background_battery_info_title', - ).tr(), + title: const Text('backup_controller_page_background_battery_info_title').tr(), content: SingleChildScrollView( - child: const Text( - 'backup_controller_page_background_battery_info_message', - ).tr(), + child: const Text('backup_controller_page_background_battery_info_message').tr(), ), actions: [ ElevatedButton( - onPressed: () => launchUrl( - Uri.parse('https://dontkillmyapp.com'), - mode: LaunchMode.externalApplication, - ), + onPressed: () => + launchUrl(Uri.parse('https://dontkillmyapp.com'), mode: LaunchMode.externalApplication), child: const Text( "backup_controller_page_background_battery_info_link", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 12), @@ -80,26 +68,22 @@ class BackgroundBackupSettings extends ConsumerWidget { title: 'backup_controller_page_background_is_off'.tr(), subtileText: 'backup_controller_page_background_description'.tr(), buttonText: 'backup_controller_page_background_turn_on'.tr(), - onButtonTap: () => - ref.read(backupProvider.notifier).configureBackgroundBackup( - enabled: true, - onError: showErrorToUser, - onBatteryInfo: showBatteryOptimizationInfoToUser, - ), + onButtonTap: () => ref + .read(backupProvider.notifier) + .configureBackgroundBackup( + enabled: true, + onError: showErrorToUser, + onBatteryInfo: showBatteryOptimizationInfoToUser, + ), ); } return Column( children: [ if (!Platform.isIOS || iosSettings?.appRefreshEnabled == true) - _BackgroundSettingsEnabled( - onError: showErrorToUser, - onBatteryInfo: showBatteryOptimizationInfoToUser, - ), - if (Platform.isIOS && iosSettings?.appRefreshEnabled != true) - const _IOSBackgroundRefreshDisabled(), - if (Platform.isIOS && iosSettings != null) - IosDebugInfoTile(settings: iosSettings), + _BackgroundSettingsEnabled(onError: showErrorToUser, onBatteryInfo: showBatteryOptimizationInfoToUser), + if (Platform.isIOS && iosSettings?.appRefreshEnabled != true) const _IOSBackgroundRefreshDisabled(), + if (Platform.isIOS && iosSettings != null) IosDebugInfoTile(settings: iosSettings), ], ); } @@ -112,13 +96,9 @@ class _IOSBackgroundRefreshDisabled extends StatelessWidget { Widget build(BuildContext context) { return SettingsButtonListTile( icon: Icons.task_outlined, - title: - 'backup_controller_page_background_app_refresh_disabled_title'.tr(), - subtileText: - 'backup_controller_page_background_app_refresh_disabled_content'.tr(), - buttonText: - 'backup_controller_page_background_app_refresh_enable_button_text' - .tr(), + title: 'backup_controller_page_background_app_refresh_disabled_title'.tr(), + subtileText: 'backup_controller_page_background_app_refresh_disabled_content'.tr(), + buttonText: 'backup_controller_page_background_app_refresh_enable_button_text'.tr(), onButtonTap: () => openAppSettings(), ); } @@ -128,60 +108,53 @@ class _BackgroundSettingsEnabled extends HookConsumerWidget { final void Function(String msg) onError; final void Function() onBatteryInfo; - const _BackgroundSettingsEnabled({ - required this.onError, - required this.onBatteryInfo, - }); + const _BackgroundSettingsEnabled({required this.onError, required this.onBatteryInfo}); @override Widget build(BuildContext context, WidgetRef ref) { - final isWifiRequired = - ref.watch(backupProvider.select((s) => s.backupRequireWifi)); + final isWifiRequired = ref.watch(backupProvider.select((s) => s.backupRequireWifi)); final isWifiRequiredNotifier = useValueNotifier(isWifiRequired); useValueChanged( isWifiRequired, - (_, __) => WidgetsBinding.instance.addPostFrameCallback( - (_) => isWifiRequiredNotifier.value = isWifiRequired, - ), + (_, __) => WidgetsBinding.instance.addPostFrameCallback((_) => isWifiRequiredNotifier.value = isWifiRequired), ); - final isChargingRequired = - ref.watch(backupProvider.select((s) => s.backupRequireCharging)); + final isChargingRequired = ref.watch(backupProvider.select((s) => s.backupRequireCharging)); final isChargingRequiredNotifier = useValueNotifier(isChargingRequired); useValueChanged( isChargingRequired, - (_, __) => WidgetsBinding.instance.addPostFrameCallback( - (_) => isChargingRequiredNotifier.value = isChargingRequired, - ), + (_, __) => + WidgetsBinding.instance.addPostFrameCallback((_) => isChargingRequiredNotifier.value = isChargingRequired), ); int backupDelayToSliderValue(int ms) => switch (ms) { - 5000 => 0, - 30000 => 1, - 120000 => 2, - _ => 3, - }; + 5000 => 0, + 30000 => 1, + 120000 => 2, + _ => 3, + }; - int backupDelayToMilliseconds(int v) => - switch (v) { 0 => 5000, 1 => 30000, 2 => 120000, _ => 600000 }; + int backupDelayToMilliseconds(int v) => switch (v) { + 0 => 5000, + 1 => 30000, + 2 => 120000, + _ => 600000, + }; String formatBackupDelaySliderValue(int v) => switch (v) { - 0 => 'setting_notifications_notify_seconds' - .tr(namedArgs: {'count': '5'}), - 1 => 'setting_notifications_notify_seconds' - .tr(namedArgs: {'count': '30'}), - 2 => 'setting_notifications_notify_minutes' - .tr(namedArgs: {'count': '2'}), - _ => 'setting_notifications_notify_minutes' - .tr(namedArgs: {'count': '10'}), - }; + 0 => 'setting_notifications_notify_seconds'.tr(namedArgs: {'count': '5'}), + 1 => 'setting_notifications_notify_seconds'.tr(namedArgs: {'count': '30'}), + 2 => 'setting_notifications_notify_minutes'.tr(namedArgs: {'count': '2'}), + _ => 'setting_notifications_notify_minutes'.tr(namedArgs: {'count': '10'}), + }; - final backupTriggerDelay = - ref.watch(backupProvider.select((s) => s.backupTriggerDelay)); + final backupTriggerDelay = ref.watch(backupProvider.select((s) => s.backupTriggerDelay)); final triggerDelay = useState(backupDelayToSliderValue(backupTriggerDelay)); useValueChanged( triggerDelay.value, - (_, __) => ref.read(backupProvider.notifier).configureBackgroundBackup( + (_, __) => ref + .read(backupProvider.notifier) + .configureBackgroundBackup( triggerDelay: backupDelayToMilliseconds(triggerDelay.value), onError: onError, onBatteryInfo: onBatteryInfo, @@ -193,43 +166,32 @@ class _BackgroundSettingsEnabled extends HookConsumerWidget { iconColor: context.primaryColor, title: 'backup_controller_page_background_is_on'.tr(), buttonText: 'backup_controller_page_background_turn_off'.tr(), - onButtonTap: () => - ref.read(backupProvider.notifier).configureBackgroundBackup( - enabled: false, - onError: onError, - onBatteryInfo: onBatteryInfo, - ), + onButtonTap: () => ref + .read(backupProvider.notifier) + .configureBackgroundBackup(enabled: false, onError: onError, onBatteryInfo: onBatteryInfo), subtitle: Column( children: [ SettingsSwitchListTile( valueNotifier: isWifiRequiredNotifier, title: 'backup_controller_page_background_wifi'.tr(), icon: Icons.wifi, - onChanged: (enabled) => - ref.read(backupProvider.notifier).configureBackgroundBackup( - requireWifi: enabled, - onError: onError, - onBatteryInfo: onBatteryInfo, - ), + onChanged: (enabled) => ref + .read(backupProvider.notifier) + .configureBackgroundBackup(requireWifi: enabled, onError: onError, onBatteryInfo: onBatteryInfo), ), SettingsSwitchListTile( valueNotifier: isChargingRequiredNotifier, title: 'backup_controller_page_background_charging'.tr(), icon: Icons.charging_station, - onChanged: (enabled) => - ref.read(backupProvider.notifier).configureBackgroundBackup( - requireCharging: enabled, - onError: onError, - onBatteryInfo: onBatteryInfo, - ), + onChanged: (enabled) => ref + .read(backupProvider.notifier) + .configureBackgroundBackup(requireCharging: enabled, onError: onError, onBatteryInfo: onBatteryInfo), ), if (Platform.isAndroid) SettingsSliderListTile( valueNotifier: triggerDelay, text: 'backup_controller_page_background_delay'.tr( - namedArgs: { - 'duration': formatBackupDelaySliderValue(triggerDelay.value), - }, + namedArgs: {'duration': formatBackupDelaySliderValue(triggerDelay.value)}, ), maxValue: 3.0, noDivisons: 3, diff --git a/mobile/lib/widgets/settings/backup_settings/backup_settings.dart b/mobile/lib/widgets/settings/backup_settings/backup_settings.dart index 20f172cb28..50aa57da9f 100644 --- a/mobile/lib/widgets/settings/backup_settings/backup_settings.dart +++ b/mobile/lib/widgets/settings/backup_settings/backup_settings.dart @@ -15,16 +15,12 @@ import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; class BackupSettings extends HookConsumerWidget { - const BackupSettings({ - super.key, - }); + const BackupSettings({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { - final ignoreIcloudAssets = - useAppSettingsState(AppSettingsEnum.ignoreIcloudAssets); - final isAdvancedTroubleshooting = - useAppSettingsState(AppSettingsEnum.advancedTroubleshooting); + final ignoreIcloudAssets = useAppSettingsState(AppSettingsEnum.ignoreIcloudAssets); + final isAdvancedTroubleshooting = useAppSettingsState(AppSettingsEnum.advancedTroubleshooting); final albumSync = useAppSettingsState(AppSettingsEnum.syncAlbums); final isCorruptCheckInProgress = ref.watch(backupVerificationProvider); final isAlbumSyncInProgress = useState(false); @@ -63,36 +59,24 @@ class BackupSettings extends HookConsumerWidget { ], ) : null, - subtileText: !isCorruptCheckInProgress - ? 'check_corrupt_asset_backup_description'.tr() - : null, + subtileText: !isCorruptCheckInProgress ? 'check_corrupt_asset_backup_description'.tr() : null, buttonText: 'check_corrupt_asset_backup_button'.tr(), onButtonTap: !isCorruptCheckInProgress - ? () => ref - .read(backupVerificationProvider.notifier) - .performBackupCheck(context) + ? () => ref.read(backupVerificationProvider.notifier).performBackupCheck(context) : null, ), if (albumSync.value) SettingsButtonListTile( icon: Icons.photo_album_outlined, title: 'sync_albums'.tr(), - subtitle: Text( - "sync_albums_manual_subtitle".tr(), - ), + subtitle: Text("sync_albums_manual_subtitle".tr()), buttonText: 'sync_albums'.tr(), child: isAlbumSyncInProgress.value ? const CircularProgressIndicator() - : ElevatedButton( - onPressed: syncAlbums, - child: Text('sync'.tr()), - ), + : ElevatedButton(onPressed: syncAlbums, child: Text('sync'.tr())), ), ]; - return SettingsSubPageScaffold( - settings: backupSettings, - showDivider: true, - ); + return SettingsSubPageScaffold(settings: backupSettings, showDivider: true); } } diff --git a/mobile/lib/widgets/settings/backup_settings/drift_backup_settings.dart b/mobile/lib/widgets/settings/backup_settings/drift_backup_settings.dart new file mode 100644 index 0000000000..553eb939c2 --- /dev/null +++ b/mobile/lib/widgets/settings/backup_settings/drift_backup_settings.dart @@ -0,0 +1,82 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/providers/app_settings.provider.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; +import 'package:immich_mobile/widgets/settings/settings_sub_page_scaffold.dart'; + +class DriftBackupSettings extends StatelessWidget { + const DriftBackupSettings({super.key}); + + @override + Widget build(BuildContext context) { + return const SettingsSubPageScaffold(settings: [_UseWifiForUploadVideosButton(), _UseWifiForUploadPhotosButton()]); + } +} + +class _UseWifiForUploadVideosButton extends ConsumerWidget { + const _UseWifiForUploadVideosButton(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final valueStream = Store.watch(StoreKey.useWifiForUploadVideos); + + return ListTile( + title: Text( + "videos".t(context: context), + style: context.textTheme.titleMedium?.copyWith(color: context.primaryColor), + ), + subtitle: Text("network_requirement_videos_upload".t(context: context), style: context.textTheme.labelLarge), + trailing: StreamBuilder( + stream: valueStream, + initialData: Store.tryGet(StoreKey.useWifiForUploadVideos) ?? false, + builder: (context, snapshot) { + final value = snapshot.data ?? false; + return Switch( + value: value, + onChanged: (bool newValue) async { + await ref + .read(appSettingsServiceProvider) + .setSetting(AppSettingsEnum.useCellularForUploadVideos, newValue); + }, + ); + }, + ), + ); + } +} + +class _UseWifiForUploadPhotosButton extends ConsumerWidget { + const _UseWifiForUploadPhotosButton(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final valueStream = Store.watch(StoreKey.useWifiForUploadPhotos); + + return ListTile( + title: Text( + "photos".t(context: context), + style: context.textTheme.titleMedium?.copyWith(color: context.primaryColor), + ), + subtitle: Text("network_requirement_photos_upload".t(context: context), style: context.textTheme.labelLarge), + trailing: StreamBuilder( + stream: valueStream, + initialData: Store.tryGet(StoreKey.useWifiForUploadPhotos) ?? false, + builder: (context, snapshot) { + final value = snapshot.data ?? false; + return Switch( + value: value, + onChanged: (bool newValue) async { + await ref + .read(appSettingsServiceProvider) + .setSetting(AppSettingsEnum.useCellularForUploadPhotos, newValue); + }, + ); + }, + ), + ); + } +} diff --git a/mobile/lib/widgets/settings/backup_settings/foreground_settings.dart b/mobile/lib/widgets/settings/backup_settings/foreground_settings.dart index fc3b32b203..a2ff00fe45 100644 --- a/mobile/lib/widgets/settings/backup_settings/foreground_settings.dart +++ b/mobile/lib/widgets/settings/backup_settings/foreground_settings.dart @@ -12,8 +12,7 @@ class ForegroundBackupSettings extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final isAutoBackup = ref.watch(backupProvider.select((s) => s.autoBackup)); - void onButtonTap() => - ref.read(backupProvider.notifier).setAutoBackup(!isAutoBackup); + void onButtonTap() => ref.read(backupProvider.notifier).setAutoBackup(!isAutoBackup); if (isAutoBackup) { return SettingsButtonListTile( diff --git a/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart b/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart new file mode 100644 index 0000000000..8916fdd92b --- /dev/null +++ b/mobile/lib/widgets/settings/beta_sync_settings/beta_sync_settings.dart @@ -0,0 +1,345 @@ +import 'dart:io'; + +import 'package:drift/drift.dart' as drift_db; +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/providers/background_sync.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/memory.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/storage.provider.dart'; +import 'package:immich_mobile/providers/sync_status.provider.dart'; +import 'package:immich_mobile/widgets/settings/beta_sync_settings/entity_count_tile.dart'; +import 'package:path/path.dart' as path; +import 'package:path_provider/path_provider.dart'; +import 'package:share_plus/share_plus.dart'; + +class BetaSyncSettings extends HookConsumerWidget { + const BetaSyncSettings({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final assetService = ref.watch(assetServiceProvider); + final localAlbumService = ref.watch(localAlbumServiceProvider); + final remoteAlbumService = ref.watch(remoteAlbumServiceProvider); + final memoryService = ref.watch(driftMemoryServiceProvider); + + Future> loadCounts() async { + final assetCounts = assetService.getAssetCounts(); + final localAlbumCounts = localAlbumService.getCount(); + final remoteAlbumCounts = remoteAlbumService.getCount(); + final memoryCount = memoryService.getCount(); + final getLocalHashedCount = assetService.getLocalHashedCount(); + + return await Future.wait([assetCounts, localAlbumCounts, remoteAlbumCounts, memoryCount, getLocalHashedCount]); + } + + Future resetDatabase() async { + // https://github.com/simolus3/drift/commit/bd80a46264b6dd833ef4fd87fffc03f5a832ab41#diff-3f879e03b4a35779344ef16170b9353608dd9c42385f5402ec6035aac4dd8a04R76-R94 + final drift = ref.read(driftProvider); + final database = drift.attachedDatabase; + await database.exclusively(() async { + // https://stackoverflow.com/a/65743498/25690041 + await database.customStatement('PRAGMA writable_schema = 1;'); + await database.customStatement('DELETE FROM sqlite_master;'); + await database.customStatement('VACUUM;'); + await database.customStatement('PRAGMA writable_schema = 0;'); + await database.customStatement('PRAGMA integrity_check'); + + await database.customStatement('PRAGMA user_version = 0'); + await database.beforeOpen( + // ignore: invalid_use_of_internal_member + database.resolvedEngine.executor, + drift_db.OpeningDetails(null, database.schemaVersion), + ); + await database.customStatement('PRAGMA user_version = ${database.schemaVersion}'); + + // Refresh all stream queries + database.notifyUpdates({for (final table in database.allTables) drift_db.TableUpdate.onTable(table)}); + }); + } + + Future exportDatabase() async { + try { + // WAL Checkpoint to ensure all changes are written to the database + await ref.read(driftProvider).customStatement("pragma wal_checkpoint(truncate)"); + final documentsDir = await getApplicationDocumentsDirectory(); + final dbFile = File(path.join(documentsDir.path, 'immich.sqlite')); + + if (!await dbFile.exists()) { + if (context.mounted) { + context.scaffoldMessenger.showSnackBar( + SnackBar(content: Text("Database file not found".t(context: context))), + ); + } + return; + } + + final timestamp = DateTime.now().millisecondsSinceEpoch; + final exportFile = File(path.join(documentsDir.path, 'immich_export_$timestamp.sqlite')); + + await dbFile.copy(exportFile.path); + + await Share.shareXFiles([XFile(exportFile.path)], text: 'Immich Database Export'); + + Future.delayed(const Duration(seconds: 30), () async { + if (await exportFile.exists()) { + await exportFile.delete(); + } + }); + + if (context.mounted) { + context.scaffoldMessenger.showSnackBar( + SnackBar(content: Text("Database exported successfully".t(context: context))), + ); + } + } catch (e) { + if (context.mounted) { + context.scaffoldMessenger.showSnackBar( + SnackBar(content: Text("Failed to export database: $e".t(context: context))), + ); + } + } + } + + Future clearFileCache() async { + await ref.read(storageRepositoryProvider).clearCache(); + } + + return FutureBuilder>( + future: loadCounts(), + builder: (context, snapshot) { + if (snapshot.connectionState != ConnectionState.done) { + return const CircularProgressIndicator(); + } + + final assetCounts = snapshot.data![0]! as (int, int); + final localAssetCount = assetCounts.$1; + final remoteAssetCount = assetCounts.$2; + + final localAlbumCount = snapshot.data![1]! as int; + final remoteAlbumCount = snapshot.data![2]! as int; + final memoryCount = snapshot.data![3]! as int; + final localHashedCount = snapshot.data![4]! as int; + + return Padding( + padding: const EdgeInsets.only(top: 16, bottom: 32), + child: ListView( + children: [ + _SectionHeaderText(text: "assets".t(context: context)), + Padding( + padding: const EdgeInsets.fromLTRB(16, 8, 16, 16), + child: Flex( + direction: Axis.horizontal, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + spacing: 8.0, + children: [ + Expanded( + child: EntitiyCountTile( + label: "local".t(context: context), + count: localAssetCount, + icon: Icons.smartphone, + ), + ), + Expanded( + child: EntitiyCountTile( + label: "remote".t(context: context), + count: remoteAssetCount, + icon: Icons.cloud, + ), + ), + ], + ), + ), + _SectionHeaderText(text: "albums".t(context: context)), + Padding( + padding: const EdgeInsets.fromLTRB(16, 8, 16, 16), + child: Flex( + direction: Axis.horizontal, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + spacing: 8.0, + children: [ + Expanded( + child: EntitiyCountTile( + label: "local".t(context: context), + count: localAlbumCount, + icon: Icons.smartphone, + ), + ), + Expanded( + child: EntitiyCountTile( + label: "remote".t(context: context), + count: remoteAlbumCount, + icon: Icons.cloud, + ), + ), + ], + ), + ), + _SectionHeaderText(text: "other".t(context: context)), + Padding( + padding: const EdgeInsets.fromLTRB(16, 8, 16, 16), + child: Flex( + direction: Axis.horizontal, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + spacing: 8.0, + children: [ + Expanded( + child: EntitiyCountTile( + label: "memories".t(context: context), + count: memoryCount, + icon: Icons.calendar_today, + ), + ), + Expanded( + child: EntitiyCountTile( + label: "hashed_assets".t(context: context), + count: localHashedCount, + icon: Icons.tag, + ), + ), + ], + ), + ), + const Divider(height: 1, indent: 16, endIndent: 16), + const SizedBox(height: 24), + _SectionHeaderText(text: "jobs".t(context: context)), + ListTile( + title: Text( + "sync_local".t(context: context), + style: const TextStyle(fontWeight: FontWeight.w500), + ), + subtitle: Text("tap_to_run_job".t(context: context)), + leading: const Icon(Icons.sync), + trailing: _SyncStatusIcon(status: ref.watch(syncStatusProvider).localSyncStatus), + onTap: () { + ref.read(backgroundSyncProvider).syncLocal(full: true); + }, + ), + ListTile( + title: Text( + "sync_remote".t(context: context), + style: const TextStyle(fontWeight: FontWeight.w500), + ), + subtitle: Text("tap_to_run_job".t(context: context)), + leading: const Icon(Icons.cloud_sync), + trailing: _SyncStatusIcon(status: ref.watch(syncStatusProvider).remoteSyncStatus), + onTap: () { + ref.read(backgroundSyncProvider).syncRemote(); + }, + ), + ListTile( + title: Text( + "hash_asset".t(context: context), + style: const TextStyle(fontWeight: FontWeight.w500), + ), + leading: const Icon(Icons.tag), + subtitle: Text("tap_to_run_job".t(context: context)), + trailing: _SyncStatusIcon(status: ref.watch(syncStatusProvider).hashJobStatus), + onTap: () { + ref.read(backgroundSyncProvider).hashAssets(); + }, + ), + const Divider(height: 1, indent: 16, endIndent: 16), + const SizedBox(height: 24), + _SectionHeaderText(text: "actions".t(context: context)), + ListTile( + title: Text( + "clear_file_cache".t(context: context), + style: const TextStyle(fontWeight: FontWeight.w500), + ), + leading: const Icon(Icons.playlist_remove_rounded), + onTap: clearFileCache, + ), + ListTile( + title: Text( + "export_database".t(context: context), + style: const TextStyle(fontWeight: FontWeight.w500), + ), + subtitle: Text("export_database_description".t(context: context)), + leading: const Icon(Icons.download), + onTap: exportDatabase, + ), + 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 { + 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), + ), + ), + ], + ); + }, + ); + }, + ), + ], + ), + ); + }, + ); + } +} + +class _SyncStatusIcon extends StatelessWidget { + final SyncStatus status; + + const _SyncStatusIcon({required this.status}); + + @override + Widget build(BuildContext context) { + return switch (status) { + SyncStatus.idle => const Icon(Icons.pause_circle_outline_rounded), + SyncStatus.syncing => const SizedBox(height: 24, width: 24, child: CircularProgressIndicator(strokeWidth: 2)), + SyncStatus.success => const Icon(Icons.check_circle_outline, color: Colors.green), + SyncStatus.error => Icon(Icons.error_outline, color: context.colorScheme.error), + }; + } +} + +class _SectionHeaderText extends StatelessWidget { + final String text; + + const _SectionHeaderText({required this.text}); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16.0), + child: Text( + text.toUpperCase(), + style: context.textTheme.labelLarge?.copyWith( + fontWeight: FontWeight.w500, + color: context.colorScheme.onSurface.withAlpha(200), + ), + ), + ); + } +} diff --git a/mobile/lib/widgets/settings/beta_sync_settings/entity_count_tile.dart b/mobile/lib/widgets/settings/beta_sync_settings/entity_count_tile.dart new file mode 100644 index 0000000000..ac357c2dee --- /dev/null +++ b/mobile/lib/widgets/settings/beta_sync_settings/entity_count_tile.dart @@ -0,0 +1,73 @@ +import 'package:flutter/material.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/theme_extensions.dart'; + +class EntitiyCountTile extends StatelessWidget { + final int count; + final String label; + final IconData icon; + + const EntitiyCountTile({super.key, required this.count, required this.label, required this.icon}); + + String zeroPadding(int number, int targetWidth) { + final numStr = number.toString(); + return numStr.length < targetWidth ? "0" * (targetWidth - numStr.length) : ""; + } + + int calculateMaxDigits(double availableWidth) { + const double charWidth = 11.0; + return (availableWidth / charWidth).floor().clamp(1, 8); + } + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: context.colorScheme.surfaceContainerLow, + borderRadius: const BorderRadius.all(Radius.circular(16)), + border: Border.all(width: 0.5, color: context.colorScheme.outline.withAlpha(25)), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + // Icon and Label + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Icon(icon, color: context.primaryColor), + const SizedBox(width: 8), + Text( + label, + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold, fontSize: 16), + ), + ], + ), + const SizedBox(height: 12), + // Number + LayoutBuilder( + builder: (context, constraints) { + final maxDigits = calculateMaxDigits(constraints.maxWidth); + return RichText( + text: TextSpan( + style: const TextStyle(fontSize: 18, fontFamily: 'OverpassMono', fontWeight: FontWeight.w600), + children: [ + TextSpan( + text: zeroPadding(count, maxDigits), + style: TextStyle(color: context.colorScheme.onSurfaceSecondary.withAlpha(75)), + ), + TextSpan( + text: count.toString(), + style: TextStyle(color: context.primaryColor), + ), + ], + ), + ); + }, + ), + ], + ), + ); + } +} diff --git a/mobile/lib/widgets/settings/beta_timeline_list_tile.dart b/mobile/lib/widgets/settings/beta_timeline_list_tile.dart index 1de3a6e7ab..3d41094b76 100644 --- a/mobile/lib/widgets/settings/beta_timeline_list_tile.dart +++ b/mobile/lib/widgets/settings/beta_timeline_list_tile.dart @@ -1,26 +1,25 @@ import 'dart:math' as math; import 'package:auto_route/auto_route.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart'; +import 'package:immich_mobile/providers/auth.provider.dart'; +import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; class BetaTimelineListTile extends ConsumerStatefulWidget { - const BetaTimelineListTile({ - super.key, - }); + const BetaTimelineListTile({super.key}); @override - ConsumerState createState() => - _BetaTimelineListTileState(); + ConsumerState createState() => _BetaTimelineListTileState(); } -class _BetaTimelineListTileState extends ConsumerState - with SingleTickerProviderStateMixin { +class _BetaTimelineListTileState extends ConsumerState with SingleTickerProviderStateMixin { late AnimationController _animationController; late Animation _rotationAnimation; late Animation _pulseAnimation; @@ -29,31 +28,22 @@ class _BetaTimelineListTileState extends ConsumerState @override void initState() { super.initState(); - _animationController = AnimationController( - duration: const Duration(seconds: 3), - vsync: this, - ); + _animationController = AnimationController(duration: const Duration(seconds: 3), vsync: this); - _rotationAnimation = Tween(begin: 0, end: 2 * math.pi).animate( - CurvedAnimation( - parent: _animationController, - curve: Curves.linear, - ), - ); + _rotationAnimation = Tween( + begin: 0, + end: 2 * math.pi, + ).animate(CurvedAnimation(parent: _animationController, curve: Curves.linear)); - _pulseAnimation = Tween(begin: 1, end: 1.1).animate( - CurvedAnimation( - parent: _animationController, - curve: Curves.easeInOut, - ), - ); + _pulseAnimation = Tween( + begin: 1, + end: 1.1, + ).animate(CurvedAnimation(parent: _animationController, curve: Curves.easeInOut)); - _gradientAnimation = Tween(begin: 0, end: 1).animate( - CurvedAnimation( - parent: _animationController, - curve: Curves.easeInOut, - ), - ); + _gradientAnimation = Tween( + begin: 0, + end: 1, + ).animate(CurvedAnimation(parent: _animationController, curve: Curves.easeInOut)); _animationController.repeat(reverse: true); } @@ -66,9 +56,13 @@ class _BetaTimelineListTileState extends ConsumerState @override Widget build(BuildContext context) { - final betaTimelineValue = ref - .watch(appSettingsServiceProvider) - .getSetting(AppSettingsEnum.betaTimeline); + final betaTimelineValue = ref.watch(appSettingsServiceProvider).getSetting(AppSettingsEnum.betaTimeline); + final serverInfo = ref.watch(serverInfoProvider); + final auth = ref.watch(authProvider); + + if (!auth.isAuthenticated || (serverInfo.serverVersion.minor < 136 && kReleaseMode)) { + return const SizedBox.shrink(); + } return AnimatedBuilder( animation: _animationController, @@ -78,50 +72,28 @@ class _BetaTimelineListTileState extends ConsumerState context: context, builder: (context) { return AlertDialog( - title: value - ? const Text("Enable Beta Timeline") - : const Text("Disable Beta Timeline"), + title: value ? const Text("Enable Beta Timeline") : const Text("Disable Beta Timeline"), content: value - ? const Text( - "Are you sure you want to enable the beta timeline?", - ) - : const Text( - "Are you sure you want to disable the beta timeline?", - ), + ? const Text("Are you sure you want to enable the beta timeline?") + : const Text("Are you sure you want to disable the beta timeline?"), actions: [ - TextButton( - onPressed: () async { - Navigator.of(context).pop(); - await ref.read(appSettingsServiceProvider).setSetting( - AppSettingsEnum.betaTimeline, - value, - ); - context.router.replaceAll( - [ChangeExperienceRoute(switchingToBeta: value)], - ); - }, - child: Text( - "YES", - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: context.primaryColor, - ), - ), - ), TextButton( onPressed: () { context.pop(); }, child: Text( - "NO", - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: context.colorScheme.outline, - ), + "cancel".t(context: context), + style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500, color: context.colorScheme.outline), ), ), + ElevatedButton( + onPressed: () async { + Navigator.of(context).pop(); + await ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.betaTimeline, value); + context.router.replaceAll([ChangeExperienceRoute(switchingToBeta: value)]); + }, + child: Text("ok".t(context: context)), + ), ], ); }, @@ -135,13 +107,13 @@ class _BetaTimelineListTileState extends ConsumerState _gradientAnimation.value, )!, Color.lerp( - context.primaryColor.withValues(alpha: 0.4), - context.primaryColor.withValues(alpha: 0.6), + context.logoPink.withValues(alpha: 0.2), + context.logoPink.withValues(alpha: 0.4), _gradientAnimation.value, )!, Color.lerp( - context.primaryColor.withValues(alpha: 0.3), - context.primaryColor.withValues(alpha: 0.5), + context.logoRed.withValues(alpha: 0.3), + context.logoRed.withValues(alpha: 0.5), _gradientAnimation.value, )!, ]; @@ -155,31 +127,25 @@ class _BetaTimelineListTileState extends ConsumerState stops: const [0.0, 0.5, 1.0], begin: Alignment.topLeft, end: Alignment.bottomRight, - transform: GradientRotation(_rotationAnimation.value * 0.1), + transform: GradientRotation(_rotationAnimation.value * 0.5), ), boxShadow: [ - BoxShadow( - color: context.primaryColor.withValues(alpha: 0.1), - blurRadius: 8, - offset: const Offset(0, 2), - ), + BoxShadow(color: context.primaryColor.withValues(alpha: 0.1), blurRadius: 8, offset: const Offset(0, 2)), ], ), child: Container( - margin: const EdgeInsets.all(1.5), + margin: const EdgeInsets.all(2), decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(10.5)), color: context.scaffoldBackgroundColor, ), child: Material( - color: Colors.transparent, borderRadius: const BorderRadius.all(Radius.circular(10.5)), child: InkWell( borderRadius: const BorderRadius.all(Radius.circular(10.5)), onTap: () => onSwitchChanged(!betaTimelineValue), child: Padding( - padding: - const EdgeInsets.symmetric(horizontal: 20, vertical: 16), + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16), child: Row( children: [ Transform.scale( @@ -197,15 +163,11 @@ class _BetaTimelineListTileState extends ConsumerState ], ), ), - child: Icon( - Icons.auto_awesome, - color: context.primaryColor, - size: 20, - ), + child: Icon(Icons.auto_awesome, color: context.primaryColor, size: 20), ), ), ), - const SizedBox(width: 16), + const SizedBox(width: 28), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -214,36 +176,24 @@ class _BetaTimelineListTileState extends ConsumerState crossAxisAlignment: CrossAxisAlignment.center, children: [ Text( - "advanced_settings_beta_timeline_title" - .t(context: context), - style: - context.textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.w600, - ), + "advanced_settings_beta_timeline_title".t(context: context), + style: context.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w600), ), const SizedBox(width: 8), Container( - padding: const EdgeInsets.symmetric( - horizontal: 6, - vertical: 2, - ), + padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(8), - ), + borderRadius: const BorderRadius.all(Radius.circular(8)), gradient: LinearGradient( colors: [ - context.primaryColor - .withValues(alpha: 0.8), - context.primaryColor - .withValues(alpha: 0.6), + context.primaryColor.withValues(alpha: 0.8), + context.primaryColor.withValues(alpha: 0.6), ], ), ), child: Text( 'NEW', - style: - context.textTheme.labelSmall?.copyWith( + style: context.textTheme.labelSmall?.copyWith( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 10, @@ -255,12 +205,11 @@ class _BetaTimelineListTileState extends ConsumerState ), const SizedBox(height: 4), Text( - "advanced_settings_beta_timeline_subtitle" - .t(context: context), + "advanced_settings_beta_timeline_subtitle".t(context: context), style: context.textTheme.labelLarge?.copyWith( - color: context.textTheme.labelLarge?.color - ?.withValues(alpha: 0.7), + color: context.textTheme.labelLarge?.color?.withValues(alpha: 0.9), ), + maxLines: 2, ), ], ), diff --git a/mobile/lib/widgets/settings/custom_proxy_headers_settings/custome_proxy_headers_settings.dart b/mobile/lib/widgets/settings/custom_proxy_headers_settings/custome_proxy_headers_settings.dart index 2e1f165602..f0e248b39d 100644 --- a/mobile/lib/widgets/settings/custom_proxy_headers_settings/custome_proxy_headers_settings.dart +++ b/mobile/lib/widgets/settings/custom_proxy_headers_settings/custome_proxy_headers_settings.dart @@ -15,15 +15,11 @@ class CustomeProxyHeaderSettings extends StatelessWidget { dense: true, title: Text( "headers_settings_tile_title".tr(), - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500), ), subtitle: Text( "headers_settings_tile_subtitle".tr(), - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), onTap: () => context.pushRoute(const HeaderSettingsRoute()), ); diff --git a/mobile/lib/widgets/settings/language_settings.dart b/mobile/lib/widgets/settings/language_settings.dart index 4d41d5b19b..d0b3ac0021 100644 --- a/mobile/lib/widgets/settings/language_settings.dart +++ b/mobile/lib/widgets/settings/language_settings.dart @@ -31,13 +31,11 @@ class LanguageSettings extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final localeEntries = useMemoized(() => locales.entries.toList(), const []); final currentLocale = context.locale; - final filteredLocaleEntries = - useState>>(localeEntries); + final filteredLocaleEntries = useState>>(localeEntries); final selectedLocale = useState(currentLocale); final isLoading = useState(false); - final isButtonDisabled = - selectedLocale.value == currentLocale || isLoading.value; + final isButtonDisabled = selectedLocale.value == currentLocale || isLoading.value; final searchController = useTextEditingController(); final searchFocusNode = useFocusNode(); @@ -50,10 +48,7 @@ class LanguageSettings extends HookConsumerWidget { filteredLocaleEntries.value = localeEntries; } else { filteredLocaleEntries.value = localeEntries - .where( - (entry) => - entry.key.toLowerCase().contains(searchTerm.toLowerCase()), - ) + .where((entry) => entry.key.toLowerCase().contains(searchTerm.toLowerCase())) .toList(); } }); @@ -64,17 +59,14 @@ class LanguageSettings extends HookConsumerWidget { onSearch(''); } - useEffect( - () { - void searchListener() => onSearch(searchController.text); - searchController.addListener(searchListener); - return () { - searchController.removeListener(searchListener); - debounceTimer.value?.cancel(); - }; - }, - [searchController], - ); + useEffect(() { + void searchListener() => onSearch(searchController.text); + searchController.addListener(searchListener); + return () { + searchController.removeListener(searchListener); + debounceTimer.value?.cancel(); + }; + }, [searchController]); return SafeArea( child: Column( @@ -94,12 +86,9 @@ class LanguageSettings extends HookConsumerWidget { itemExtent: 64.0, cacheExtent: 100, itemBuilder: (context, index) { - final countryName = - filteredLocaleEntries.value[index].key; - final localeValue = - filteredLocaleEntries.value[index].value; - final bool isSelected = - selectedLocale.value == localeValue; + final countryName = filteredLocaleEntries.value[index].key; + final localeValue = filteredLocaleEntries.value[index].value; + final bool isSelected = selectedLocale.value == localeValue; return _LanguageItem( key: ValueKey(localeValue.toString()), @@ -117,11 +106,7 @@ class LanguageSettings extends HookConsumerWidget { _LanguageApplyButton( isDisabled: isButtonDisabled, isLoading: isLoading.value, - onPressed: () => _applyLanguageChange( - context, - selectedLocale, - isLoading, - ), + onPressed: () => _applyLanguageChange(context, selectedLocale, isLoading), ), ], ), @@ -146,9 +131,7 @@ class _LanguageSearchBar extends StatelessWidget { Widget build(BuildContext context) { return Container( padding: const EdgeInsets.only(top: 16, bottom: 8, left: 50, right: 50), - decoration: BoxDecoration( - color: context.colorScheme.surface, - ), + decoration: BoxDecoration(color: context.colorScheme.surface), child: DecoratedBox( decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(24)), @@ -168,10 +151,7 @@ class _LanguageSearchBar extends StatelessWidget { hintText: 'language_search_hint'.t(context: context), prefixIcon: const Icon(Icons.search_rounded), suffixIcon: controller.text.isNotEmpty - ? IconButton( - icon: const Icon(Icons.clear_rounded), - onPressed: onClear, - ) + ? IconButton(icon: const Icon(Icons.clear_rounded), onPressed: onClear) : null, controller: controller, onChanged: onChanged, @@ -192,24 +172,16 @@ class _LanguageNotFound extends StatelessWidget { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Icon( - Icons.search_off_rounded, - size: 64, - color: context.colorScheme.onSurface.withValues(alpha: 0.4), - ), + Icon(Icons.search_off_rounded, size: 64, color: context.colorScheme.onSurface.withValues(alpha: 0.4)), const SizedBox(height: 8), Text( 'language_no_results_title'.t(context: context), - style: context.textTheme.titleMedium?.copyWith( - color: context.colorScheme.onSurface, - ), + style: context.textTheme.titleMedium?.copyWith(color: context.colorScheme.onSurface), ), const SizedBox(height: 4), Text( 'language_no_results_subtitle'.t(context: context), - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurface.withValues(alpha: 0.8), - ), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurface.withValues(alpha: 0.8)), ), ], ), @@ -218,11 +190,7 @@ class _LanguageNotFound extends StatelessWidget { } class _LanguageApplyButton extends StatelessWidget { - const _LanguageApplyButton({ - required this.isDisabled, - required this.isLoading, - required this.onPressed, - }); + const _LanguageApplyButton({required this.isDisabled, required this.isLoading, required this.onPressed}); final bool isDisabled; final bool isLoading; @@ -231,9 +199,7 @@ class _LanguageApplyButton extends StatelessWidget { @override Widget build(BuildContext context) { return DecoratedBox( - decoration: BoxDecoration( - color: context.colorScheme.surface, - ), + decoration: BoxDecoration(color: context.colorScheme.surface), child: Padding( padding: const EdgeInsets.all(16.0), child: SizedBox( @@ -242,18 +208,10 @@ class _LanguageApplyButton extends StatelessWidget { child: ElevatedButton( onPressed: isDisabled ? null : onPressed, child: isLoading - ? const SizedBox.square( - dimension: 24, - child: CircularProgressIndicator( - strokeWidth: 2, - ), - ) + ? const SizedBox.square(dimension: 24, child: CircularProgressIndicator(strokeWidth: 2)) : Text( 'setting_languages_apply'.t(context: context), - style: const TextStyle( - fontWeight: FontWeight.w600, - fontSize: 16.0, - ), + style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 16.0), ), ), ), @@ -279,48 +237,27 @@ class _LanguageItem extends StatelessWidget { @override Widget build(BuildContext context) { return Padding( - padding: const EdgeInsets.symmetric( - vertical: 4.0, - horizontal: 8.0, - ), + padding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 8.0), child: DecoratedBox( decoration: BoxDecoration( - color: - context.colorScheme.surfaceContainerLowest.withValues(alpha: .6), - borderRadius: const BorderRadius.all( - Radius.circular(16.0), - ), - border: Border.all( - color: context.colorScheme.outlineVariant.withValues(alpha: .4), - width: 1.0, - ), + color: context.colorScheme.surfaceContainerLowest.withValues(alpha: .6), + borderRadius: const BorderRadius.all(Radius.circular(16.0)), + border: Border.all(color: context.colorScheme.outlineVariant.withValues(alpha: .4), width: 1.0), ), child: ListTile( title: Text( countryName, style: context.textTheme.titleSmall?.copyWith( fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal, - color: isSelected - ? context.colorScheme.primary - : context.colorScheme.onSurfaceVariant, + color: isSelected ? context.colorScheme.primary : context.colorScheme.onSurfaceVariant, ), ), - trailing: isSelected - ? Icon( - Icons.check, - color: context.colorScheme.primary, - size: 20, - ) - : null, + trailing: isSelected ? Icon(Icons.check, color: context.colorScheme.primary, size: 20) : null, onTap: onTap, selected: isSelected, selectedTileColor: context.colorScheme.primary.withValues(alpha: .15), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(16.0)), - ), - contentPadding: const EdgeInsets.symmetric( - horizontal: 16.0, - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16.0))), + contentPadding: const EdgeInsets.symmetric(horizontal: 16.0), ), ), ); diff --git a/mobile/lib/widgets/settings/local_storage_settings.dart b/mobile/lib/widgets/settings/local_storage_settings.dart index 06db78cb97..af9e4079bb 100644 --- a/mobile/lib/widgets/settings/local_storage_settings.dart +++ b/mobile/lib/widgets/settings/local_storage_settings.dart @@ -14,13 +14,10 @@ class LocalStorageSettings extends HookConsumerWidget { final isarDb = ref.watch(dbProvider); final cacheItemCount = useState(0); - useEffect( - () { - cacheItemCount.value = isarDb.duplicatedAssets.countSync(); - return null; - }, - [], - ); + useEffect(() { + cacheItemCount.value = isarDb.duplicatedAssets.countSync(); + return null; + }, []); void clearCache() async { await isarDb.writeTxn(() => isarDb.duplicatedAssets.clear()); @@ -32,15 +29,11 @@ class LocalStorageSettings extends HookConsumerWidget { dense: true, title: Text( "cache_settings_duplicated_assets_title", - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500), ).tr(namedArgs: {'count': "${cacheItemCount.value}"}), subtitle: Text( "cache_settings_duplicated_assets_subtitle", - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ).tr(), trailing: TextButton( onPressed: cacheItemCount.value > 0 ? clearCache : null, diff --git a/mobile/lib/widgets/settings/networking_settings/endpoint_input.dart b/mobile/lib/widgets/settings/networking_settings/endpoint_input.dart index 6302f9422a..a712ce416c 100644 --- a/mobile/lib/widgets/settings/networking_settings/endpoint_input.dart +++ b/mobile/lib/widgets/settings/networking_settings/endpoint_input.dart @@ -61,8 +61,7 @@ class EndpointInputState extends ConsumerState { final url = controller.text; setState(() => auxCheckStatus = AuxCheckStatus.loading); - final isValid = - await ref.read(authProvider.notifier).validateAuxilaryServerUrl(url); + final isValid = await ref.read(authProvider.notifier).validateAuxilaryServerUrl(url); setState(() { if (mounted) { @@ -98,10 +97,7 @@ class EndpointInputState extends ConsumerState { color: Colors.red, alignment: Alignment.centerRight, padding: const EdgeInsets.only(right: 16), - child: const Icon( - Icons.delete, - color: Colors.white, - ), + child: const Icon(Icons.delete, color: Colors.white), ), child: ListTile( contentPadding: const EdgeInsets.symmetric(horizontal: 24), @@ -121,28 +117,19 @@ class EndpointInputState extends ConsumerState { autovalidateMode: AutovalidateMode.onUserInteraction, validator: validateUrl, keyboardType: TextInputType.url, - style: const TextStyle( - fontFamily: 'Inconsolata', - fontWeight: FontWeight.w600, - fontSize: 14, - ), + style: const TextStyle(fontFamily: 'Inconsolata', fontWeight: FontWeight.w600, fontSize: 14), decoration: InputDecoration( hintText: 'http(s)://immich.domain.com', contentPadding: const EdgeInsets.all(16), filled: true, fillColor: context.colorScheme.surfaceContainer, - border: const OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(16)), - ), + border: const OutlineInputBorder(borderRadius: BorderRadius.all(Radius.circular(16))), errorBorder: OutlineInputBorder( borderSide: BorderSide(color: Colors.red[300]!), borderRadius: const BorderRadius.all(Radius.circular(16)), ), disabledBorder: OutlineInputBorder( - borderSide: BorderSide( - color: - context.isDarkTheme ? Colors.grey[900]! : Colors.grey[300]!, - ), + borderSide: BorderSide(color: context.isDarkTheme ? Colors.grey[900]! : Colors.grey[300]!), borderRadius: const BorderRadius.all(Radius.circular(16)), ), ), diff --git a/mobile/lib/widgets/settings/networking_settings/external_network_preference.dart b/mobile/lib/widgets/settings/networking_settings/external_network_preference.dart index d38ba06d76..8cc6079961 100644 --- a/mobile/lib/widgets/settings/networking_settings/external_network_preference.dart +++ b/mobile/lib/widgets/settings/networking_settings/external_network_preference.dart @@ -17,30 +17,21 @@ class ExternalNetworkPreference extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final entries = useState( - [const AuxilaryEndpoint(url: '', status: AuxCheckStatus.unknown)], - ); + final entries = useState([const AuxilaryEndpoint(url: '', status: AuxCheckStatus.unknown)]); final canSave = useState(false); saveEndpointList() { - canSave.value = - entries.value.every((e) => e.status == AuxCheckStatus.valid); + canSave.value = entries.value.every((e) => e.status == AuxCheckStatus.valid); - final endpointList = entries.value - .where((url) => url.status == AuxCheckStatus.valid) - .toList(); + final endpointList = entries.value.where((url) => url.status == AuxCheckStatus.valid).toList(); final jsonString = jsonEncode(endpointList); - Store.put( - StoreKey.externalEndpointList, - jsonString, - ); + Store.put(StoreKey.externalEndpointList, jsonString); } updateValidationStatus(String url, int index, AuxCheckStatus status) { - entries.value[index] = - entries.value[index].copyWith(url: url, status: status); + entries.value[index] = entries.value[index].copyWith(url: url, status: status); saveEndpointList(); } @@ -63,11 +54,7 @@ class ExternalNetworkPreference extends HookConsumerWidget { saveEndpointList(); } - Widget proxyDecorator( - Widget child, - int index, - Animation animation, - ) { + Widget proxyDecorator(Widget child, int index, Animation animation) { return AnimatedBuilder( animation: animation, builder: (BuildContext context, Widget? child) { @@ -81,21 +68,17 @@ class ExternalNetworkPreference extends HookConsumerWidget { ); } - useEffect( - () { - final jsonString = Store.tryGet(StoreKey.externalEndpointList); + useEffect(() { + final jsonString = Store.tryGet(StoreKey.externalEndpointList); - if (jsonString == null) { - return null; - } - - final List jsonList = jsonDecode(jsonString); - entries.value = - jsonList.map((e) => AuxilaryEndpoint.fromJson(e)).toList(); + if (jsonString == null) { return null; - }, - const [], - ); + } + + final List jsonList = jsonDecode(jsonString); + entries.value = jsonList.map((e) => AuxilaryEndpoint.fromJson(e)).toList(); + return null; + }, const []); return Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), @@ -104,21 +87,14 @@ class ExternalNetworkPreference extends HookConsumerWidget { decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(16)), color: context.colorScheme.surfaceContainerLow, - border: Border.all( - color: context.colorScheme.surfaceContainerHighest, - width: 1, - ), + border: Border.all(color: context.colorScheme.surfaceContainerHighest, width: 1), ), child: Stack( children: [ Positioned( bottom: -36, right: -36, - child: Icon( - Icons.dns_rounded, - size: 120, - color: context.primaryColor.withValues(alpha: 0.05), - ), + child: Icon(Icons.dns_rounded, size: 120, color: context.primaryColor.withValues(alpha: 0.05)), ), ListView( padding: const EdgeInsets.symmetric(vertical: 16.0), @@ -126,14 +102,8 @@ class ExternalNetworkPreference extends HookConsumerWidget { shrinkWrap: true, children: [ Padding( - padding: const EdgeInsets.symmetric( - vertical: 4.0, - horizontal: 24, - ), - child: Text( - "external_network_sheet_info".tr(), - style: context.textTheme.bodyMedium, - ), + padding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 24), + child: Text("external_network_sheet_info".tr(), style: context.textTheme.bodyMedium), ), const SizedBox(height: 4), Divider(color: context.colorScheme.surfaceContainerHighest), @@ -170,10 +140,7 @@ class ExternalNetworkPreference extends HookConsumerWidget { ? () { entries.value = [ ...entries.value, - const AuxilaryEndpoint( - url: '', - status: AuxCheckStatus.unknown, - ), + const AuxilaryEndpoint(url: '', status: AuxCheckStatus.unknown), ]; } : null, diff --git a/mobile/lib/widgets/settings/networking_settings/local_network_preference.dart b/mobile/lib/widgets/settings/networking_settings/local_network_preference.dart index a50d216a9d..9fbc43a429 100644 --- a/mobile/lib/widgets/settings/networking_settings/local_network_preference.dart +++ b/mobile/lib/widgets/settings/networking_settings/local_network_preference.dart @@ -7,19 +7,11 @@ import 'package:immich_mobile/providers/auth.provider.dart'; import 'package:immich_mobile/providers/network.provider.dart'; class LocalNetworkPreference extends HookConsumerWidget { - const LocalNetworkPreference({ - super.key, - required this.enabled, - }); + const LocalNetworkPreference({super.key, required this.enabled}); final bool enabled; - Future _showEditDialog( - BuildContext context, - String title, - String hintText, - String initialValue, - ) { + Future _showEditDialog(BuildContext context, String title, String hintText, String initialValue) { final controller = TextEditingController(text: initialValue); return showDialog( @@ -29,23 +21,14 @@ class LocalNetworkPreference extends HookConsumerWidget { content: TextField( controller: controller, autofocus: true, - decoration: InputDecoration( - border: const OutlineInputBorder(), - hintText: hintText, - ), + decoration: InputDecoration(border: const OutlineInputBorder(), hintText: hintText), ), actions: [ TextButton( onPressed: () => Navigator.pop(context), - child: Text( - 'cancel'.tr().toUpperCase(), - style: const TextStyle(color: Colors.red), - ), - ), - TextButton( - onPressed: () => Navigator.pop(context, controller.text), - child: Text('save'.tr().toUpperCase()), + child: Text('cancel'.tr().toUpperCase(), style: const TextStyle(color: Colors.red)), ), + TextButton(onPressed: () => Navigator.pop(context, controller.text), child: Text('save'.tr().toUpperCase())), ], ), ); @@ -56,24 +39,20 @@ class LocalNetworkPreference extends HookConsumerWidget { final wifiNameText = useState(""); final localEndpointText = useState(""); - useEffect( - () { - final wifiName = ref.read(authProvider.notifier).getSavedWifiName(); - final localEndpoint = - ref.read(authProvider.notifier).getSavedLocalEndpoint(); + useEffect(() { + final wifiName = ref.read(authProvider.notifier).getSavedWifiName(); + final localEndpoint = ref.read(authProvider.notifier).getSavedLocalEndpoint(); - if (wifiName != null) { - wifiNameText.value = wifiName; - } + if (wifiName != null) { + wifiNameText.value = wifiName; + } - if (localEndpoint != null) { - localEndpointText.value = localEndpoint; - } + if (localEndpoint != null) { + localEndpointText.value = localEndpoint; + } - return null; - }, - [], - ); + return null; + }, []); saveWifiName(String wifiName) { wifiNameText.value = wifiName; @@ -86,12 +65,7 @@ class LocalNetworkPreference extends HookConsumerWidget { } handleEditWifiName() async { - final wifiName = await _showEditDialog( - context, - "wifi_name".tr(), - "your_wifi_name".tr(), - wifiNameText.value, - ); + final wifiName = await _showEditDialog(context, "wifi_name".tr(), "your_wifi_name".tr(), wifiNameText.value); if (wifiName != null) { await saveWifiName(wifiName); @@ -131,8 +105,7 @@ class LocalNetworkPreference extends HookConsumerWidget { saveWifiName(wifiName); } - final serverEndpoint = - ref.read(authProvider.notifier).getServerEndpoint(); + final serverEndpoint = ref.read(authProvider.notifier).getServerEndpoint(); if (serverEndpoint != null) { saveLocalEndpoint(serverEndpoint); @@ -148,21 +121,14 @@ class LocalNetworkPreference extends HookConsumerWidget { decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(16)), color: context.colorScheme.surfaceContainerLow, - border: Border.all( - color: context.colorScheme.surfaceContainerHighest, - width: 1, - ), + border: Border.all(color: context.colorScheme.surfaceContainerHighest, width: 1), ), child: Stack( children: [ Positioned( bottom: -36, right: -36, - child: Icon( - Icons.home_outlined, - size: 120, - color: context.primaryColor.withValues(alpha: 0.05), - ), + child: Icon(Icons.home_outlined, size: 120, color: context.primaryColor.withValues(alpha: 0.05)), ), ListView( padding: const EdgeInsets.symmetric(vertical: 16.0), @@ -170,19 +136,11 @@ class LocalNetworkPreference extends HookConsumerWidget { shrinkWrap: true, children: [ Padding( - padding: const EdgeInsets.symmetric( - vertical: 4.0, - horizontal: 24, - ), - child: Text( - "local_network_sheet_info".tr(), - style: context.textTheme.bodyMedium, - ), + padding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 24), + child: Text("local_network_sheet_info".tr(), style: context.textTheme.bodyMedium), ), const SizedBox(height: 4), - Divider( - color: context.colorScheme.surfaceContainerHighest, - ), + Divider(color: context.colorScheme.surfaceContainerHighest), ListTile( enabled: enabled, contentPadding: const EdgeInsets.only(left: 24, right: 8), @@ -194,10 +152,7 @@ class LocalNetworkPreference extends HookConsumerWidget { wifiNameText.value, style: context.textTheme.labelLarge?.copyWith( fontWeight: FontWeight.bold, - color: enabled - ? context.primaryColor - : context.colorScheme.onSurface - .withAlpha(100), + color: enabled ? context.primaryColor : context.colorScheme.onSurface.withAlpha(100), fontFamily: 'Inconsolata', ), ), @@ -217,10 +172,7 @@ class LocalNetworkPreference extends HookConsumerWidget { localEndpointText.value, style: context.textTheme.labelLarge?.copyWith( fontWeight: FontWeight.bold, - color: enabled - ? context.primaryColor - : context.colorScheme.onSurface - .withAlpha(100), + color: enabled ? context.primaryColor : context.colorScheme.onSurface.withAlpha(100), fontFamily: 'Inconsolata', ), ), @@ -231,15 +183,12 @@ class LocalNetworkPreference extends HookConsumerWidget { ), const SizedBox(height: 16), Padding( - padding: const EdgeInsets.symmetric( - horizontal: 24.0, - ), + padding: const EdgeInsets.symmetric(horizontal: 24.0), child: SizedBox( height: 48, child: OutlinedButton.icon( icon: const Icon(Icons.wifi_find_rounded), - label: - Text('use_current_connection'.tr().toUpperCase()), + label: Text('use_current_connection'.tr().toUpperCase()), onPressed: enabled ? autofillCurrentNetwork : null, ), ), diff --git a/mobile/lib/widgets/settings/networking_settings/networking_settings.dart b/mobile/lib/widgets/settings/networking_settings/networking_settings.dart index 587a0ce6d3..426ea5ac0f 100644 --- a/mobile/lib/widgets/settings/networking_settings/networking_settings.dart +++ b/mobile/lib/widgets/settings/networking_settings/networking_settings.dart @@ -18,8 +18,7 @@ class NetworkingSettings extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final currentEndpoint = getServerUrl(); - final featureEnabled = - useAppSettingsState(AppSettingsEnum.autoEndpointSwitching); + final featureEnabled = useAppSettingsState(AppSettingsEnum.autoEndpointSwitching); Future checkWifiReadPermission() async { final [hasLocationInUse, hasLocationAlways] = await Future.wait([ @@ -39,9 +38,7 @@ class NetworkingSettings extends HookConsumerWidget { actions: [ TextButton( onPressed: () async { - final isGrant = await ref - .read(networkProvider.notifier) - .requestWifiReadPermission(); + final isGrant = await ref.read(networkProvider.notifier).requestWifiReadPermission(); Navigator.pop(context, isGrant); }, @@ -63,9 +60,7 @@ class NetworkingSettings extends HookConsumerWidget { actions: [ TextButton( onPressed: () async { - final isGrant = await ref - .read(networkProvider.notifier) - .requestWifiReadBackgroundPermission(); + final isGrant = await ref.read(networkProvider.notifier).requestWifiReadBackgroundPermission(); Navigator.pop(context, isGrant); }, @@ -77,21 +72,17 @@ class NetworkingSettings extends HookConsumerWidget { ); } - if (isGrantLocationAlwaysPermission != null && - !isGrantLocationAlwaysPermission) { + if (isGrantLocationAlwaysPermission != null && !isGrantLocationAlwaysPermission) { await ref.read(networkProvider.notifier).openSettings(); } } - useEffect( - () { - if (featureEnabled.value == true) { - checkWifiReadPermission(); - } - return null; - }, - [featureEnabled.value], - ); + useEffect(() { + if (featureEnabled.value == true) { + checkWifiReadPermission(); + } + return null; + }, [featureEnabled.value]); return ListView( padding: const EdgeInsets.only(bottom: 96), @@ -101,9 +92,7 @@ class NetworkingSettings extends HookConsumerWidget { padding: const EdgeInsets.only(top: 8, left: 16, bottom: 8), child: NetworkPreferenceTitle( title: "current_server_address".tr().toUpperCase(), - icon: (currentEndpoint?.startsWith('https') ?? false) - ? Icons.https_outlined - : Icons.http_outlined, + icon: (currentEndpoint?.startsWith('https') ?? false) ? Icons.https_outlined : Icons.http_outlined, ), ), Padding( @@ -112,20 +101,12 @@ class NetworkingSettings extends HookConsumerWidget { elevation: 0, shape: RoundedRectangleBorder( borderRadius: const BorderRadius.all(Radius.circular(16)), - side: BorderSide( - color: context.colorScheme.surfaceContainerHighest, - width: 1, - ), + side: BorderSide(color: context.colorScheme.surfaceContainerHighest, width: 1), ), child: ListTile( leading: currentEndpoint != null - ? const Icon( - Icons.check_circle_rounded, - color: Colors.green, - ) - : const Icon( - Icons.circle_outlined, - ), + ? const Icon(Icons.check_circle_rounded, color: Colors.green) + : const Icon(Icons.circle_outlined), title: Text( currentEndpoint ?? "--", style: TextStyle( @@ -140,9 +121,7 @@ class NetworkingSettings extends HookConsumerWidget { ), Padding( padding: const EdgeInsets.only(top: 10.0), - child: Divider( - color: context.colorScheme.surfaceContainerHighest, - ), + child: Divider(color: context.colorScheme.surfaceContainerHighest), ), SettingsSwitchListTile( enabled: true, @@ -152,35 +131,21 @@ class NetworkingSettings extends HookConsumerWidget { ), Padding( padding: const EdgeInsets.only(top: 8, left: 16, bottom: 16), - child: NetworkPreferenceTitle( - title: "local_network".tr().toUpperCase(), - icon: Icons.home_outlined, - ), - ), - LocalNetworkPreference( - enabled: featureEnabled.value, + child: NetworkPreferenceTitle(title: "local_network".tr().toUpperCase(), icon: Icons.home_outlined), ), + LocalNetworkPreference(enabled: featureEnabled.value), Padding( padding: const EdgeInsets.only(top: 32, left: 16, bottom: 16), - child: NetworkPreferenceTitle( - title: "external_network".tr().toUpperCase(), - icon: Icons.dns_outlined, - ), - ), - ExternalNetworkPreference( - enabled: featureEnabled.value, + child: NetworkPreferenceTitle(title: "external_network".tr().toUpperCase(), icon: Icons.dns_outlined), ), + ExternalNetworkPreference(enabled: featureEnabled.value), ], ); } } class NetworkPreferenceTitle extends StatelessWidget { - const NetworkPreferenceTitle({ - super.key, - required this.icon, - required this.title, - }); + const NetworkPreferenceTitle({super.key, required this.icon, required this.title}); final IconData icon; final String title; @@ -189,10 +154,7 @@ class NetworkPreferenceTitle extends StatelessWidget { Widget build(BuildContext context) { return Row( children: [ - Icon( - icon, - color: context.colorScheme.onSurface.withAlpha(150), - ), + Icon(icon, color: context.colorScheme.onSurface.withAlpha(150)), const SizedBox(width: 8), Text( title, @@ -207,58 +169,37 @@ class NetworkPreferenceTitle extends StatelessWidget { } class NetworkStatusIcon extends StatelessWidget { - const NetworkStatusIcon({ - super.key, - required this.status, - this.enabled = true, - }) : super(); + const NetworkStatusIcon({super.key, required this.status, this.enabled = true}) : super(); final AuxCheckStatus status; final bool enabled; @override Widget build(BuildContext context) { - return AnimatedSwitcher( - duration: const Duration(milliseconds: 200), - child: _buildIcon(context), - ); + return AnimatedSwitcher(duration: const Duration(milliseconds: 200), child: _buildIcon(context)); } Widget _buildIcon(BuildContext context) => switch (status) { - AuxCheckStatus.loading => Padding( - padding: const EdgeInsets.only(left: 4.0), - child: SizedBox( - width: 18, - height: 18, - child: CircularProgressIndicator( - color: context.primaryColor, - strokeWidth: 2, - key: const ValueKey('loading'), - ), + AuxCheckStatus.loading => Padding( + padding: const EdgeInsets.only(left: 4.0), + child: SizedBox( + width: 18, + height: 18, + child: CircularProgressIndicator(color: context.primaryColor, strokeWidth: 2, key: const ValueKey('loading')), + ), + ), + AuxCheckStatus.valid => + enabled + ? const Icon(Icons.check_circle_rounded, color: Colors.green, key: ValueKey('success')) + : Icon( + Icons.check_circle_rounded, + color: context.colorScheme.onSurface.withAlpha(100), + key: const ValueKey('success'), ), - ), - AuxCheckStatus.valid => enabled - ? const Icon( - Icons.check_circle_rounded, - color: Colors.green, - key: ValueKey('success'), - ) - : Icon( - Icons.check_circle_rounded, - color: context.colorScheme.onSurface.withAlpha(100), - key: const ValueKey('success'), - ), - AuxCheckStatus.error => enabled - ? const Icon( - Icons.error_rounded, - color: Colors.red, - key: ValueKey('error'), - ) - : const Icon( - Icons.error_rounded, - color: Colors.grey, - key: ValueKey('error'), - ), - _ => const Icon(Icons.circle_outlined, key: ValueKey('unknown')), - }; + AuxCheckStatus.error => + enabled + ? const Icon(Icons.error_rounded, color: Colors.red, key: ValueKey('error')) + : const Icon(Icons.error_rounded, color: Colors.grey, key: ValueKey('error')), + _ => const Icon(Icons.circle_outlined, key: ValueKey('unknown')), + }; } diff --git a/mobile/lib/widgets/settings/notification_setting.dart b/mobile/lib/widgets/settings/notification_setting.dart index cf6745199e..d9eab26bda 100644 --- a/mobile/lib/widgets/settings/notification_setting.dart +++ b/mobile/lib/widgets/settings/notification_setting.dart @@ -12,20 +12,15 @@ import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; import 'package:permission_handler/permission_handler.dart'; class NotificationSetting extends HookConsumerWidget { - const NotificationSetting({ - super.key, - }); + const NotificationSetting({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final permissionService = ref.watch(notificationPermissionProvider); - final sliderValue = - useAppSettingsState(AppSettingsEnum.uploadErrorNotificationGracePeriod); - final totalProgressValue = - useAppSettingsState(AppSettingsEnum.backgroundBackupTotalProgress); - final singleProgressValue = - useAppSettingsState(AppSettingsEnum.backgroundBackupSingleProgress); + final sliderValue = useAppSettingsState(AppSettingsEnum.uploadErrorNotificationGracePeriod); + final totalProgressValue = useAppSettingsState(AppSettingsEnum.backgroundBackupTotalProgress); + final singleProgressValue = useAppSettingsState(AppSettingsEnum.backgroundBackupSingleProgress); final hasPermission = permissionService == PermissionStatus.granted; @@ -42,21 +37,14 @@ class NotificationSetting extends HookConsumerWidget { builder: (ctx) => AlertDialog( content: const Text('notification_permission_dialog_content').tr(), actions: [ - TextButton( - child: const Text('cancel').tr(), - onPressed: () => ctx.pop(), - ), - TextButton( - onPressed: () => openAppNotificationSettings(ctx), - child: const Text('settings').tr(), - ), + TextButton(child: const Text('cancel').tr(), onPressed: () => ctx.pop()), + TextButton(onPressed: () => openAppNotificationSettings(ctx), child: const Text('settings').tr()), ], ), ); } - final String formattedValue = - _formatSliderValue(sliderValue.value.toDouble()); + final String formattedValue = _formatSliderValue(sliderValue.value.toDouble()); final notificationSettings = [ if (!hasPermission) @@ -65,14 +53,12 @@ class NotificationSetting extends HookConsumerWidget { title: 'notification_permission_list_tile_title'.tr(), subtileText: 'notification_permission_list_tile_content'.tr(), buttonText: 'notification_permission_list_tile_enable_button'.tr(), - onButtonTap: () => ref - .watch(notificationPermissionProvider.notifier) - .requestNotificationPermission() - .then((permission) { - if (permission == PermissionStatus.permanentlyDenied) { - showPermissionsDialog(); - } - }), + onButtonTap: () => + ref.watch(notificationPermissionProvider.notifier).requestNotificationPermission().then((permission) { + if (permission == PermissionStatus.permanentlyDenied) { + showPermissionsDialog(); + } + }), ), SettingsSwitchListTile( enabled: hasPermission, @@ -89,8 +75,7 @@ class NotificationSetting extends HookConsumerWidget { SettingsSliderListTile( enabled: hasPermission, valueNotifier: sliderValue, - text: 'setting_notifications_notify_failures_grace_period' - .tr(namedArgs: {'duration': formattedValue}), + text: 'setting_notifications_notify_failures_grace_period'.tr(namedArgs: {'duration': formattedValue}), maxValue: 5.0, noDivisons: 5, label: formattedValue, @@ -105,8 +90,7 @@ String _formatSliderValue(double v) { if (v == 0.0) { return 'setting_notifications_notify_immediately'.tr(); } else if (v == 1.0) { - return 'setting_notifications_notify_minutes' - .tr(namedArgs: {'count': '30'}); + return 'setting_notifications_notify_minutes'.tr(namedArgs: {'count': '30'}); } else if (v == 2.0) { return 'setting_notifications_notify_hours'.tr(namedArgs: {'count': '2'}); } else if (v == 3.0) { diff --git a/mobile/lib/widgets/settings/preference_settings/haptic_setting.dart b/mobile/lib/widgets/settings/preference_settings/haptic_setting.dart index 90a123bfbd..49f57a5e94 100644 --- a/mobile/lib/widgets/settings/preference_settings/haptic_setting.dart +++ b/mobile/lib/widgets/settings/preference_settings/haptic_setting.dart @@ -8,16 +8,12 @@ import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; class HapticSetting extends HookConsumerWidget { - const HapticSetting({ - super.key, - }); + const HapticSetting({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { - final hapticFeedbackSetting = - useAppSettingsState(AppSettingsEnum.enableHapticFeedback); - final isHapticFeedbackEnabled = - useValueNotifier(hapticFeedbackSetting.value); + final hapticFeedbackSetting = useAppSettingsState(AppSettingsEnum.enableHapticFeedback); + final isHapticFeedbackEnabled = useValueNotifier(hapticFeedbackSetting.value); onHapticFeedbackChange(bool isEnabled) { hapticFeedbackSetting.value = isEnabled; diff --git a/mobile/lib/widgets/settings/preference_settings/preference_setting.dart b/mobile/lib/widgets/settings/preference_settings/preference_setting.dart index 8a3684e093..144fbf9758 100644 --- a/mobile/lib/widgets/settings/preference_settings/preference_setting.dart +++ b/mobile/lib/widgets/settings/preference_settings/preference_setting.dart @@ -4,20 +4,12 @@ import 'package:immich_mobile/widgets/settings/preference_settings/theme_setting import 'package:immich_mobile/widgets/settings/settings_sub_page_scaffold.dart'; class PreferenceSetting extends StatelessWidget { - const PreferenceSetting({ - super.key, - }); + const PreferenceSetting({super.key}); @override Widget build(BuildContext context) { - const preferenceSettings = [ - ThemeSetting(), - HapticSetting(), - ]; + const preferenceSettings = [ThemeSetting(), HapticSetting()]; - return const SettingsSubPageScaffold( - settings: preferenceSettings, - showDivider: true, - ); + return const SettingsSubPageScaffold(settings: preferenceSettings, showDivider: true); } } diff --git a/mobile/lib/widgets/settings/preference_settings/primary_color_setting.dart b/mobile/lib/widgets/settings/preference_settings/primary_color_setting.dart index 011904c310..ddf2cc6215 100644 --- a/mobile/lib/widgets/settings/preference_settings/primary_color_setting.dart +++ b/mobile/lib/widgets/settings/preference_settings/primary_color_setting.dart @@ -12,26 +12,21 @@ import 'package:immich_mobile/theme/dynamic_theme.dart'; import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; class PrimaryColorSetting extends HookConsumerWidget { - const PrimaryColorSetting({ - super.key, - }); + const PrimaryColorSetting({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final themeProvider = ref.read(immichThemeProvider); - final primaryColorSetting = - useAppSettingsState(AppSettingsEnum.primaryColor); - final systemPrimaryColorSetting = - useAppSettingsState(AppSettingsEnum.dynamicTheme); + final primaryColorSetting = useAppSettingsState(AppSettingsEnum.primaryColor); + final systemPrimaryColorSetting = useAppSettingsState(AppSettingsEnum.dynamicTheme); final currentPreset = useValueNotifier(ref.read(immichThemePresetProvider)); const tileSize = 55.0; useValueChanged( primaryColorSetting.value, - (_, __) => currentPreset.value = ImmichColorPreset.values - .firstWhere((e) => e.name == primaryColorSetting.value), + (_, __) => currentPreset.value = ImmichColorPreset.values.firstWhere((e) => e.name == primaryColorSetting.value), ); void popBottomSheet() { @@ -73,20 +68,14 @@ class PrimaryColorSetting extends HookConsumerWidget { Container( height: tileSize, width: tileSize, - decoration: BoxDecoration( - color: bottomColor, - borderRadius: const BorderRadius.all(Radius.circular(100)), - ), + decoration: BoxDecoration(color: bottomColor, borderRadius: const BorderRadius.all(Radius.circular(100))), ), Container( height: tileSize / 2, width: tileSize, decoration: BoxDecoration( color: topColor, - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(100), - topRight: Radius.circular(100), - ), + borderRadius: const BorderRadius.only(topLeft: Radius.circular(100), topRight: Radius.circular(100)), ), ), if (showSelector) @@ -102,11 +91,7 @@ class PrimaryColorSetting extends HookConsumerWidget { ), child: const Padding( padding: EdgeInsets.all(3), - child: Icon( - Icons.check_rounded, - color: Colors.white, - size: 25, - ), + child: Icon(Icons.check_rounded, color: Colors.white, size: 25), ), ), ), @@ -121,30 +106,21 @@ class PrimaryColorSetting extends HookConsumerWidget { children: [ Align( alignment: Alignment.center, - child: Text( - "theme_setting_primary_color_title".tr(), - style: context.textTheme.titleLarge, - ), + child: Text("theme_setting_primary_color_title".tr(), style: context.textTheme.titleLarge), ), if (DynamicTheme.isAvailable) Container( padding: const EdgeInsets.symmetric(horizontal: 20), margin: const EdgeInsets.only(top: 10), child: SwitchListTile.adaptive( - contentPadding: - const EdgeInsets.symmetric(vertical: 6, horizontal: 20), + contentPadding: const EdgeInsets.symmetric(vertical: 6, horizontal: 20), dense: true, activeColor: context.primaryColor, tileColor: context.colorScheme.surfaceContainerHigh, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(15)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(15))), title: Text( 'theme_setting_system_primary_color_title'.tr(), - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - height: 1.5, - ), + style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500, height: 1.5), ), value: systemPrimaryColorSetting.value, onChanged: onUseSystemColorChange, @@ -164,8 +140,7 @@ class PrimaryColorSetting extends HookConsumerWidget { topColor: theme.light.primary, bottomColor: theme.dark.primary, tileSize: tileSize, - showSelector: currentPreset.value == preset && - !systemPrimaryColorSetting.value, + showSelector: currentPreset.value == preset && !systemPrimaryColorSetting.value, ), ); }).toList(), @@ -180,10 +155,7 @@ class PrimaryColorSetting extends HookConsumerWidget { context: context, isScrollControlled: true, builder: (BuildContext ctx) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 0), - child: bottomSheetContent(), - ); + return Padding(padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 0), child: bottomSheetContent()); }, ), contentPadding: const EdgeInsets.symmetric(horizontal: 20), @@ -195,14 +167,11 @@ class PrimaryColorSetting extends HookConsumerWidget { children: [ Text( "theme_setting_primary_color_title".tr(), - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - ), + style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500), ), Text( "theme_setting_primary_color_subtitle".tr(), - style: context.textTheme.bodyMedium - ?.copyWith(color: context.colorScheme.onSurfaceSecondary), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), ], ), diff --git a/mobile/lib/widgets/settings/preference_settings/theme_setting.dart b/mobile/lib/widgets/settings/preference_settings/theme_setting.dart index 0c68de4c52..123f7c9921 100644 --- a/mobile/lib/widgets/settings/preference_settings/theme_setting.dart +++ b/mobile/lib/widgets/settings/preference_settings/theme_setting.dart @@ -11,22 +11,17 @@ import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; class ThemeSetting extends HookConsumerWidget { - const ThemeSetting({ - super.key, - }); + const ThemeSetting({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final currentThemeString = useAppSettingsState(AppSettingsEnum.themeMode); final currentTheme = useValueNotifier(ref.read(immichThemeModeProvider)); final isDarkTheme = useValueNotifier(currentTheme.value == ThemeMode.dark); - final isSystemTheme = - useValueNotifier(currentTheme.value == ThemeMode.system); + final isSystemTheme = useValueNotifier(currentTheme.value == ThemeMode.system); - final applyThemeToBackgroundSetting = - useAppSettingsState(AppSettingsEnum.colorfulInterface); - final applyThemeToBackgroundProvider = - useValueNotifier(ref.read(colorfulInterfaceSettingProvider)); + final applyThemeToBackgroundSetting = useAppSettingsState(AppSettingsEnum.colorfulInterface); + final applyThemeToBackgroundProvider = useValueNotifier(ref.read(colorfulInterfaceSettingProvider)); useValueChanged( currentThemeString.value, @@ -39,8 +34,7 @@ class ThemeSetting extends HookConsumerWidget { useValueChanged( applyThemeToBackgroundSetting.value, - (_, __) => applyThemeToBackgroundProvider.value = - applyThemeToBackgroundSetting.value, + (_, __) => applyThemeToBackgroundProvider.value = applyThemeToBackgroundSetting.value, ); void onThemeChange(bool isDark) { @@ -74,8 +68,7 @@ class ThemeSetting extends HookConsumerWidget { void onSurfaceColorSettingChange(bool useColorfulInterface) { applyThemeToBackgroundSetting.value = useColorfulInterface; - ref.watch(colorfulInterfaceSettingProvider.notifier).state = - useColorfulInterface; + ref.watch(colorfulInterfaceSettingProvider.notifier).state = useColorfulInterface; } return Column( diff --git a/mobile/lib/widgets/settings/settings_button_list_tile.dart b/mobile/lib/widgets/settings/settings_button_list_tile.dart index c8bd8e4b58..0e8d75b22f 100644 --- a/mobile/lib/widgets/settings/settings_button_list_tile.dart +++ b/mobile/lib/widgets/settings/settings_button_list_tile.dart @@ -31,12 +31,7 @@ class SettingsButtonListTile extends StatelessWidget { horizontalTitleGap: 20, isThreeLine: true, leading: Icon(icon, color: iconColor), - title: Text( - title, - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - ), - ), + title: Text(title, style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500)), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -44,14 +39,11 @@ class SettingsButtonListTile extends StatelessWidget { if (subtileText != null) Text( subtileText!, - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), if (subtitle != null) subtitle!, const SizedBox(height: 6), - child ?? - ElevatedButton(onPressed: onButtonTap, child: Text(buttonText)), + child ?? ElevatedButton(onPressed: onButtonTap, child: Text(buttonText)), ], ), ); diff --git a/mobile/lib/widgets/settings/settings_card.dart b/mobile/lib/widgets/settings/settings_card.dart new file mode 100644 index 0000000000..36eff7bae1 --- /dev/null +++ b/mobile/lib/widgets/settings/settings_card.dart @@ -0,0 +1,49 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; + +class SettingsCard extends StatelessWidget { + const SettingsCard({ + super.key, + required this.icon, + required this.title, + required this.subtitle, + required this.settingRoute, + }); + + final IconData icon; + final String title; + final String subtitle; + final PageRouteInfo settingRoute; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Card( + elevation: 0, + clipBehavior: Clip.antiAlias, + color: context.colorScheme.surfaceContainer, + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16))), + margin: const EdgeInsets.symmetric(vertical: 4.0), + child: ListTile( + contentPadding: const EdgeInsets.symmetric(horizontal: 16.0), + leading: Container( + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(16)), + color: context.isDarkTheme ? Colors.black26 : Colors.white.withAlpha(100), + ), + padding: const EdgeInsets.all(16.0), + child: Icon(icon, color: context.primaryColor), + ), + title: Text( + title, + style: context.textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w600, color: context.primaryColor), + ), + subtitle: Text(subtitle, style: context.textTheme.labelLarge), + onTap: () => context.pushRoute(settingRoute), + ), + ), + ); + } +} diff --git a/mobile/lib/widgets/settings/settings_radio_list_tile.dart b/mobile/lib/widgets/settings/settings_radio_list_tile.dart index 3f3a6cbe69..95224e3f59 100644 --- a/mobile/lib/widgets/settings/settings_radio_list_tile.dart +++ b/mobile/lib/widgets/settings/settings_radio_list_tile.dart @@ -13,12 +13,7 @@ class SettingsRadioListTile extends StatelessWidget { final T groupBy; final void Function(T?) onRadioChanged; - const SettingsRadioListTile({ - super.key, - required this.groups, - required this.groupBy, - required this.onRadioChanged, - }); + const SettingsRadioListTile({super.key, required this.groups, required this.groupBy, required this.onRadioChanged}); @override Widget build(BuildContext context) { @@ -29,12 +24,7 @@ class SettingsRadioListTile extends StatelessWidget { contentPadding: const EdgeInsets.symmetric(horizontal: 20), dense: true, activeColor: context.primaryColor, - title: Text( - g.title, - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - ), - ), + title: Text(g.title, style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500)), value: g.value, groupValue: groupBy, onChanged: onRadioChanged, diff --git a/mobile/lib/widgets/settings/settings_slider_list_tile.dart b/mobile/lib/widgets/settings/settings_slider_list_tile.dart index 386a690864..500591badb 100644 --- a/mobile/lib/widgets/settings/settings_slider_list_tile.dart +++ b/mobile/lib/widgets/settings/settings_slider_list_tile.dart @@ -28,12 +28,7 @@ class SettingsSliderListTile extends StatelessWidget { return ListTile( contentPadding: const EdgeInsets.symmetric(horizontal: 20), dense: true, - title: Text( - text, - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - ), - ), + title: Text(text, style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500)), subtitle: Slider( value: valueNotifier.value.toDouble(), onChanged: (double v) => valueNotifier.value = v.toInt(), diff --git a/mobile/lib/widgets/settings/settings_sub_page_scaffold.dart b/mobile/lib/widgets/settings/settings_sub_page_scaffold.dart index 96c4678ede..b4cb67239e 100644 --- a/mobile/lib/widgets/settings/settings_sub_page_scaffold.dart +++ b/mobile/lib/widgets/settings/settings_sub_page_scaffold.dart @@ -4,11 +4,7 @@ class SettingsSubPageScaffold extends StatelessWidget { final List settings; final bool showDivider; - const SettingsSubPageScaffold({ - super.key, - required this.settings, - this.showDivider = false, - }); + const SettingsSubPageScaffold({super.key, required this.settings, this.showDivider = false}); @override Widget build(BuildContext context) { @@ -18,11 +14,7 @@ class SettingsSubPageScaffold extends StatelessWidget { itemBuilder: (ctx, index) => settings[index], separatorBuilder: (context, index) => showDivider ? const Column( - children: [ - SizedBox(height: 5), - Divider(height: 10, indent: 15, endIndent: 15), - SizedBox(height: 15), - ], + children: [SizedBox(height: 5), Divider(height: 10, indent: 15, endIndent: 15), SizedBox(height: 15)], ) : const SizedBox(height: 10), ); diff --git a/mobile/lib/widgets/settings/settings_sub_title.dart b/mobile/lib/widgets/settings/settings_sub_title.dart index 9a3fb6947d..d98f1929b2 100644 --- a/mobile/lib/widgets/settings/settings_sub_title.dart +++ b/mobile/lib/widgets/settings/settings_sub_title.dart @@ -4,10 +4,7 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart'; class SettingsSubTitle extends StatelessWidget { final String title; - const SettingsSubTitle({ - super.key, - required this.title, - }); + const SettingsSubTitle({super.key, required this.title}); @override Widget build(BuildContext context) { @@ -15,10 +12,7 @@ class SettingsSubTitle extends StatelessWidget { padding: const EdgeInsets.only(left: 20), child: Text( title, - style: context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - fontWeight: FontWeight.w700, - ), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor, fontWeight: FontWeight.w700), ), ); } diff --git a/mobile/lib/widgets/settings/settings_switch_list_tile.dart b/mobile/lib/widgets/settings/settings_switch_list_tile.dart index 8aa4ec0a60..d51e2eb2ca 100644 --- a/mobile/lib/widgets/settings/settings_switch_list_tile.dart +++ b/mobile/lib/widgets/settings/settings_switch_list_tile.dart @@ -40,18 +40,13 @@ class SettingsSwitchListTile extends StatelessWidget { selectedTileColor: enabled ? null : context.themeData.disabledColor, value: valueNotifier.value, onChanged: onSwitchChanged, - activeColor: - enabled ? context.primaryColor : context.themeData.disabledColor, + activeColor: enabled ? context.primaryColor : context.themeData.disabledColor, dense: true, - secondary: icon != null - ? Icon( - icon!, - color: valueNotifier.value ? context.primaryColor : null, - ) - : null, + secondary: icon != null ? Icon(icon!, color: valueNotifier.value ? context.primaryColor : null) : null, title: Text( title, - style: titleStyle ?? + style: + titleStyle ?? context.textTheme.bodyLarge?.copyWith( fontWeight: FontWeight.w500, color: enabled ? null : context.themeData.disabledColor, @@ -61,11 +56,10 @@ class SettingsSwitchListTile extends StatelessWidget { subtitle: subtitle != null ? Text( subtitle!, - style: subtitleStyle ?? + style: + subtitleStyle ?? context.textTheme.bodyMedium?.copyWith( - color: enabled - ? context.colorScheme.onSurfaceSecondary - : context.themeData.disabledColor, + color: enabled ? context.colorScheme.onSurfaceSecondary : context.themeData.disabledColor, ), ) : null, diff --git a/mobile/lib/widgets/settings/ssl_client_cert_settings.dart b/mobile/lib/widgets/settings/ssl_client_cert_settings.dart index 6fdbb156d9..dc31acf0a4 100644 --- a/mobile/lib/widgets/settings/ssl_client_cert_settings.dart +++ b/mobile/lib/widgets/settings/ssl_client_cert_settings.dart @@ -20,8 +20,7 @@ class SslClientCertSettings extends StatefulWidget { } class _SslClientCertSettingsState extends State { - _SslClientCertSettingsState() - : isCertExist = SSLClientCertStoreVal.load() != null; + _SslClientCertSettingsState() : isCertExist = SSLClientCertStoreVal.load() != null; bool isCertExist; @@ -31,24 +30,15 @@ class _SslClientCertSettingsState extends State { contentPadding: const EdgeInsets.symmetric(horizontal: 20), horizontalTitleGap: 20, isThreeLine: true, - title: Text( - "client_cert_title".tr(), - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - ), - ), + title: Text("client_cert_title".tr(), style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500)), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "client_cert_subtitle".tr(), - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurfaceSecondary, - ), - ), - const SizedBox( - height: 6, + style: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), + const SizedBox(height: 6), Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, @@ -58,13 +48,9 @@ class _SslClientCertSettingsState extends State { onPressed: widget.isLoggedIn ? null : () => importCert(context), child: Text("client_cert_import".tr()), ), - const SizedBox( - width: 15, - ), + const SizedBox(width: 15), ElevatedButton( - onPressed: widget.isLoggedIn || !isCertExist - ? null - : () => removeCert(context), + onPressed: widget.isLoggedIn || !isCertExist ? null : () async => await removeCert(context), child: Text("remove".tr()), ), ], @@ -79,35 +65,25 @@ class _SslClientCertSettingsState extends State { context: context, builder: (ctx) => AlertDialog( content: Text(message), - actions: [ - TextButton( - onPressed: () => ctx.pop(), - child: Text("client_cert_dialog_msg_confirm".tr()), - ), - ], + actions: [TextButton(onPressed: () => ctx.pop(), child: Text("client_cert_dialog_msg_confirm".tr()))], ), ); } - void storeCert(BuildContext context, Uint8List data, String? password) { + Future storeCert(BuildContext context, Uint8List data, String? password) async { if (password != null && password.isEmpty) { password = null; } final cert = SSLClientCertStoreVal(data, password); // Test whether the certificate is valid - final isCertValid = HttpSSLCertOverride.setClientCert( - SecurityContext(withTrustedRoots: true), - cert, - ); + final isCertValid = HttpSSLCertOverride.setClientCert(SecurityContext(withTrustedRoots: true), cert); if (!isCertValid) { showMessage(context, "client_cert_invalid_msg".tr()); return; } - cert.save(); + await cert.save(); HttpSSLOptions.apply(); - setState( - () => isCertExist = true, - ); + setState(() => isCertExist = true); showMessage(context, "client_cert_import_success_msg".tr()); } @@ -121,14 +97,11 @@ class _SslClientCertSettingsState extends State { controller: password, obscureText: true, obscuringCharacter: "*", - decoration: InputDecoration( - hintText: "client_cert_enter_password".tr(), - ), + decoration: InputDecoration(hintText: "client_cert_enter_password".tr()), ), actions: [ TextButton( - onPressed: () => - {ctx.pop(), storeCert(context, data, password.text)}, + onPressed: () async => {ctx.pop(), await storeCert(context, data, password.text)}, child: Text("client_cert_dialog_msg_confirm".tr()), ), ], @@ -139,10 +112,7 @@ class _SslClientCertSettingsState extends State { Future importCert(BuildContext ctx) async { FilePickerResult? res = await FilePicker.platform.pickFiles( type: FileType.custom, - allowedExtensions: [ - 'p12', - 'pfx', - ], + allowedExtensions: ['p12', 'pfx'], ); if (res != null) { File file = File(res.files.single.path!); @@ -151,12 +121,10 @@ class _SslClientCertSettingsState extends State { } } - void removeCert(BuildContext context) { - SSLClientCertStoreVal.delete(); + Future removeCert(BuildContext context) async { + await SSLClientCertStoreVal.delete(); HttpSSLOptions.apply(); - setState( - () => isCertExist = false, - ); + setState(() => isCertExist = false); showMessage(context, "client_cert_remove_msg".tr()); } } diff --git a/mobile/lib/widgets/shared_link/shared_link_item.dart b/mobile/lib/widgets/shared_link/shared_link_item.dart index a9707eb0d3..0eced33ce3 100644 --- a/mobile/lib/widgets/shared_link/shared_link_item.dart +++ b/mobile/lib/widgets/shared_link/shared_link_item.dart @@ -33,10 +33,7 @@ class SharedLinkItem extends ConsumerWidget { var expiresText = "shared_link_expires_never".tr(); if (sharedLink.expiresAt != null) { if (isExpired()) { - return Text( - "expired", - style: TextStyle(color: Colors.red[300]), - ).tr(); + return Text("expired", style: TextStyle(color: Colors.red[300])).tr(); } final difference = sharedLink.expiresAt!.difference(DateTime.now()); debugPrint("Difference: $difference"); @@ -45,40 +42,28 @@ class SharedLinkItem extends ConsumerWidget { if (difference.inHours % 24 > 12) { dayDifference += 1; } - expiresText = "shared_link_expires_days" - .tr(namedArgs: {'count': dayDifference.toString()}); + expiresText = "shared_link_expires_days".tr(namedArgs: {'count': dayDifference.toString()}); } else if (difference.inHours > 0) { - expiresText = "shared_link_expires_hours" - .tr(namedArgs: {'count': difference.inHours.toString()}); + expiresText = "shared_link_expires_hours".tr(namedArgs: {'count': difference.inHours.toString()}); } else if (difference.inMinutes > 0) { - expiresText = "shared_link_expires_minutes" - .tr(namedArgs: {'count': difference.inMinutes.toString()}); + expiresText = "shared_link_expires_minutes".tr(namedArgs: {'count': difference.inMinutes.toString()}); } else if (difference.inSeconds > 0) { - expiresText = "shared_link_expires_seconds" - .tr(namedArgs: {'count': difference.inSeconds.toString()}); + expiresText = "shared_link_expires_seconds".tr(namedArgs: {'count': difference.inSeconds.toString()}); } } - return Text( - expiresText, - style: TextStyle(color: isDarkMode ? Colors.grey[400] : Colors.grey[600]), - ); + return Text(expiresText, style: TextStyle(color: isDarkMode ? Colors.grey[400] : Colors.grey[600])); } @override Widget build(BuildContext context, WidgetRef ref) { final colorScheme = context.colorScheme; final isDarkMode = colorScheme.brightness == Brightness.dark; - final thumbnailUrl = sharedLink.thumbAssetId != null - ? getThumbnailUrlForRemoteId(sharedLink.thumbAssetId!) - : null; + final thumbnailUrl = sharedLink.thumbAssetId != null ? getThumbnailUrlForRemoteId(sharedLink.thumbAssetId!) : null; final imageSize = math.min(context.width / 4, 100.0); void copyShareLinkToClipboard() { - final externalDomain = ref.read( - serverInfoProvider.select((s) => s.serverConfig.externalDomain), - ); - var serverUrl = - externalDomain.isNotEmpty ? externalDomain : getServerUrl(); + final externalDomain = ref.read(serverInfoProvider.select((s) => s.serverConfig.externalDomain)); + var serverUrl = externalDomain.isNotEmpty ? externalDomain : getServerUrl(); if (serverUrl != null && !serverUrl.endsWith('/')) { serverUrl += '/'; } @@ -92,16 +77,12 @@ class SharedLinkItem extends ConsumerWidget { return; } - Clipboard.setData( - ClipboardData(text: "${serverUrl}share/${sharedLink.key}"), - ).then((_) { + Clipboard.setData(ClipboardData(text: "${serverUrl}share/${sharedLink.key}")).then((_) { context.scaffoldMessenger.showSnackBar( SnackBar( content: Text( "shared_link_clipboard_copied_massage", - style: context.textTheme.bodyLarge?.copyWith( - color: context.primaryColor, - ), + style: context.textTheme.bodyLarge?.copyWith(color: context.primaryColor), ).tr(), duration: const Duration(seconds: 2), ), @@ -116,9 +97,7 @@ class SharedLinkItem extends ConsumerWidget { return ConfirmDialog( title: "delete_shared_link_dialog_title", content: "confirm_delete_shared_link", - onOk: () => ref - .read(sharedLinksStateProvider.notifier) - .deleteLink(sharedLink.id), + onOk: () => ref.read(sharedLinksStateProvider.notifier).deleteLink(sharedLink.id), ); }, ); @@ -129,14 +108,9 @@ class SharedLinkItem extends ConsumerWidget { return Container( height: imageSize * 1.2, width: imageSize, - decoration: BoxDecoration( - color: isDarkMode ? Colors.grey[800] : Colors.grey[200], - ), + decoration: BoxDecoration(color: isDarkMode ? Colors.grey[800] : Colors.grey[200]), child: Center( - child: Icon( - Icons.image_not_supported_outlined, - color: isDarkMode ? Colors.grey[100] : Colors.grey[700], - ), + child: Icon(Icons.image_not_supported_outlined, color: isDarkMode ? Colors.grey[100] : Colors.grey[700]), ), ); } @@ -169,9 +143,7 @@ class SharedLinkItem extends ConsumerWidget { color: isDarkMode ? Colors.black : Colors.white, ), ), - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(25)), - ), + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(25))), ), ); } @@ -181,8 +153,7 @@ class SharedLinkItem extends ConsumerWidget { children: [ if (sharedLink.allowUpload) buildInfoChip("upload".tr()), if (sharedLink.allowDownload) buildInfoChip("download".tr()), - if (sharedLink.showMetadata) - buildInfoChip("shared_link_info_chip_metadata".tr()), + if (sharedLink.showMetadata) buildInfoChip("shared_link_info_chip_metadata".tr()), ], ); } @@ -197,8 +168,7 @@ class SharedLinkItem extends ConsumerWidget { iconSize: actionIconSize, icon: const Icon(Icons.delete_outline), style: const ButtonStyle( - tapTargetSize: - MaterialTapTargetSize.shrinkWrap, // the '2023' part + tapTargetSize: MaterialTapTargetSize.shrinkWrap, // the '2023' part ), onPressed: deleteShareLink, ), @@ -208,11 +178,9 @@ class SharedLinkItem extends ConsumerWidget { iconSize: actionIconSize, icon: const Icon(Icons.edit_outlined), style: const ButtonStyle( - tapTargetSize: - MaterialTapTargetSize.shrinkWrap, // the '2023' part + tapTargetSize: MaterialTapTargetSize.shrinkWrap, // the '2023' part ), - onPressed: () => context - .pushRoute(SharedLinkEditRoute(existingLink: sharedLink)), + onPressed: () => context.pushRoute(SharedLinkEditRoute(existingLink: sharedLink)), ), IconButton( splashRadius: 25, @@ -220,8 +188,7 @@ class SharedLinkItem extends ConsumerWidget { iconSize: actionIconSize, icon: const Icon(Icons.copy_outlined), style: const ButtonStyle( - tapTargetSize: - MaterialTapTargetSize.shrinkWrap, // the '2023' part + tapTargetSize: MaterialTapTargetSize.shrinkWrap, // the '2023' part ), onPressed: copyShareLinkToClipboard, ), @@ -242,10 +209,7 @@ class SharedLinkItem extends ConsumerWidget { color: colorScheme.primary.withValues(alpha: 0.9), borderRadius: const BorderRadius.all(Radius.circular(10)), ), - textStyle: TextStyle( - color: isDarkMode ? Colors.black : Colors.white, - fontWeight: FontWeight.bold, - ), + textStyle: TextStyle(color: isDarkMode ? Colors.black : Colors.white, fontWeight: FontWeight.bold), message: sharedLink.title, preferBelow: false, triggerMode: TooltipTriggerMode.tap, @@ -270,23 +234,14 @@ class SharedLinkItem extends ConsumerWidget { color: colorScheme.primary.withValues(alpha: 0.9), borderRadius: const BorderRadius.all(Radius.circular(10)), ), - textStyle: TextStyle( - color: isDarkMode ? Colors.black : Colors.white, - fontWeight: FontWeight.bold, - ), + textStyle: TextStyle(color: isDarkMode ? Colors.black : Colors.white, fontWeight: FontWeight.bold), message: sharedLink.description ?? "", preferBelow: false, triggerMode: TooltipTriggerMode.tap, - child: Text( - sharedLink.description ?? "", - overflow: TextOverflow.ellipsis, - ), + child: Text(sharedLink.description ?? "", overflow: TextOverflow.ellipsis), ), ), - Padding( - padding: const EdgeInsets.only(right: 15), - child: buildSharedLinkActions(), - ), + Padding(padding: const EdgeInsets.only(right: 15), child: buildSharedLinkActions()), ], ), buildBottomInfo(), @@ -300,24 +255,13 @@ class SharedLinkItem extends ConsumerWidget { Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Padding( - padding: const EdgeInsets.only(left: 15), - child: buildThumbnail(), - ), + Padding(padding: const EdgeInsets.only(left: 15), child: buildThumbnail()), Expanded( - child: Padding( - padding: const EdgeInsets.only(left: 15), - child: buildSharedLinkDetails(), - ), + child: Padding(padding: const EdgeInsets.only(left: 15), child: buildSharedLinkDetails()), ), ], ), - const Padding( - padding: EdgeInsets.all(20), - child: Divider( - height: 0, - ), - ), + const Padding(padding: EdgeInsets.all(20), child: Divider(height: 0)), ], ); } diff --git a/mobile/makefile b/mobile/makefile index 37d33fa817..356649d5dd 100644 --- a/mobile/makefile +++ b/mobile/makefile @@ -1,4 +1,4 @@ -.PHONY: build watch create_app_icon create_splash build_release_android pigeon +.PHONY: build watch create_app_icon create_splash build_release_android pigeon test analyze format build: dart run build_runner build --delete-conflicting-outputs @@ -30,5 +30,13 @@ translation: dart format lib/generated/codegen_loader.g.dart dart format lib/generated/intl_keys.g.dart -build-beta: - flutter build apk --flavor beta --release +analyze: + dart analyze --fatal-infos + dcm analyze lib --fatal-style --fatal-warnings + +format: +# Ignore generated files manually until https://github.com/dart-lang/dart_style/issues/864 is resolved + dart format --set-exit-if-changed $$(find lib -name '*.dart' -not \( -name 'generated_plugin_registrant.dart' -o -name '*.g.dart' -o -name '*.drift.dart' \)) + +test: + flutter test diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index 28fa63ba84..c4349ff657 100644 --- a/mobile/openapi/README.md +++ b/mobile/openapi/README.md @@ -3,7 +3,7 @@ Immich API This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project: -- API version: 1.135.3 +- API version: 1.137.3 - Generator version: 7.8.0 - Build package: org.openapitools.codegen.languages.DartClientCodegen @@ -184,6 +184,7 @@ Class | Method | HTTP request | Description *SearchApi* | [**getSearchSuggestions**](doc//SearchApi.md#getsearchsuggestions) | **GET** /search/suggestions | *SearchApi* | [**searchAssetStatistics**](doc//SearchApi.md#searchassetstatistics) | **POST** /search/statistics | *SearchApi* | [**searchAssets**](doc//SearchApi.md#searchassets) | **POST** /search/metadata | +*SearchApi* | [**searchLargeAssets**](doc//SearchApi.md#searchlargeassets) | **POST** /search/large-assets | *SearchApi* | [**searchPerson**](doc//SearchApi.md#searchperson) | **GET** /search/person | *SearchApi* | [**searchPlaces**](doc//SearchApi.md#searchplaces) | **GET** /search/places | *SearchApi* | [**searchRandom**](doc//SearchApi.md#searchrandom) | **POST** /search/random | @@ -221,6 +222,7 @@ Class | Method | HTTP request | Description *StacksApi* | [**deleteStack**](doc//StacksApi.md#deletestack) | **DELETE** /stacks/{id} | *StacksApi* | [**deleteStacks**](doc//StacksApi.md#deletestacks) | **DELETE** /stacks | *StacksApi* | [**getStack**](doc//StacksApi.md#getstack) | **GET** /stacks/{id} | +*StacksApi* | [**removeAssetFromStack**](doc//StacksApi.md#removeassetfromstack) | **DELETE** /stacks/{id}/assets/{assetId} | *StacksApi* | [**searchStacks**](doc//StacksApi.md#searchstacks) | **GET** /stacks | *StacksApi* | [**updateStack**](doc//StacksApi.md#updatestack) | **PUT** /stacks/{id} | *SyncApi* | [**deleteSyncAck**](doc//SyncApi.md#deletesyncack) | **DELETE** /sync/ack | @@ -475,7 +477,10 @@ Class | Method | HTTP request | Description - [SyncAlbumV1](doc//SyncAlbumV1.md) - [SyncAssetDeleteV1](doc//SyncAssetDeleteV1.md) - [SyncAssetExifV1](doc//SyncAssetExifV1.md) + - [SyncAssetFaceDeleteV1](doc//SyncAssetFaceDeleteV1.md) + - [SyncAssetFaceV1](doc//SyncAssetFaceV1.md) - [SyncAssetV1](doc//SyncAssetV1.md) + - [SyncAuthUserV1](doc//SyncAuthUserV1.md) - [SyncEntityType](doc//SyncEntityType.md) - [SyncMemoryAssetDeleteV1](doc//SyncMemoryAssetDeleteV1.md) - [SyncMemoryAssetV1](doc//SyncMemoryAssetV1.md) @@ -553,6 +558,7 @@ Class | Method | HTTP request | Description - [UserAdminUpdateDto](doc//UserAdminUpdateDto.md) - [UserAvatarColor](doc//UserAvatarColor.md) - [UserLicense](doc//UserLicense.md) + - [UserMetadataKey](doc//UserMetadataKey.md) - [UserPreferencesResponseDto](doc//UserPreferencesResponseDto.md) - [UserPreferencesUpdateDto](doc//UserPreferencesUpdateDto.md) - [UserResponseDto](doc//UserResponseDto.md) diff --git a/mobile/openapi/lib/api.dart b/mobile/openapi/lib/api.dart index becafa06bf..8c1fa1a80a 100644 --- a/mobile/openapi/lib/api.dart +++ b/mobile/openapi/lib/api.dart @@ -257,7 +257,10 @@ part 'model/sync_album_user_v1.dart'; part 'model/sync_album_v1.dart'; part 'model/sync_asset_delete_v1.dart'; part 'model/sync_asset_exif_v1.dart'; +part 'model/sync_asset_face_delete_v1.dart'; +part 'model/sync_asset_face_v1.dart'; part 'model/sync_asset_v1.dart'; +part 'model/sync_auth_user_v1.dart'; part 'model/sync_entity_type.dart'; part 'model/sync_memory_asset_delete_v1.dart'; part 'model/sync_memory_asset_v1.dart'; @@ -335,6 +338,7 @@ part 'model/user_admin_response_dto.dart'; part 'model/user_admin_update_dto.dart'; part 'model/user_avatar_color.dart'; part 'model/user_license.dart'; +part 'model/user_metadata_key.dart'; part 'model/user_preferences_response_dto.dart'; part 'model/user_preferences_update_dto.dart'; part 'model/user_response_dto.dart'; diff --git a/mobile/openapi/lib/api/activities_api.dart b/mobile/openapi/lib/api/activities_api.dart index 5c83ba7db9..67015499fa 100644 --- a/mobile/openapi/lib/api/activities_api.dart +++ b/mobile/openapi/lib/api/activities_api.dart @@ -16,7 +16,10 @@ class ActivitiesApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /activities' operation and returns the [Response]. + /// This endpoint requires the `activity.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [ActivityCreateDto] activityCreateDto (required): @@ -45,6 +48,8 @@ class ActivitiesApi { ); } + /// This endpoint requires the `activity.create` permission. + /// /// Parameters: /// /// * [ActivityCreateDto] activityCreateDto (required): @@ -63,7 +68,10 @@ class ActivitiesApi { return null; } - /// Performs an HTTP 'DELETE /activities/{id}' operation and returns the [Response]. + /// This endpoint requires the `activity.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -93,6 +101,8 @@ class ActivitiesApi { ); } + /// This endpoint requires the `activity.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -103,7 +113,10 @@ class ActivitiesApi { } } - /// Performs an HTTP 'GET /activities' operation and returns the [Response]. + /// This endpoint requires the `activity.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] albumId (required): @@ -154,6 +167,8 @@ class ActivitiesApi { ); } + /// This endpoint requires the `activity.read` permission. + /// /// Parameters: /// /// * [String] albumId (required): @@ -183,7 +198,10 @@ class ActivitiesApi { return null; } - /// Performs an HTTP 'GET /activities/statistics' operation and returns the [Response]. + /// This endpoint requires the `activity.statistics` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] albumId (required): @@ -219,6 +237,8 @@ class ActivitiesApi { ); } + /// This endpoint requires the `activity.statistics` permission. + /// /// Parameters: /// /// * [String] albumId (required): diff --git a/mobile/openapi/lib/api/albums_api.dart b/mobile/openapi/lib/api/albums_api.dart index a8c518ace2..10674b894f 100644 --- a/mobile/openapi/lib/api/albums_api.dart +++ b/mobile/openapi/lib/api/albums_api.dart @@ -16,7 +16,10 @@ class AlbumsApi { final ApiClient apiClient; - /// Performs an HTTP 'PUT /albums/{id}/assets' operation and returns the [Response]. + /// This endpoint requires the `albumAsset.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -24,7 +27,9 @@ class AlbumsApi { /// * [BulkIdsDto] bulkIdsDto (required): /// /// * [String] key: - Future addAssetsToAlbumWithHttpInfo(String id, BulkIdsDto bulkIdsDto, { String? key, }) async { + /// + /// * [String] slug: + Future addAssetsToAlbumWithHttpInfo(String id, BulkIdsDto bulkIdsDto, { String? key, String? slug, }) async { // ignore: prefer_const_declarations final apiPath = r'/albums/{id}/assets' .replaceAll('{id}', id); @@ -39,6 +44,9 @@ class AlbumsApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = ['application/json']; @@ -54,6 +62,8 @@ class AlbumsApi { ); } + /// This endpoint requires the `albumAsset.create` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -61,8 +71,10 @@ class AlbumsApi { /// * [BulkIdsDto] bulkIdsDto (required): /// /// * [String] key: - Future?> addAssetsToAlbum(String id, BulkIdsDto bulkIdsDto, { String? key, }) async { - final response = await addAssetsToAlbumWithHttpInfo(id, bulkIdsDto, key: key, ); + /// + /// * [String] slug: + Future?> addAssetsToAlbum(String id, BulkIdsDto bulkIdsDto, { String? key, String? slug, }) async { + final response = await addAssetsToAlbumWithHttpInfo(id, bulkIdsDto, key: key, slug: slug, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -79,7 +91,10 @@ class AlbumsApi { return null; } - /// Performs an HTTP 'PUT /albums/{id}/users' operation and returns the [Response]. + /// This endpoint requires the `albumUser.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -111,6 +126,8 @@ class AlbumsApi { ); } + /// This endpoint requires the `albumUser.create` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -131,7 +148,10 @@ class AlbumsApi { return null; } - /// Performs an HTTP 'POST /albums' operation and returns the [Response]. + /// This endpoint requires the `album.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [CreateAlbumDto] createAlbumDto (required): @@ -160,6 +180,8 @@ class AlbumsApi { ); } + /// This endpoint requires the `album.create` permission. + /// /// Parameters: /// /// * [CreateAlbumDto] createAlbumDto (required): @@ -178,7 +200,10 @@ class AlbumsApi { return null; } - /// Performs an HTTP 'DELETE /albums/{id}' operation and returns the [Response]. + /// This endpoint requires the `album.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -208,6 +233,8 @@ class AlbumsApi { ); } + /// This endpoint requires the `album.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -218,15 +245,20 @@ class AlbumsApi { } } - /// Performs an HTTP 'GET /albums/{id}' operation and returns the [Response]. + /// This endpoint requires the `album.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): /// /// * [String] key: /// + /// * [String] slug: + /// /// * [bool] withoutAssets: - Future getAlbumInfoWithHttpInfo(String id, { String? key, bool? withoutAssets, }) async { + Future getAlbumInfoWithHttpInfo(String id, { String? key, String? slug, bool? withoutAssets, }) async { // ignore: prefer_const_declarations final apiPath = r'/albums/{id}' .replaceAll('{id}', id); @@ -241,6 +273,9 @@ class AlbumsApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } if (withoutAssets != null) { queryParams.addAll(_queryParams('', 'withoutAssets', withoutAssets)); } @@ -259,15 +294,19 @@ class AlbumsApi { ); } + /// This endpoint requires the `album.read` permission. + /// /// Parameters: /// /// * [String] id (required): /// /// * [String] key: /// + /// * [String] slug: + /// /// * [bool] withoutAssets: - Future getAlbumInfo(String id, { String? key, bool? withoutAssets, }) async { - final response = await getAlbumInfoWithHttpInfo(id, key: key, withoutAssets: withoutAssets, ); + Future getAlbumInfo(String id, { String? key, String? slug, bool? withoutAssets, }) async { + final response = await getAlbumInfoWithHttpInfo(id, key: key, slug: slug, withoutAssets: withoutAssets, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -281,7 +320,9 @@ class AlbumsApi { return null; } - /// Performs an HTTP 'GET /albums/statistics' operation and returns the [Response]. + /// This endpoint requires the `album.statistics` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getAlbumStatisticsWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/albums/statistics'; @@ -307,6 +348,7 @@ class AlbumsApi { ); } + /// This endpoint requires the `album.statistics` permission. Future getAlbumStatistics() async { final response = await getAlbumStatisticsWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -322,7 +364,10 @@ class AlbumsApi { return null; } - /// Performs an HTTP 'GET /albums' operation and returns the [Response]. + /// This endpoint requires the `album.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] assetId: @@ -361,6 +406,8 @@ class AlbumsApi { ); } + /// This endpoint requires the `album.read` permission. + /// /// Parameters: /// /// * [String] assetId: @@ -385,7 +432,10 @@ class AlbumsApi { return null; } - /// Performs an HTTP 'DELETE /albums/{id}/assets' operation and returns the [Response]. + /// This endpoint requires the `albumAsset.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -417,6 +467,8 @@ class AlbumsApi { ); } + /// This endpoint requires the `albumAsset.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -440,7 +492,10 @@ class AlbumsApi { return null; } - /// Performs an HTTP 'DELETE /albums/{id}/user/{userId}' operation and returns the [Response]. + /// This endpoint requires the `albumUser.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -473,6 +528,8 @@ class AlbumsApi { ); } + /// This endpoint requires the `albumUser.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -485,7 +542,10 @@ class AlbumsApi { } } - /// Performs an HTTP 'PATCH /albums/{id}' operation and returns the [Response]. + /// This endpoint requires the `album.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -517,6 +577,8 @@ class AlbumsApi { ); } + /// This endpoint requires the `album.update` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -537,7 +599,10 @@ class AlbumsApi { return null; } - /// Performs an HTTP 'PUT /albums/{id}/user/{userId}' operation and returns the [Response]. + /// This endpoint requires the `albumUser.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -572,6 +637,8 @@ class AlbumsApi { ); } + /// This endpoint requires the `albumUser.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/api_keys_api.dart b/mobile/openapi/lib/api/api_keys_api.dart index cf54ac5c04..e86c63bc6e 100644 --- a/mobile/openapi/lib/api/api_keys_api.dart +++ b/mobile/openapi/lib/api/api_keys_api.dart @@ -16,7 +16,10 @@ class APIKeysApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /api-keys' operation and returns the [Response]. + /// This endpoint requires the `apiKey.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [APIKeyCreateDto] aPIKeyCreateDto (required): @@ -45,6 +48,8 @@ class APIKeysApi { ); } + /// This endpoint requires the `apiKey.create` permission. + /// /// Parameters: /// /// * [APIKeyCreateDto] aPIKeyCreateDto (required): @@ -63,7 +68,10 @@ class APIKeysApi { return null; } - /// Performs an HTTP 'DELETE /api-keys/{id}' operation and returns the [Response]. + /// This endpoint requires the `apiKey.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -93,6 +101,8 @@ class APIKeysApi { ); } + /// This endpoint requires the `apiKey.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -103,7 +113,10 @@ class APIKeysApi { } } - /// Performs an HTTP 'GET /api-keys/{id}' operation and returns the [Response]. + /// This endpoint requires the `apiKey.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -133,6 +146,8 @@ class APIKeysApi { ); } + /// This endpoint requires the `apiKey.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -151,7 +166,9 @@ class APIKeysApi { return null; } - /// Performs an HTTP 'GET /api-keys' operation and returns the [Response]. + /// This endpoint requires the `apiKey.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getApiKeysWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/api-keys'; @@ -177,6 +194,7 @@ class APIKeysApi { ); } + /// This endpoint requires the `apiKey.read` permission. Future?> getApiKeys() async { final response = await getApiKeysWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -195,7 +213,10 @@ class APIKeysApi { return null; } - /// Performs an HTTP 'PUT /api-keys/{id}' operation and returns the [Response]. + /// This endpoint requires the `apiKey.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -227,6 +248,8 @@ class APIKeysApi { ); } + /// This endpoint requires the `apiKey.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/assets_api.dart b/mobile/openapi/lib/api/assets_api.dart index db6a2e78a3..c0de1a0801 100644 --- a/mobile/openapi/lib/api/assets_api.dart +++ b/mobile/openapi/lib/api/assets_api.dart @@ -128,7 +128,10 @@ class AssetsApi { return null; } - /// Performs an HTTP 'DELETE /assets' operation and returns the [Response]. + /// This endpoint requires the `asset.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [AssetBulkDeleteDto] assetBulkDeleteDto (required): @@ -157,6 +160,8 @@ class AssetsApi { ); } + /// This endpoint requires the `asset.delete` permission. + /// /// Parameters: /// /// * [AssetBulkDeleteDto] assetBulkDeleteDto (required): @@ -167,13 +172,18 @@ class AssetsApi { } } - /// Performs an HTTP 'GET /assets/{id}/original' operation and returns the [Response]. + /// This endpoint requires the `asset.download` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): /// /// * [String] key: - Future downloadAssetWithHttpInfo(String id, { String? key, }) async { + /// + /// * [String] slug: + Future downloadAssetWithHttpInfo(String id, { String? key, String? slug, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/{id}/original' .replaceAll('{id}', id); @@ -188,6 +198,9 @@ class AssetsApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = []; @@ -203,13 +216,17 @@ class AssetsApi { ); } + /// This endpoint requires the `asset.download` permission. + /// /// Parameters: /// /// * [String] id (required): /// /// * [String] key: - Future downloadAsset(String id, { String? key, }) async { - final response = await downloadAssetWithHttpInfo(id, key: key, ); + /// + /// * [String] slug: + Future downloadAsset(String id, { String? key, String? slug, }) async { + final response = await downloadAssetWithHttpInfo(id, key: key, slug: slug, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -283,13 +300,18 @@ class AssetsApi { return null; } - /// Performs an HTTP 'GET /assets/{id}' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): /// /// * [String] key: - Future getAssetInfoWithHttpInfo(String id, { String? key, }) async { + /// + /// * [String] slug: + Future getAssetInfoWithHttpInfo(String id, { String? key, String? slug, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/{id}' .replaceAll('{id}', id); @@ -304,6 +326,9 @@ class AssetsApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = []; @@ -319,13 +344,17 @@ class AssetsApi { ); } + /// This endpoint requires the `asset.read` permission. + /// /// Parameters: /// /// * [String] id (required): /// /// * [String] key: - Future getAssetInfo(String id, { String? key, }) async { - final response = await getAssetInfoWithHttpInfo(id, key: key, ); + /// + /// * [String] slug: + Future getAssetInfo(String id, { String? key, String? slug, }) async { + final response = await getAssetInfoWithHttpInfo(id, key: key, slug: slug, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -339,7 +368,10 @@ class AssetsApi { return null; } - /// Performs an HTTP 'GET /assets/statistics' operation and returns the [Response]. + /// This endpoint requires the `asset.statistics` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [bool] isFavorite: @@ -382,6 +414,8 @@ class AssetsApi { ); } + /// This endpoint requires the `asset.statistics` permission. + /// /// Parameters: /// /// * [bool] isFavorite: @@ -404,7 +438,7 @@ class AssetsApi { return null; } - /// This property was deprecated in v1.116.0 + /// This property was deprecated in v1.116.0. This endpoint requires the `asset.read` permission. /// /// Note: This method returns the HTTP [Response]. /// @@ -440,7 +474,7 @@ class AssetsApi { ); } - /// This property was deprecated in v1.116.0 + /// This property was deprecated in v1.116.0. This endpoint requires the `asset.read` permission. /// /// Parameters: /// @@ -463,13 +497,18 @@ class AssetsApi { return null; } - /// Performs an HTTP 'GET /assets/{id}/video/playback' operation and returns the [Response]. + /// This endpoint requires the `asset.view` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): /// /// * [String] key: - Future playAssetVideoWithHttpInfo(String id, { String? key, }) async { + /// + /// * [String] slug: + Future playAssetVideoWithHttpInfo(String id, { String? key, String? slug, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/{id}/video/playback' .replaceAll('{id}', id); @@ -484,6 +523,9 @@ class AssetsApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = []; @@ -499,13 +541,17 @@ class AssetsApi { ); } + /// This endpoint requires the `asset.view` permission. + /// /// Parameters: /// /// * [String] id (required): /// /// * [String] key: - Future playAssetVideo(String id, { String? key, }) async { - final response = await playAssetVideoWithHttpInfo(id, key: key, ); + /// + /// * [String] slug: + Future playAssetVideo(String id, { String? key, String? slug, }) async { + final response = await playAssetVideoWithHttpInfo(id, key: key, slug: slug, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -521,7 +567,7 @@ class AssetsApi { /// replaceAsset /// - /// Replace the asset with new file, without changing its id + /// Replace the asset with new file, without changing its id. This endpoint requires the `asset.replace` permission. /// /// Note: This method returns the HTTP [Response]. /// @@ -541,10 +587,12 @@ class AssetsApi { /// /// * [String] key: /// + /// * [String] slug: + /// /// * [String] duration: /// /// * [String] filename: - Future replaceAssetWithHttpInfo(String id, MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? duration, String? filename, }) async { + Future replaceAssetWithHttpInfo(String id, MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? slug, String? duration, String? filename, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/{id}/original' .replaceAll('{id}', id); @@ -559,6 +607,9 @@ class AssetsApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = ['multipart/form-data']; @@ -610,7 +661,7 @@ class AssetsApi { /// replaceAsset /// - /// Replace the asset with new file, without changing its id + /// Replace the asset with new file, without changing its id. This endpoint requires the `asset.replace` permission. /// /// Parameters: /// @@ -628,11 +679,13 @@ class AssetsApi { /// /// * [String] key: /// + /// * [String] slug: + /// /// * [String] duration: /// /// * [String] filename: - Future replaceAsset(String id, MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? duration, String? filename, }) async { - final response = await replaceAssetWithHttpInfo(id, assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key: key, duration: duration, filename: filename, ); + Future replaceAsset(String id, MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? slug, String? duration, String? filename, }) async { + final response = await replaceAssetWithHttpInfo(id, assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key: key, slug: slug, duration: duration, filename: filename, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -685,7 +738,10 @@ class AssetsApi { } } - /// Performs an HTTP 'PUT /assets/{id}' operation and returns the [Response]. + /// This endpoint requires the `asset.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -717,6 +773,8 @@ class AssetsApi { ); } + /// This endpoint requires the `asset.update` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -737,7 +795,10 @@ class AssetsApi { return null; } - /// Performs an HTTP 'PUT /assets' operation and returns the [Response]. + /// This endpoint requires the `asset.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [AssetBulkUpdateDto] assetBulkUpdateDto (required): @@ -766,6 +827,8 @@ class AssetsApi { ); } + /// This endpoint requires the `asset.update` permission. + /// /// Parameters: /// /// * [AssetBulkUpdateDto] assetBulkUpdateDto (required): @@ -776,7 +839,10 @@ class AssetsApi { } } - /// Performs an HTTP 'POST /assets' operation and returns the [Response]. + /// This endpoint requires the `asset.upload` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [MultipartFile] assetData (required): @@ -791,6 +857,8 @@ class AssetsApi { /// /// * [String] key: /// + /// * [String] slug: + /// /// * [String] xImmichChecksum: /// sha1 checksum that can be used for duplicate detection before the file is uploaded /// @@ -805,7 +873,7 @@ class AssetsApi { /// * [MultipartFile] sidecarData: /// /// * [AssetVisibility] visibility: - Future uploadAssetWithHttpInfo(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? xImmichChecksum, String? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, MultipartFile? sidecarData, AssetVisibility? visibility, }) async { + Future uploadAssetWithHttpInfo(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? slug, String? xImmichChecksum, String? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, MultipartFile? sidecarData, AssetVisibility? visibility, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets'; @@ -819,6 +887,9 @@ class AssetsApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } if (xImmichChecksum != null) { headerParams[r'x-immich-checksum'] = parameterToString(xImmichChecksum); @@ -889,6 +960,8 @@ class AssetsApi { ); } + /// This endpoint requires the `asset.upload` permission. + /// /// Parameters: /// /// * [MultipartFile] assetData (required): @@ -903,6 +976,8 @@ class AssetsApi { /// /// * [String] key: /// + /// * [String] slug: + /// /// * [String] xImmichChecksum: /// sha1 checksum that can be used for duplicate detection before the file is uploaded /// @@ -917,8 +992,8 @@ class AssetsApi { /// * [MultipartFile] sidecarData: /// /// * [AssetVisibility] visibility: - Future uploadAsset(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? xImmichChecksum, String? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, MultipartFile? sidecarData, AssetVisibility? visibility, }) async { - final response = await uploadAssetWithHttpInfo(assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key: key, xImmichChecksum: xImmichChecksum, duration: duration, filename: filename, isFavorite: isFavorite, livePhotoVideoId: livePhotoVideoId, sidecarData: sidecarData, visibility: visibility, ); + Future uploadAsset(MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? slug, String? xImmichChecksum, String? duration, String? filename, bool? isFavorite, String? livePhotoVideoId, MultipartFile? sidecarData, AssetVisibility? visibility, }) async { + final response = await uploadAssetWithHttpInfo(assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key: key, slug: slug, xImmichChecksum: xImmichChecksum, duration: duration, filename: filename, isFavorite: isFavorite, livePhotoVideoId: livePhotoVideoId, sidecarData: sidecarData, visibility: visibility, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -932,7 +1007,10 @@ class AssetsApi { return null; } - /// Performs an HTTP 'GET /assets/{id}/thumbnail' operation and returns the [Response]. + /// This endpoint requires the `asset.view` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -940,7 +1018,9 @@ class AssetsApi { /// * [String] key: /// /// * [AssetMediaSize] size: - Future viewAssetWithHttpInfo(String id, { String? key, AssetMediaSize? size, }) async { + /// + /// * [String] slug: + Future viewAssetWithHttpInfo(String id, { String? key, AssetMediaSize? size, String? slug, }) async { // ignore: prefer_const_declarations final apiPath = r'/assets/{id}/thumbnail' .replaceAll('{id}', id); @@ -958,6 +1038,9 @@ class AssetsApi { if (size != null) { queryParams.addAll(_queryParams('', 'size', size)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = []; @@ -973,6 +1056,8 @@ class AssetsApi { ); } + /// This endpoint requires the `asset.view` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -980,8 +1065,10 @@ class AssetsApi { /// * [String] key: /// /// * [AssetMediaSize] size: - Future viewAsset(String id, { String? key, AssetMediaSize? size, }) async { - final response = await viewAssetWithHttpInfo(id, key: key, size: size, ); + /// + /// * [String] slug: + Future viewAsset(String id, { String? key, AssetMediaSize? size, String? slug, }) async { + final response = await viewAssetWithHttpInfo(id, key: key, size: size, slug: slug, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/authentication_api.dart b/mobile/openapi/lib/api/authentication_api.dart index 5482a9fc51..a74af33a43 100644 --- a/mobile/openapi/lib/api/authentication_api.dart +++ b/mobile/openapi/lib/api/authentication_api.dart @@ -16,7 +16,10 @@ class AuthenticationApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /auth/change-password' operation and returns the [Response]. + /// This endpoint requires the `auth.changePassword` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [ChangePasswordDto] changePasswordDto (required): @@ -45,6 +48,8 @@ class AuthenticationApi { ); } + /// This endpoint requires the `auth.changePassword` permission. + /// /// Parameters: /// /// * [ChangePasswordDto] changePasswordDto (required): @@ -63,7 +68,10 @@ class AuthenticationApi { return null; } - /// Performs an HTTP 'PUT /auth/pin-code' operation and returns the [Response]. + /// This endpoint requires the `pinCode.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [PinCodeChangeDto] pinCodeChangeDto (required): @@ -92,6 +100,8 @@ class AuthenticationApi { ); } + /// This endpoint requires the `pinCode.update` permission. + /// /// Parameters: /// /// * [PinCodeChangeDto] pinCodeChangeDto (required): @@ -264,7 +274,10 @@ class AuthenticationApi { return null; } - /// Performs an HTTP 'DELETE /auth/pin-code' operation and returns the [Response]. + /// This endpoint requires the `pinCode.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [PinCodeResetDto] pinCodeResetDto (required): @@ -293,6 +306,8 @@ class AuthenticationApi { ); } + /// This endpoint requires the `pinCode.delete` permission. + /// /// Parameters: /// /// * [PinCodeResetDto] pinCodeResetDto (required): @@ -303,7 +318,10 @@ class AuthenticationApi { } } - /// Performs an HTTP 'POST /auth/pin-code' operation and returns the [Response]. + /// This endpoint requires the `pinCode.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [PinCodeSetupDto] pinCodeSetupDto (required): @@ -332,6 +350,8 @@ class AuthenticationApi { ); } + /// This endpoint requires the `pinCode.create` permission. + /// /// Parameters: /// /// * [PinCodeSetupDto] pinCodeSetupDto (required): diff --git a/mobile/openapi/lib/api/deprecated_api.dart b/mobile/openapi/lib/api/deprecated_api.dart index 7aa9662c23..f9a496b990 100644 --- a/mobile/openapi/lib/api/deprecated_api.dart +++ b/mobile/openapi/lib/api/deprecated_api.dart @@ -16,7 +16,7 @@ class DeprecatedApi { final ApiClient apiClient; - /// This property was deprecated in v1.116.0 + /// This property was deprecated in v1.116.0. This endpoint requires the `asset.read` permission. /// /// Note: This method returns the HTTP [Response]. /// @@ -52,7 +52,7 @@ class DeprecatedApi { ); } - /// This property was deprecated in v1.116.0 + /// This property was deprecated in v1.116.0. This endpoint requires the `asset.read` permission. /// /// Parameters: /// diff --git a/mobile/openapi/lib/api/download_api.dart b/mobile/openapi/lib/api/download_api.dart index 3b11c2f630..62c97bfc9c 100644 --- a/mobile/openapi/lib/api/download_api.dart +++ b/mobile/openapi/lib/api/download_api.dart @@ -16,13 +16,18 @@ class DownloadApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /download/archive' operation and returns the [Response]. + /// This endpoint requires the `asset.download` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [AssetIdsDto] assetIdsDto (required): /// /// * [String] key: - Future downloadArchiveWithHttpInfo(AssetIdsDto assetIdsDto, { String? key, }) async { + /// + /// * [String] slug: + Future downloadArchiveWithHttpInfo(AssetIdsDto assetIdsDto, { String? key, String? slug, }) async { // ignore: prefer_const_declarations final apiPath = r'/download/archive'; @@ -36,6 +41,9 @@ class DownloadApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = ['application/json']; @@ -51,13 +59,17 @@ class DownloadApi { ); } + /// This endpoint requires the `asset.download` permission. + /// /// Parameters: /// /// * [AssetIdsDto] assetIdsDto (required): /// /// * [String] key: - Future downloadArchive(AssetIdsDto assetIdsDto, { String? key, }) async { - final response = await downloadArchiveWithHttpInfo(assetIdsDto, key: key, ); + /// + /// * [String] slug: + Future downloadArchive(AssetIdsDto assetIdsDto, { String? key, String? slug, }) async { + final response = await downloadArchiveWithHttpInfo(assetIdsDto, key: key, slug: slug, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -71,13 +83,18 @@ class DownloadApi { return null; } - /// Performs an HTTP 'POST /download/info' operation and returns the [Response]. + /// This endpoint requires the `asset.download` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [DownloadInfoDto] downloadInfoDto (required): /// /// * [String] key: - Future getDownloadInfoWithHttpInfo(DownloadInfoDto downloadInfoDto, { String? key, }) async { + /// + /// * [String] slug: + Future getDownloadInfoWithHttpInfo(DownloadInfoDto downloadInfoDto, { String? key, String? slug, }) async { // ignore: prefer_const_declarations final apiPath = r'/download/info'; @@ -91,6 +108,9 @@ class DownloadApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = ['application/json']; @@ -106,13 +126,17 @@ class DownloadApi { ); } + /// This endpoint requires the `asset.download` permission. + /// /// Parameters: /// /// * [DownloadInfoDto] downloadInfoDto (required): /// /// * [String] key: - Future getDownloadInfo(DownloadInfoDto downloadInfoDto, { String? key, }) async { - final response = await getDownloadInfoWithHttpInfo(downloadInfoDto, key: key, ); + /// + /// * [String] slug: + Future getDownloadInfo(DownloadInfoDto downloadInfoDto, { String? key, String? slug, }) async { + final response = await getDownloadInfoWithHttpInfo(downloadInfoDto, key: key, slug: slug, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/duplicates_api.dart b/mobile/openapi/lib/api/duplicates_api.dart index d8b45d21a2..9df6e46586 100644 --- a/mobile/openapi/lib/api/duplicates_api.dart +++ b/mobile/openapi/lib/api/duplicates_api.dart @@ -16,7 +16,10 @@ class DuplicatesApi { final ApiClient apiClient; - /// Performs an HTTP 'DELETE /duplicates/{id}' operation and returns the [Response]. + /// This endpoint requires the `duplicate.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -46,6 +49,8 @@ class DuplicatesApi { ); } + /// This endpoint requires the `duplicate.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -56,7 +61,10 @@ class DuplicatesApi { } } - /// Performs an HTTP 'DELETE /duplicates' operation and returns the [Response]. + /// This endpoint requires the `duplicate.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): @@ -85,6 +93,8 @@ class DuplicatesApi { ); } + /// This endpoint requires the `duplicate.delete` permission. + /// /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): @@ -95,7 +105,9 @@ class DuplicatesApi { } } - /// Performs an HTTP 'GET /duplicates' operation and returns the [Response]. + /// This endpoint requires the `duplicate.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getAssetDuplicatesWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/duplicates'; @@ -121,6 +133,7 @@ class DuplicatesApi { ); } + /// This endpoint requires the `duplicate.read` permission. Future?> getAssetDuplicates() async { final response = await getAssetDuplicatesWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { diff --git a/mobile/openapi/lib/api/faces_api.dart b/mobile/openapi/lib/api/faces_api.dart index 44e3d53f8e..2f8e6be60d 100644 --- a/mobile/openapi/lib/api/faces_api.dart +++ b/mobile/openapi/lib/api/faces_api.dart @@ -16,7 +16,10 @@ class FacesApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /faces' operation and returns the [Response]. + /// This endpoint requires the `face.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [AssetFaceCreateDto] assetFaceCreateDto (required): @@ -45,6 +48,8 @@ class FacesApi { ); } + /// This endpoint requires the `face.create` permission. + /// /// Parameters: /// /// * [AssetFaceCreateDto] assetFaceCreateDto (required): @@ -55,7 +60,10 @@ class FacesApi { } } - /// Performs an HTTP 'DELETE /faces/{id}' operation and returns the [Response]. + /// This endpoint requires the `face.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -87,6 +95,8 @@ class FacesApi { ); } + /// This endpoint requires the `face.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -99,7 +109,10 @@ class FacesApi { } } - /// Performs an HTTP 'GET /faces' operation and returns the [Response]. + /// This endpoint requires the `face.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -130,6 +143,8 @@ class FacesApi { ); } + /// This endpoint requires the `face.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -151,7 +166,10 @@ class FacesApi { return null; } - /// Performs an HTTP 'PUT /faces/{id}' operation and returns the [Response]. + /// This endpoint requires the `face.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -183,6 +201,8 @@ class FacesApi { ); } + /// This endpoint requires the `face.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/jobs_api.dart b/mobile/openapi/lib/api/jobs_api.dart index 182bb14e4f..4c935828a0 100644 --- a/mobile/openapi/lib/api/jobs_api.dart +++ b/mobile/openapi/lib/api/jobs_api.dart @@ -16,7 +16,10 @@ class JobsApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /jobs' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `job.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [JobCreateDto] jobCreateDto (required): @@ -45,6 +48,8 @@ class JobsApi { ); } + /// This endpoint is an admin-only route, and requires the `job.create` permission. + /// /// Parameters: /// /// * [JobCreateDto] jobCreateDto (required): @@ -55,7 +60,9 @@ class JobsApi { } } - /// Performs an HTTP 'GET /jobs' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `job.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getAllJobsStatusWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/jobs'; @@ -81,6 +88,7 @@ class JobsApi { ); } + /// This endpoint is an admin-only route, and requires the `job.read` permission. Future getAllJobsStatus() async { final response = await getAllJobsStatusWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -96,7 +104,10 @@ class JobsApi { return null; } - /// Performs an HTTP 'PUT /jobs/{id}' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `job.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [JobName] id (required): @@ -128,6 +139,8 @@ class JobsApi { ); } + /// This endpoint is an admin-only route, and requires the `job.create` permission. + /// /// Parameters: /// /// * [JobName] id (required): diff --git a/mobile/openapi/lib/api/libraries_api.dart b/mobile/openapi/lib/api/libraries_api.dart index 86acce76b4..9258f8e3eb 100644 --- a/mobile/openapi/lib/api/libraries_api.dart +++ b/mobile/openapi/lib/api/libraries_api.dart @@ -16,7 +16,10 @@ class LibrariesApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /libraries' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `library.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [CreateLibraryDto] createLibraryDto (required): @@ -45,6 +48,8 @@ class LibrariesApi { ); } + /// This endpoint is an admin-only route, and requires the `library.create` permission. + /// /// Parameters: /// /// * [CreateLibraryDto] createLibraryDto (required): @@ -63,7 +68,10 @@ class LibrariesApi { return null; } - /// Performs an HTTP 'DELETE /libraries/{id}' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `library.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -93,6 +101,8 @@ class LibrariesApi { ); } + /// This endpoint is an admin-only route, and requires the `library.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -103,7 +113,9 @@ class LibrariesApi { } } - /// Performs an HTTP 'GET /libraries' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `library.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getAllLibrariesWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/libraries'; @@ -129,6 +141,7 @@ class LibrariesApi { ); } + /// This endpoint is an admin-only route, and requires the `library.read` permission. Future?> getAllLibraries() async { final response = await getAllLibrariesWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -147,7 +160,10 @@ class LibrariesApi { return null; } - /// Performs an HTTP 'GET /libraries/{id}' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `library.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -177,6 +193,8 @@ class LibrariesApi { ); } + /// This endpoint is an admin-only route, and requires the `library.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -195,7 +213,10 @@ class LibrariesApi { return null; } - /// Performs an HTTP 'GET /libraries/{id}/statistics' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `library.statistics` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -225,6 +246,8 @@ class LibrariesApi { ); } + /// This endpoint is an admin-only route, and requires the `library.statistics` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -243,7 +266,10 @@ class LibrariesApi { return null; } - /// Performs an HTTP 'POST /libraries/{id}/scan' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `library.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -273,6 +299,8 @@ class LibrariesApi { ); } + /// This endpoint is an admin-only route, and requires the `library.update` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -283,7 +311,10 @@ class LibrariesApi { } } - /// Performs an HTTP 'PUT /libraries/{id}' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `library.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -315,6 +346,8 @@ class LibrariesApi { ); } + /// This endpoint is an admin-only route, and requires the `library.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/map_api.dart b/mobile/openapi/lib/api/map_api.dart index ffe72df453..da4f3dffcc 100644 --- a/mobile/openapi/lib/api/map_api.dart +++ b/mobile/openapi/lib/api/map_api.dart @@ -19,18 +19,18 @@ class MapApi { /// Performs an HTTP 'GET /map/markers' operation and returns the [Response]. /// Parameters: /// - /// * [DateTime] fileCreatedAfter: - /// - /// * [DateTime] fileCreatedBefore: - /// /// * [bool] isArchived: /// /// * [bool] isFavorite: /// + /// * [DateTime] fileCreatedAfter: + /// + /// * [DateTime] fileCreatedBefore: + /// /// * [bool] withPartners: /// /// * [bool] withSharedAlbums: - Future getMapMarkersWithHttpInfo({ DateTime? fileCreatedAfter, DateTime? fileCreatedBefore, bool? isArchived, bool? isFavorite, bool? withPartners, bool? withSharedAlbums, }) async { + Future getMapMarkersWithHttpInfo({ bool? isArchived, bool? isFavorite, DateTime? fileCreatedAfter, DateTime? fileCreatedBefore, bool? withPartners, bool? withSharedAlbums, }) async { // ignore: prefer_const_declarations final apiPath = r'/map/markers'; @@ -41,18 +41,18 @@ class MapApi { final headerParams = {}; final formParams = {}; - if (fileCreatedAfter != null) { - queryParams.addAll(_queryParams('', 'fileCreatedAfter', fileCreatedAfter)); - } - if (fileCreatedBefore != null) { - queryParams.addAll(_queryParams('', 'fileCreatedBefore', fileCreatedBefore)); - } if (isArchived != null) { queryParams.addAll(_queryParams('', 'isArchived', isArchived)); } if (isFavorite != null) { queryParams.addAll(_queryParams('', 'isFavorite', isFavorite)); } + if (fileCreatedAfter != null) { + queryParams.addAll(_queryParams('', 'fileCreatedAfter', fileCreatedAfter)); + } + if (fileCreatedBefore != null) { + queryParams.addAll(_queryParams('', 'fileCreatedBefore', fileCreatedBefore)); + } if (withPartners != null) { queryParams.addAll(_queryParams('', 'withPartners', withPartners)); } @@ -76,19 +76,19 @@ class MapApi { /// Parameters: /// - /// * [DateTime] fileCreatedAfter: - /// - /// * [DateTime] fileCreatedBefore: - /// /// * [bool] isArchived: /// /// * [bool] isFavorite: /// + /// * [DateTime] fileCreatedAfter: + /// + /// * [DateTime] fileCreatedBefore: + /// /// * [bool] withPartners: /// /// * [bool] withSharedAlbums: - Future?> getMapMarkers({ DateTime? fileCreatedAfter, DateTime? fileCreatedBefore, bool? isArchived, bool? isFavorite, bool? withPartners, bool? withSharedAlbums, }) async { - final response = await getMapMarkersWithHttpInfo( fileCreatedAfter: fileCreatedAfter, fileCreatedBefore: fileCreatedBefore, isArchived: isArchived, isFavorite: isFavorite, withPartners: withPartners, withSharedAlbums: withSharedAlbums, ); + Future?> getMapMarkers({ bool? isArchived, bool? isFavorite, DateTime? fileCreatedAfter, DateTime? fileCreatedBefore, bool? withPartners, bool? withSharedAlbums, }) async { + final response = await getMapMarkersWithHttpInfo( isArchived: isArchived, isFavorite: isFavorite, fileCreatedAfter: fileCreatedAfter, fileCreatedBefore: fileCreatedBefore, withPartners: withPartners, withSharedAlbums: withSharedAlbums, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/memories_api.dart b/mobile/openapi/lib/api/memories_api.dart index 9b62cce9c0..f9280101e6 100644 --- a/mobile/openapi/lib/api/memories_api.dart +++ b/mobile/openapi/lib/api/memories_api.dart @@ -16,7 +16,10 @@ class MemoriesApi { final ApiClient apiClient; - /// Performs an HTTP 'PUT /memories/{id}/assets' operation and returns the [Response]. + /// This endpoint requires the `memoryAsset.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -48,6 +51,8 @@ class MemoriesApi { ); } + /// This endpoint requires the `memoryAsset.create` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -71,7 +76,10 @@ class MemoriesApi { return null; } - /// Performs an HTTP 'POST /memories' operation and returns the [Response]. + /// This endpoint requires the `memory.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [MemoryCreateDto] memoryCreateDto (required): @@ -100,6 +108,8 @@ class MemoriesApi { ); } + /// This endpoint requires the `memory.create` permission. + /// /// Parameters: /// /// * [MemoryCreateDto] memoryCreateDto (required): @@ -118,7 +128,10 @@ class MemoriesApi { return null; } - /// Performs an HTTP 'DELETE /memories/{id}' operation and returns the [Response]. + /// This endpoint requires the `memory.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -148,6 +161,8 @@ class MemoriesApi { ); } + /// This endpoint requires the `memory.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -158,7 +173,10 @@ class MemoriesApi { } } - /// Performs an HTTP 'GET /memories/{id}' operation and returns the [Response]. + /// This endpoint requires the `memory.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -188,6 +206,8 @@ class MemoriesApi { ); } + /// This endpoint requires the `memory.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -206,7 +226,10 @@ class MemoriesApi { return null; } - /// Performs an HTTP 'GET /memories/statistics' operation and returns the [Response]. + /// This endpoint requires the `memory.statistics` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [DateTime] for_: @@ -254,6 +277,8 @@ class MemoriesApi { ); } + /// This endpoint requires the `memory.statistics` permission. + /// /// Parameters: /// /// * [DateTime] for_: @@ -278,7 +303,10 @@ class MemoriesApi { return null; } - /// Performs an HTTP 'DELETE /memories/{id}/assets' operation and returns the [Response]. + /// This endpoint requires the `memoryAsset.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -310,6 +338,8 @@ class MemoriesApi { ); } + /// This endpoint requires the `memoryAsset.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -333,7 +363,10 @@ class MemoriesApi { return null; } - /// Performs an HTTP 'GET /memories' operation and returns the [Response]. + /// This endpoint requires the `memory.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [DateTime] for_: @@ -381,6 +414,8 @@ class MemoriesApi { ); } + /// This endpoint requires the `memory.read` permission. + /// /// Parameters: /// /// * [DateTime] for_: @@ -408,7 +443,10 @@ class MemoriesApi { return null; } - /// Performs an HTTP 'PUT /memories/{id}' operation and returns the [Response]. + /// This endpoint requires the `memory.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -440,6 +478,8 @@ class MemoriesApi { ); } + /// This endpoint requires the `memory.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/notifications_api.dart b/mobile/openapi/lib/api/notifications_api.dart index 501cc70a29..1d276efaaf 100644 --- a/mobile/openapi/lib/api/notifications_api.dart +++ b/mobile/openapi/lib/api/notifications_api.dart @@ -16,7 +16,10 @@ class NotificationsApi { final ApiClient apiClient; - /// Performs an HTTP 'DELETE /notifications/{id}' operation and returns the [Response]. + /// This endpoint requires the `notification.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -46,6 +49,8 @@ class NotificationsApi { ); } + /// This endpoint requires the `notification.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -56,7 +61,10 @@ class NotificationsApi { } } - /// Performs an HTTP 'DELETE /notifications' operation and returns the [Response]. + /// This endpoint requires the `notification.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [NotificationDeleteAllDto] notificationDeleteAllDto (required): @@ -85,6 +93,8 @@ class NotificationsApi { ); } + /// This endpoint requires the `notification.delete` permission. + /// /// Parameters: /// /// * [NotificationDeleteAllDto] notificationDeleteAllDto (required): @@ -95,7 +105,10 @@ class NotificationsApi { } } - /// Performs an HTTP 'GET /notifications/{id}' operation and returns the [Response]. + /// This endpoint requires the `notification.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -125,6 +138,8 @@ class NotificationsApi { ); } + /// This endpoint requires the `notification.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -143,7 +158,10 @@ class NotificationsApi { return null; } - /// Performs an HTTP 'GET /notifications' operation and returns the [Response]. + /// This endpoint requires the `notification.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id: @@ -191,6 +209,8 @@ class NotificationsApi { ); } + /// This endpoint requires the `notification.read` permission. + /// /// Parameters: /// /// * [String] id: @@ -218,7 +238,10 @@ class NotificationsApi { return null; } - /// Performs an HTTP 'PUT /notifications/{id}' operation and returns the [Response]. + /// This endpoint requires the `notification.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -250,6 +273,8 @@ class NotificationsApi { ); } + /// This endpoint requires the `notification.update` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -270,7 +295,10 @@ class NotificationsApi { return null; } - /// Performs an HTTP 'PUT /notifications' operation and returns the [Response]. + /// This endpoint requires the `notification.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [NotificationUpdateAllDto] notificationUpdateAllDto (required): @@ -299,6 +327,8 @@ class NotificationsApi { ); } + /// This endpoint requires the `notification.update` permission. + /// /// Parameters: /// /// * [NotificationUpdateAllDto] notificationUpdateAllDto (required): diff --git a/mobile/openapi/lib/api/partners_api.dart b/mobile/openapi/lib/api/partners_api.dart index 9f10ea4d1e..eb5d5f5806 100644 --- a/mobile/openapi/lib/api/partners_api.dart +++ b/mobile/openapi/lib/api/partners_api.dart @@ -16,7 +16,10 @@ class PartnersApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /partners/{id}' operation and returns the [Response]. + /// This endpoint requires the `partner.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -46,6 +49,8 @@ class PartnersApi { ); } + /// This endpoint requires the `partner.create` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -64,7 +69,10 @@ class PartnersApi { return null; } - /// Performs an HTTP 'GET /partners' operation and returns the [Response]. + /// This endpoint requires the `partner.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [PartnerDirection] direction (required): @@ -95,6 +103,8 @@ class PartnersApi { ); } + /// This endpoint requires the `partner.read` permission. + /// /// Parameters: /// /// * [PartnerDirection] direction (required): @@ -116,7 +126,10 @@ class PartnersApi { return null; } - /// Performs an HTTP 'DELETE /partners/{id}' operation and returns the [Response]. + /// This endpoint requires the `partner.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -146,6 +159,8 @@ class PartnersApi { ); } + /// This endpoint requires the `partner.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -156,7 +171,10 @@ class PartnersApi { } } - /// Performs an HTTP 'PUT /partners/{id}' operation and returns the [Response]. + /// This endpoint requires the `partner.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -188,6 +206,8 @@ class PartnersApi { ); } + /// This endpoint requires the `partner.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/people_api.dart b/mobile/openapi/lib/api/people_api.dart index 35dbac4e97..68c16785cc 100644 --- a/mobile/openapi/lib/api/people_api.dart +++ b/mobile/openapi/lib/api/people_api.dart @@ -16,7 +16,10 @@ class PeopleApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /people' operation and returns the [Response]. + /// This endpoint requires the `person.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [PersonCreateDto] personCreateDto (required): @@ -45,6 +48,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.create` permission. + /// /// Parameters: /// /// * [PersonCreateDto] personCreateDto (required): @@ -63,7 +68,10 @@ class PeopleApi { return null; } - /// Performs an HTTP 'DELETE /people' operation and returns the [Response]. + /// This endpoint requires the `person.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): @@ -92,6 +100,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.delete` permission. + /// /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): @@ -102,7 +112,10 @@ class PeopleApi { } } - /// Performs an HTTP 'DELETE /people/{id}' operation and returns the [Response]. + /// This endpoint requires the `person.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -132,6 +145,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -142,7 +157,10 @@ class PeopleApi { } } - /// Performs an HTTP 'GET /people' operation and returns the [Response]. + /// This endpoint requires the `person.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] closestAssetId: @@ -197,6 +215,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.read` permission. + /// /// Parameters: /// /// * [String] closestAssetId: @@ -225,7 +245,10 @@ class PeopleApi { return null; } - /// Performs an HTTP 'GET /people/{id}' operation and returns the [Response]. + /// This endpoint requires the `person.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -255,6 +278,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -273,7 +298,10 @@ class PeopleApi { return null; } - /// Performs an HTTP 'GET /people/{id}/statistics' operation and returns the [Response]. + /// This endpoint requires the `person.statistics` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -303,6 +331,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.statistics` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -321,7 +351,10 @@ class PeopleApi { return null; } - /// Performs an HTTP 'GET /people/{id}/thumbnail' operation and returns the [Response]. + /// This endpoint requires the `person.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -351,6 +384,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -369,7 +404,10 @@ class PeopleApi { return null; } - /// Performs an HTTP 'POST /people/{id}/merge' operation and returns the [Response]. + /// This endpoint requires the `person.merge` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -401,6 +439,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.merge` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -424,7 +464,10 @@ class PeopleApi { return null; } - /// Performs an HTTP 'PUT /people/{id}/reassign' operation and returns the [Response]. + /// This endpoint requires the `person.reassign` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -456,6 +499,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.reassign` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -479,7 +524,10 @@ class PeopleApi { return null; } - /// Performs an HTTP 'PUT /people' operation and returns the [Response]. + /// This endpoint requires the `person.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [PeopleUpdateDto] peopleUpdateDto (required): @@ -508,6 +556,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.update` permission. + /// /// Parameters: /// /// * [PeopleUpdateDto] peopleUpdateDto (required): @@ -529,7 +579,10 @@ class PeopleApi { return null; } - /// Performs an HTTP 'PUT /people/{id}' operation and returns the [Response]. + /// This endpoint requires the `person.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -561,6 +614,8 @@ class PeopleApi { ); } + /// This endpoint requires the `person.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/search_api.dart b/mobile/openapi/lib/api/search_api.dart index 5c7a8de59d..4d9e1172b8 100644 --- a/mobile/openapi/lib/api/search_api.dart +++ b/mobile/openapi/lib/api/search_api.dart @@ -16,7 +16,9 @@ class SearchApi { final ApiClient apiClient; - /// Performs an HTTP 'GET /search/cities' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getAssetsByCityWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/search/cities'; @@ -42,6 +44,7 @@ class SearchApi { ); } + /// This endpoint requires the `asset.read` permission. Future?> getAssetsByCity() async { final response = await getAssetsByCityWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -60,7 +63,9 @@ class SearchApi { return null; } - /// Performs an HTTP 'GET /search/explore' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getExploreDataWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/search/explore'; @@ -86,6 +91,7 @@ class SearchApi { ); } + /// This endpoint requires the `asset.read` permission. Future?> getExploreData() async { final response = await getExploreDataWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -104,7 +110,10 @@ class SearchApi { return null; } - /// Performs an HTTP 'GET /search/suggestions' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [SearchSuggestionType] type (required): @@ -161,6 +170,8 @@ class SearchApi { ); } + /// This endpoint requires the `asset.read` permission. + /// /// Parameters: /// /// * [SearchSuggestionType] type (required): @@ -193,7 +204,10 @@ class SearchApi { return null; } - /// Performs an HTTP 'POST /search/statistics' operation and returns the [Response]. + /// This endpoint requires the `asset.statistics` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [StatisticsSearchDto] statisticsSearchDto (required): @@ -222,6 +236,8 @@ class SearchApi { ); } + /// This endpoint requires the `asset.statistics` permission. + /// /// Parameters: /// /// * [StatisticsSearchDto] statisticsSearchDto (required): @@ -240,7 +256,10 @@ class SearchApi { return null; } - /// Performs an HTTP 'POST /search/metadata' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [MetadataSearchDto] metadataSearchDto (required): @@ -269,6 +288,8 @@ class SearchApi { ); } + /// This endpoint requires the `asset.read` permission. + /// /// Parameters: /// /// * [MetadataSearchDto] metadataSearchDto (required): @@ -287,7 +308,279 @@ class SearchApi { return null; } - /// Performs an HTTP 'GET /search/person' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// + /// Parameters: + /// + /// * [List] albumIds: + /// + /// * [String] city: + /// + /// * [String] country: + /// + /// * [DateTime] createdAfter: + /// + /// * [DateTime] createdBefore: + /// + /// * [String] deviceId: + /// + /// * [bool] isEncoded: + /// + /// * [bool] isFavorite: + /// + /// * [bool] isMotion: + /// + /// * [bool] isNotInAlbum: + /// + /// * [bool] isOffline: + /// + /// * [String] lensModel: + /// + /// * [String] libraryId: + /// + /// * [String] make: + /// + /// * [int] minFileSize: + /// + /// * [String] model: + /// + /// * [List] personIds: + /// + /// * [num] rating: + /// + /// * [num] size: + /// + /// * [String] state: + /// + /// * [List] tagIds: + /// + /// * [DateTime] takenAfter: + /// + /// * [DateTime] takenBefore: + /// + /// * [DateTime] trashedAfter: + /// + /// * [DateTime] trashedBefore: + /// + /// * [AssetTypeEnum] type: + /// + /// * [DateTime] updatedAfter: + /// + /// * [DateTime] updatedBefore: + /// + /// * [AssetVisibility] visibility: + /// + /// * [bool] withDeleted: + /// + /// * [bool] withExif: + Future searchLargeAssetsWithHttpInfo({ List? albumIds, String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, String? deviceId, bool? isEncoded, bool? isFavorite, bool? isMotion, bool? isNotInAlbum, bool? isOffline, String? lensModel, String? libraryId, String? make, int? minFileSize, String? model, List? personIds, num? rating, num? size, String? state, List? tagIds, DateTime? takenAfter, DateTime? takenBefore, DateTime? trashedAfter, DateTime? trashedBefore, AssetTypeEnum? type, DateTime? updatedAfter, DateTime? updatedBefore, AssetVisibility? visibility, bool? withDeleted, bool? withExif, }) async { + // ignore: prefer_const_declarations + final apiPath = r'/search/large-assets'; + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + if (albumIds != null) { + queryParams.addAll(_queryParams('multi', 'albumIds', albumIds)); + } + if (city != null) { + queryParams.addAll(_queryParams('', 'city', city)); + } + if (country != null) { + queryParams.addAll(_queryParams('', 'country', country)); + } + if (createdAfter != null) { + queryParams.addAll(_queryParams('', 'createdAfter', createdAfter)); + } + if (createdBefore != null) { + queryParams.addAll(_queryParams('', 'createdBefore', createdBefore)); + } + if (deviceId != null) { + queryParams.addAll(_queryParams('', 'deviceId', deviceId)); + } + if (isEncoded != null) { + queryParams.addAll(_queryParams('', 'isEncoded', isEncoded)); + } + if (isFavorite != null) { + queryParams.addAll(_queryParams('', 'isFavorite', isFavorite)); + } + if (isMotion != null) { + queryParams.addAll(_queryParams('', 'isMotion', isMotion)); + } + if (isNotInAlbum != null) { + queryParams.addAll(_queryParams('', 'isNotInAlbum', isNotInAlbum)); + } + if (isOffline != null) { + queryParams.addAll(_queryParams('', 'isOffline', isOffline)); + } + if (lensModel != null) { + queryParams.addAll(_queryParams('', 'lensModel', lensModel)); + } + if (libraryId != null) { + queryParams.addAll(_queryParams('', 'libraryId', libraryId)); + } + if (make != null) { + queryParams.addAll(_queryParams('', 'make', make)); + } + if (minFileSize != null) { + queryParams.addAll(_queryParams('', 'minFileSize', minFileSize)); + } + if (model != null) { + queryParams.addAll(_queryParams('', 'model', model)); + } + if (personIds != null) { + queryParams.addAll(_queryParams('multi', 'personIds', personIds)); + } + if (rating != null) { + queryParams.addAll(_queryParams('', 'rating', rating)); + } + if (size != null) { + queryParams.addAll(_queryParams('', 'size', size)); + } + if (state != null) { + queryParams.addAll(_queryParams('', 'state', state)); + } + if (tagIds != null) { + queryParams.addAll(_queryParams('multi', 'tagIds', tagIds)); + } + if (takenAfter != null) { + queryParams.addAll(_queryParams('', 'takenAfter', takenAfter)); + } + if (takenBefore != null) { + queryParams.addAll(_queryParams('', 'takenBefore', takenBefore)); + } + if (trashedAfter != null) { + queryParams.addAll(_queryParams('', 'trashedAfter', trashedAfter)); + } + if (trashedBefore != null) { + queryParams.addAll(_queryParams('', 'trashedBefore', trashedBefore)); + } + if (type != null) { + queryParams.addAll(_queryParams('', 'type', type)); + } + if (updatedAfter != null) { + queryParams.addAll(_queryParams('', 'updatedAfter', updatedAfter)); + } + if (updatedBefore != null) { + queryParams.addAll(_queryParams('', 'updatedBefore', updatedBefore)); + } + if (visibility != null) { + queryParams.addAll(_queryParams('', 'visibility', visibility)); + } + if (withDeleted != null) { + queryParams.addAll(_queryParams('', 'withDeleted', withDeleted)); + } + if (withExif != null) { + queryParams.addAll(_queryParams('', 'withExif', withExif)); + } + + const contentTypes = []; + + + return apiClient.invokeAPI( + apiPath, + 'POST', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// This endpoint requires the `asset.read` permission. + /// + /// Parameters: + /// + /// * [List] albumIds: + /// + /// * [String] city: + /// + /// * [String] country: + /// + /// * [DateTime] createdAfter: + /// + /// * [DateTime] createdBefore: + /// + /// * [String] deviceId: + /// + /// * [bool] isEncoded: + /// + /// * [bool] isFavorite: + /// + /// * [bool] isMotion: + /// + /// * [bool] isNotInAlbum: + /// + /// * [bool] isOffline: + /// + /// * [String] lensModel: + /// + /// * [String] libraryId: + /// + /// * [String] make: + /// + /// * [int] minFileSize: + /// + /// * [String] model: + /// + /// * [List] personIds: + /// + /// * [num] rating: + /// + /// * [num] size: + /// + /// * [String] state: + /// + /// * [List] tagIds: + /// + /// * [DateTime] takenAfter: + /// + /// * [DateTime] takenBefore: + /// + /// * [DateTime] trashedAfter: + /// + /// * [DateTime] trashedBefore: + /// + /// * [AssetTypeEnum] type: + /// + /// * [DateTime] updatedAfter: + /// + /// * [DateTime] updatedBefore: + /// + /// * [AssetVisibility] visibility: + /// + /// * [bool] withDeleted: + /// + /// * [bool] withExif: + Future?> searchLargeAssets({ List? albumIds, String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, String? deviceId, bool? isEncoded, bool? isFavorite, bool? isMotion, bool? isNotInAlbum, bool? isOffline, String? lensModel, String? libraryId, String? make, int? minFileSize, String? model, List? personIds, num? rating, num? size, String? state, List? tagIds, DateTime? takenAfter, DateTime? takenBefore, DateTime? trashedAfter, DateTime? trashedBefore, AssetTypeEnum? type, DateTime? updatedAfter, DateTime? updatedBefore, AssetVisibility? visibility, bool? withDeleted, bool? withExif, }) async { + final response = await searchLargeAssetsWithHttpInfo( albumIds: albumIds, city: city, country: country, createdAfter: createdAfter, createdBefore: createdBefore, deviceId: deviceId, isEncoded: isEncoded, isFavorite: isFavorite, isMotion: isMotion, isNotInAlbum: isNotInAlbum, isOffline: isOffline, lensModel: lensModel, libraryId: libraryId, make: make, minFileSize: minFileSize, model: model, personIds: personIds, rating: rating, size: size, state: state, tagIds: tagIds, takenAfter: takenAfter, takenBefore: takenBefore, trashedAfter: trashedAfter, trashedBefore: trashedBefore, type: type, updatedAfter: updatedAfter, updatedBefore: updatedBefore, visibility: visibility, withDeleted: withDeleted, withExif: withExif, ); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + // When a remote server returns no body with a status of 204, we shall not decode it. + // At the time of writing this, `dart:convert` will throw an "Unexpected end of input" + // FormatException when trying to decode an empty string. + if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { + final responseBody = await _decodeBodyBytes(response); + return (await apiClient.deserializeAsync(responseBody, 'List') as List) + .cast() + .toList(growable: false); + + } + return null; + } + + /// This endpoint requires the `person.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] name (required): @@ -323,6 +616,8 @@ class SearchApi { ); } + /// This endpoint requires the `person.read` permission. + /// /// Parameters: /// /// * [String] name (required): @@ -346,7 +641,10 @@ class SearchApi { return null; } - /// Performs an HTTP 'GET /search/places' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] name (required): @@ -377,6 +675,8 @@ class SearchApi { ); } + /// This endpoint requires the `asset.read` permission. + /// /// Parameters: /// /// * [String] name (required): @@ -398,7 +698,10 @@ class SearchApi { return null; } - /// Performs an HTTP 'POST /search/random' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [RandomSearchDto] randomSearchDto (required): @@ -427,6 +730,8 @@ class SearchApi { ); } + /// This endpoint requires the `asset.read` permission. + /// /// Parameters: /// /// * [RandomSearchDto] randomSearchDto (required): @@ -448,7 +753,10 @@ class SearchApi { return null; } - /// Performs an HTTP 'POST /search/smart' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [SmartSearchDto] smartSearchDto (required): @@ -477,6 +785,8 @@ class SearchApi { ); } + /// This endpoint requires the `asset.read` permission. + /// /// Parameters: /// /// * [SmartSearchDto] smartSearchDto (required): diff --git a/mobile/openapi/lib/api/server_api.dart b/mobile/openapi/lib/api/server_api.dart index 7abdabcd3e..9e250b83b5 100644 --- a/mobile/openapi/lib/api/server_api.dart +++ b/mobile/openapi/lib/api/server_api.dart @@ -16,7 +16,9 @@ class ServerApi { final ApiClient apiClient; - /// Performs an HTTP 'DELETE /server/license' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `serverLicense.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. Future deleteServerLicenseWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/server/license'; @@ -42,6 +44,7 @@ class ServerApi { ); } + /// This endpoint is an admin-only route, and requires the `serverLicense.delete` permission. Future deleteServerLicense() async { final response = await deleteServerLicenseWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -49,7 +52,9 @@ class ServerApi { } } - /// Performs an HTTP 'GET /server/about' operation and returns the [Response]. + /// This endpoint requires the `server.about` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getAboutInfoWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/server/about'; @@ -75,6 +80,7 @@ class ServerApi { ); } + /// This endpoint requires the `server.about` permission. Future getAboutInfo() async { final response = await getAboutInfoWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -90,7 +96,9 @@ class ServerApi { return null; } - /// Performs an HTTP 'GET /server/apk-links' operation and returns the [Response]. + /// This endpoint requires the `server.apkLinks` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getApkLinksWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/server/apk-links'; @@ -116,6 +124,7 @@ class ServerApi { ); } + /// This endpoint requires the `server.apkLinks` permission. Future getApkLinks() async { final response = await getApkLinksWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -213,7 +222,9 @@ class ServerApi { return null; } - /// Performs an HTTP 'GET /server/license' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `serverLicense.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getServerLicenseWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/server/license'; @@ -239,6 +250,7 @@ class ServerApi { ); } + /// This endpoint is an admin-only route, and requires the `serverLicense.read` permission. Future getServerLicense() async { final response = await getServerLicenseWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -254,7 +266,9 @@ class ServerApi { return null; } - /// Performs an HTTP 'GET /server/statistics' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `server.statistics` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getServerStatisticsWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/server/statistics'; @@ -280,6 +294,7 @@ class ServerApi { ); } + /// This endpoint is an admin-only route, and requires the `server.statistics` permission. Future getServerStatistics() async { final response = await getServerStatisticsWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -336,7 +351,9 @@ class ServerApi { return null; } - /// Performs an HTTP 'GET /server/storage' operation and returns the [Response]. + /// This endpoint requires the `server.storage` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getStorageWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/server/storage'; @@ -362,6 +379,7 @@ class ServerApi { ); } + /// This endpoint requires the `server.storage` permission. Future getStorage() async { final response = await getStorageWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -585,7 +603,10 @@ class ServerApi { return null; } - /// Performs an HTTP 'PUT /server/license' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `serverLicense.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [LicenseKeyDto] licenseKeyDto (required): @@ -614,6 +635,8 @@ class ServerApi { ); } + /// This endpoint is an admin-only route, and requires the `serverLicense.update` permission. + /// /// Parameters: /// /// * [LicenseKeyDto] licenseKeyDto (required): diff --git a/mobile/openapi/lib/api/sessions_api.dart b/mobile/openapi/lib/api/sessions_api.dart index d54f520641..63528d17a7 100644 --- a/mobile/openapi/lib/api/sessions_api.dart +++ b/mobile/openapi/lib/api/sessions_api.dart @@ -16,7 +16,10 @@ class SessionsApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /sessions' operation and returns the [Response]. + /// This endpoint requires the `session.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [SessionCreateDto] sessionCreateDto (required): @@ -45,6 +48,8 @@ class SessionsApi { ); } + /// This endpoint requires the `session.create` permission. + /// /// Parameters: /// /// * [SessionCreateDto] sessionCreateDto (required): @@ -63,7 +68,9 @@ class SessionsApi { return null; } - /// Performs an HTTP 'DELETE /sessions' operation and returns the [Response]. + /// This endpoint requires the `session.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. Future deleteAllSessionsWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/sessions'; @@ -89,6 +96,7 @@ class SessionsApi { ); } + /// This endpoint requires the `session.delete` permission. Future deleteAllSessions() async { final response = await deleteAllSessionsWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -96,7 +104,10 @@ class SessionsApi { } } - /// Performs an HTTP 'DELETE /sessions/{id}' operation and returns the [Response]. + /// This endpoint requires the `session.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -126,6 +137,8 @@ class SessionsApi { ); } + /// This endpoint requires the `session.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -136,7 +149,9 @@ class SessionsApi { } } - /// Performs an HTTP 'GET /sessions' operation and returns the [Response]. + /// This endpoint requires the `session.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getSessionsWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/sessions'; @@ -162,6 +177,7 @@ class SessionsApi { ); } + /// This endpoint requires the `session.read` permission. Future?> getSessions() async { final response = await getSessionsWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -180,7 +196,10 @@ class SessionsApi { return null; } - /// Performs an HTTP 'POST /sessions/{id}/lock' operation and returns the [Response]. + /// This endpoint requires the `session.lock` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -210,6 +229,8 @@ class SessionsApi { ); } + /// This endpoint requires the `session.lock` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -220,7 +241,10 @@ class SessionsApi { } } - /// Performs an HTTP 'PUT /sessions/{id}' operation and returns the [Response]. + /// This endpoint requires the `session.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -252,6 +276,8 @@ class SessionsApi { ); } + /// This endpoint requires the `session.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/shared_links_api.dart b/mobile/openapi/lib/api/shared_links_api.dart index 5bac8988dc..e32c566754 100644 --- a/mobile/openapi/lib/api/shared_links_api.dart +++ b/mobile/openapi/lib/api/shared_links_api.dart @@ -24,7 +24,9 @@ class SharedLinksApi { /// * [AssetIdsDto] assetIdsDto (required): /// /// * [String] key: - Future addSharedLinkAssetsWithHttpInfo(String id, AssetIdsDto assetIdsDto, { String? key, }) async { + /// + /// * [String] slug: + Future addSharedLinkAssetsWithHttpInfo(String id, AssetIdsDto assetIdsDto, { String? key, String? slug, }) async { // ignore: prefer_const_declarations final apiPath = r'/shared-links/{id}/assets' .replaceAll('{id}', id); @@ -39,6 +41,9 @@ class SharedLinksApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = ['application/json']; @@ -61,8 +66,10 @@ class SharedLinksApi { /// * [AssetIdsDto] assetIdsDto (required): /// /// * [String] key: - Future?> addSharedLinkAssets(String id, AssetIdsDto assetIdsDto, { String? key, }) async { - final response = await addSharedLinkAssetsWithHttpInfo(id, assetIdsDto, key: key, ); + /// + /// * [String] slug: + Future?> addSharedLinkAssets(String id, AssetIdsDto assetIdsDto, { String? key, String? slug, }) async { + final response = await addSharedLinkAssetsWithHttpInfo(id, assetIdsDto, key: key, slug: slug, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -79,7 +86,10 @@ class SharedLinksApi { return null; } - /// Performs an HTTP 'POST /shared-links' operation and returns the [Response]. + /// This endpoint requires the `sharedLink.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [SharedLinkCreateDto] sharedLinkCreateDto (required): @@ -108,6 +118,8 @@ class SharedLinksApi { ); } + /// This endpoint requires the `sharedLink.create` permission. + /// /// Parameters: /// /// * [SharedLinkCreateDto] sharedLinkCreateDto (required): @@ -126,7 +138,10 @@ class SharedLinksApi { return null; } - /// Performs an HTTP 'GET /shared-links' operation and returns the [Response]. + /// This endpoint requires the `sharedLink.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] albumId: @@ -159,6 +174,8 @@ class SharedLinksApi { ); } + /// This endpoint requires the `sharedLink.read` permission. + /// /// Parameters: /// /// * [String] albumId: @@ -183,12 +200,14 @@ class SharedLinksApi { /// Performs an HTTP 'GET /shared-links/me' operation and returns the [Response]. /// Parameters: /// - /// * [String] key: - /// /// * [String] password: /// /// * [String] token: - Future getMySharedLinkWithHttpInfo({ String? key, String? password, String? token, }) async { + /// + /// * [String] key: + /// + /// * [String] slug: + Future getMySharedLinkWithHttpInfo({ String? password, String? token, String? key, String? slug, }) async { // ignore: prefer_const_declarations final apiPath = r'/shared-links/me'; @@ -199,15 +218,18 @@ class SharedLinksApi { final headerParams = {}; final formParams = {}; - if (key != null) { - queryParams.addAll(_queryParams('', 'key', key)); - } if (password != null) { queryParams.addAll(_queryParams('', 'password', password)); } if (token != null) { queryParams.addAll(_queryParams('', 'token', token)); } + if (key != null) { + queryParams.addAll(_queryParams('', 'key', key)); + } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = []; @@ -225,13 +247,15 @@ class SharedLinksApi { /// Parameters: /// - /// * [String] key: - /// /// * [String] password: /// /// * [String] token: - Future getMySharedLink({ String? key, String? password, String? token, }) async { - final response = await getMySharedLinkWithHttpInfo( key: key, password: password, token: token, ); + /// + /// * [String] key: + /// + /// * [String] slug: + Future getMySharedLink({ String? password, String? token, String? key, String? slug, }) async { + final response = await getMySharedLinkWithHttpInfo( password: password, token: token, key: key, slug: slug, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -245,7 +269,10 @@ class SharedLinksApi { return null; } - /// Performs an HTTP 'GET /shared-links/{id}' operation and returns the [Response]. + /// This endpoint requires the `sharedLink.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -275,6 +302,8 @@ class SharedLinksApi { ); } + /// This endpoint requires the `sharedLink.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -293,7 +322,10 @@ class SharedLinksApi { return null; } - /// Performs an HTTP 'DELETE /shared-links/{id}' operation and returns the [Response]. + /// This endpoint requires the `sharedLink.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -323,6 +355,8 @@ class SharedLinksApi { ); } + /// This endpoint requires the `sharedLink.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -341,7 +375,9 @@ class SharedLinksApi { /// * [AssetIdsDto] assetIdsDto (required): /// /// * [String] key: - Future removeSharedLinkAssetsWithHttpInfo(String id, AssetIdsDto assetIdsDto, { String? key, }) async { + /// + /// * [String] slug: + Future removeSharedLinkAssetsWithHttpInfo(String id, AssetIdsDto assetIdsDto, { String? key, String? slug, }) async { // ignore: prefer_const_declarations final apiPath = r'/shared-links/{id}/assets' .replaceAll('{id}', id); @@ -356,6 +392,9 @@ class SharedLinksApi { if (key != null) { queryParams.addAll(_queryParams('', 'key', key)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } const contentTypes = ['application/json']; @@ -378,8 +417,10 @@ class SharedLinksApi { /// * [AssetIdsDto] assetIdsDto (required): /// /// * [String] key: - Future?> removeSharedLinkAssets(String id, AssetIdsDto assetIdsDto, { String? key, }) async { - final response = await removeSharedLinkAssetsWithHttpInfo(id, assetIdsDto, key: key, ); + /// + /// * [String] slug: + Future?> removeSharedLinkAssets(String id, AssetIdsDto assetIdsDto, { String? key, String? slug, }) async { + final response = await removeSharedLinkAssetsWithHttpInfo(id, assetIdsDto, key: key, slug: slug, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -396,7 +437,10 @@ class SharedLinksApi { return null; } - /// Performs an HTTP 'PATCH /shared-links/{id}' operation and returns the [Response]. + /// This endpoint requires the `sharedLink.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -428,6 +472,8 @@ class SharedLinksApi { ); } + /// This endpoint requires the `sharedLink.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/stacks_api.dart b/mobile/openapi/lib/api/stacks_api.dart index 84f23ec55d..0f76f3396b 100644 --- a/mobile/openapi/lib/api/stacks_api.dart +++ b/mobile/openapi/lib/api/stacks_api.dart @@ -16,7 +16,10 @@ class StacksApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /stacks' operation and returns the [Response]. + /// This endpoint requires the `stack.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [StackCreateDto] stackCreateDto (required): @@ -45,6 +48,8 @@ class StacksApi { ); } + /// This endpoint requires the `stack.create` permission. + /// /// Parameters: /// /// * [StackCreateDto] stackCreateDto (required): @@ -63,7 +68,10 @@ class StacksApi { return null; } - /// Performs an HTTP 'DELETE /stacks/{id}' operation and returns the [Response]. + /// This endpoint requires the `stack.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -93,6 +101,8 @@ class StacksApi { ); } + /// This endpoint requires the `stack.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -103,7 +113,10 @@ class StacksApi { } } - /// Performs an HTTP 'DELETE /stacks' operation and returns the [Response]. + /// This endpoint requires the `stack.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): @@ -132,6 +145,8 @@ class StacksApi { ); } + /// This endpoint requires the `stack.delete` permission. + /// /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): @@ -142,7 +157,10 @@ class StacksApi { } } - /// Performs an HTTP 'GET /stacks/{id}' operation and returns the [Response]. + /// This endpoint requires the `stack.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -172,6 +190,8 @@ class StacksApi { ); } + /// This endpoint requires the `stack.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -190,7 +210,60 @@ class StacksApi { return null; } - /// Performs an HTTP 'GET /stacks' operation and returns the [Response]. + /// This endpoint requires the `stack.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// + /// Parameters: + /// + /// * [String] assetId (required): + /// + /// * [String] id (required): + Future removeAssetFromStackWithHttpInfo(String assetId, String id,) async { + // ignore: prefer_const_declarations + final apiPath = r'/stacks/{id}/assets/{assetId}' + .replaceAll('{assetId}', assetId) + .replaceAll('{id}', id); + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = []; + + + return apiClient.invokeAPI( + apiPath, + 'DELETE', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// This endpoint requires the `stack.update` permission. + /// + /// Parameters: + /// + /// * [String] assetId (required): + /// + /// * [String] id (required): + Future removeAssetFromStack(String assetId, String id,) async { + final response = await removeAssetFromStackWithHttpInfo(assetId, id,); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + } + + /// This endpoint requires the `stack.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] primaryAssetId: @@ -223,6 +296,8 @@ class StacksApi { ); } + /// This endpoint requires the `stack.read` permission. + /// /// Parameters: /// /// * [String] primaryAssetId: @@ -244,7 +319,10 @@ class StacksApi { return null; } - /// Performs an HTTP 'PUT /stacks/{id}' operation and returns the [Response]. + /// This endpoint requires the `stack.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -276,6 +354,8 @@ class StacksApi { ); } + /// This endpoint requires the `stack.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/sync_api.dart b/mobile/openapi/lib/api/sync_api.dart index fe2876ddd8..9e594d6ace 100644 --- a/mobile/openapi/lib/api/sync_api.dart +++ b/mobile/openapi/lib/api/sync_api.dart @@ -16,7 +16,10 @@ class SyncApi { final ApiClient apiClient; - /// Performs an HTTP 'DELETE /sync/ack' operation and returns the [Response]. + /// This endpoint requires the `syncCheckpoint.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [SyncAckDeleteDto] syncAckDeleteDto (required): @@ -45,6 +48,8 @@ class SyncApi { ); } + /// This endpoint requires the `syncCheckpoint.delete` permission. + /// /// Parameters: /// /// * [SyncAckDeleteDto] syncAckDeleteDto (required): @@ -152,7 +157,9 @@ class SyncApi { return null; } - /// Performs an HTTP 'GET /sync/ack' operation and returns the [Response]. + /// This endpoint requires the `syncCheckpoint.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getSyncAckWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/sync/ack'; @@ -178,6 +185,7 @@ class SyncApi { ); } + /// This endpoint requires the `syncCheckpoint.read` permission. Future?> getSyncAck() async { final response = await getSyncAckWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -196,7 +204,10 @@ class SyncApi { return null; } - /// Performs an HTTP 'POST /sync/stream' operation and returns the [Response]. + /// This endpoint requires the `sync.stream` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [SyncStreamDto] syncStreamDto (required): @@ -225,6 +236,8 @@ class SyncApi { ); } + /// This endpoint requires the `sync.stream` permission. + /// /// Parameters: /// /// * [SyncStreamDto] syncStreamDto (required): @@ -235,7 +248,10 @@ class SyncApi { } } - /// Performs an HTTP 'POST /sync/ack' operation and returns the [Response]. + /// This endpoint requires the `syncCheckpoint.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [SyncAckSetDto] syncAckSetDto (required): @@ -264,6 +280,8 @@ class SyncApi { ); } + /// This endpoint requires the `syncCheckpoint.update` permission. + /// /// Parameters: /// /// * [SyncAckSetDto] syncAckSetDto (required): diff --git a/mobile/openapi/lib/api/system_config_api.dart b/mobile/openapi/lib/api/system_config_api.dart index a03b9d3e72..2ab3879b8a 100644 --- a/mobile/openapi/lib/api/system_config_api.dart +++ b/mobile/openapi/lib/api/system_config_api.dart @@ -16,7 +16,9 @@ class SystemConfigApi { final ApiClient apiClient; - /// Performs an HTTP 'GET /system-config' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `systemConfig.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getConfigWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/system-config'; @@ -42,6 +44,7 @@ class SystemConfigApi { ); } + /// This endpoint is an admin-only route, and requires the `systemConfig.read` permission. Future getConfig() async { final response = await getConfigWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -57,7 +60,9 @@ class SystemConfigApi { return null; } - /// Performs an HTTP 'GET /system-config/defaults' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `systemConfig.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getConfigDefaultsWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/system-config/defaults'; @@ -83,6 +88,7 @@ class SystemConfigApi { ); } + /// This endpoint is an admin-only route, and requires the `systemConfig.read` permission. Future getConfigDefaults() async { final response = await getConfigDefaultsWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -98,7 +104,9 @@ class SystemConfigApi { return null; } - /// Performs an HTTP 'GET /system-config/storage-template-options' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `systemConfig.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getStorageTemplateOptionsWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/system-config/storage-template-options'; @@ -124,6 +132,7 @@ class SystemConfigApi { ); } + /// This endpoint is an admin-only route, and requires the `systemConfig.read` permission. Future getStorageTemplateOptions() async { final response = await getStorageTemplateOptionsWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -139,7 +148,10 @@ class SystemConfigApi { return null; } - /// Performs an HTTP 'PUT /system-config' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `systemConfig.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [SystemConfigDto] systemConfigDto (required): @@ -168,6 +180,8 @@ class SystemConfigApi { ); } + /// This endpoint is an admin-only route, and requires the `systemConfig.update` permission. + /// /// Parameters: /// /// * [SystemConfigDto] systemConfigDto (required): diff --git a/mobile/openapi/lib/api/system_metadata_api.dart b/mobile/openapi/lib/api/system_metadata_api.dart index 3fcceb8e42..f6b9bad1d6 100644 --- a/mobile/openapi/lib/api/system_metadata_api.dart +++ b/mobile/openapi/lib/api/system_metadata_api.dart @@ -16,7 +16,9 @@ class SystemMetadataApi { final ApiClient apiClient; - /// Performs an HTTP 'GET /system-metadata/admin-onboarding' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `systemMetadata.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getAdminOnboardingWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/system-metadata/admin-onboarding'; @@ -42,6 +44,7 @@ class SystemMetadataApi { ); } + /// This endpoint is an admin-only route, and requires the `systemMetadata.read` permission. Future getAdminOnboarding() async { final response = await getAdminOnboardingWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -57,7 +60,9 @@ class SystemMetadataApi { return null; } - /// Performs an HTTP 'GET /system-metadata/reverse-geocoding-state' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `systemMetadata.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getReverseGeocodingStateWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/system-metadata/reverse-geocoding-state'; @@ -83,6 +88,7 @@ class SystemMetadataApi { ); } + /// This endpoint is an admin-only route, and requires the `systemMetadata.read` permission. Future getReverseGeocodingState() async { final response = await getReverseGeocodingStateWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -98,7 +104,9 @@ class SystemMetadataApi { return null; } - /// Performs an HTTP 'GET /system-metadata/version-check-state' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `systemMetadata.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getVersionCheckStateWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/system-metadata/version-check-state'; @@ -124,6 +132,7 @@ class SystemMetadataApi { ); } + /// This endpoint is an admin-only route, and requires the `systemMetadata.read` permission. Future getVersionCheckState() async { final response = await getVersionCheckStateWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -139,7 +148,10 @@ class SystemMetadataApi { return null; } - /// Performs an HTTP 'POST /system-metadata/admin-onboarding' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `systemMetadata.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [AdminOnboardingUpdateDto] adminOnboardingUpdateDto (required): @@ -168,6 +180,8 @@ class SystemMetadataApi { ); } + /// This endpoint is an admin-only route, and requires the `systemMetadata.update` permission. + /// /// Parameters: /// /// * [AdminOnboardingUpdateDto] adminOnboardingUpdateDto (required): diff --git a/mobile/openapi/lib/api/tags_api.dart b/mobile/openapi/lib/api/tags_api.dart index f6cfc8720b..a0cdb91acf 100644 --- a/mobile/openapi/lib/api/tags_api.dart +++ b/mobile/openapi/lib/api/tags_api.dart @@ -16,7 +16,10 @@ class TagsApi { final ApiClient apiClient; - /// Performs an HTTP 'PUT /tags/assets' operation and returns the [Response]. + /// This endpoint requires the `tag.asset` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [TagBulkAssetsDto] tagBulkAssetsDto (required): @@ -45,6 +48,8 @@ class TagsApi { ); } + /// This endpoint requires the `tag.asset` permission. + /// /// Parameters: /// /// * [TagBulkAssetsDto] tagBulkAssetsDto (required): @@ -63,7 +68,10 @@ class TagsApi { return null; } - /// Performs an HTTP 'POST /tags' operation and returns the [Response]. + /// This endpoint requires the `tag.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [TagCreateDto] tagCreateDto (required): @@ -92,6 +100,8 @@ class TagsApi { ); } + /// This endpoint requires the `tag.create` permission. + /// /// Parameters: /// /// * [TagCreateDto] tagCreateDto (required): @@ -110,7 +120,10 @@ class TagsApi { return null; } - /// Performs an HTTP 'DELETE /tags/{id}' operation and returns the [Response]. + /// This endpoint requires the `tag.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -140,6 +153,8 @@ class TagsApi { ); } + /// This endpoint requires the `tag.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -150,7 +165,9 @@ class TagsApi { } } - /// Performs an HTTP 'GET /tags' operation and returns the [Response]. + /// This endpoint requires the `tag.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getAllTagsWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/tags'; @@ -176,6 +193,7 @@ class TagsApi { ); } + /// This endpoint requires the `tag.read` permission. Future?> getAllTags() async { final response = await getAllTagsWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -194,7 +212,10 @@ class TagsApi { return null; } - /// Performs an HTTP 'GET /tags/{id}' operation and returns the [Response]. + /// This endpoint requires the `tag.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -224,6 +245,8 @@ class TagsApi { ); } + /// This endpoint requires the `tag.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -242,7 +265,10 @@ class TagsApi { return null; } - /// Performs an HTTP 'PUT /tags/{id}/assets' operation and returns the [Response]. + /// This endpoint requires the `tag.asset` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -274,6 +300,8 @@ class TagsApi { ); } + /// This endpoint requires the `tag.asset` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -297,7 +325,10 @@ class TagsApi { return null; } - /// Performs an HTTP 'DELETE /tags/{id}/assets' operation and returns the [Response]. + /// This endpoint requires the `tag.asset` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -329,6 +360,8 @@ class TagsApi { ); } + /// This endpoint requires the `tag.asset` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -352,7 +385,10 @@ class TagsApi { return null; } - /// Performs an HTTP 'PUT /tags/{id}' operation and returns the [Response]. + /// This endpoint requires the `tag.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -384,6 +420,8 @@ class TagsApi { ); } + /// This endpoint requires the `tag.update` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -404,7 +442,10 @@ class TagsApi { return null; } - /// Performs an HTTP 'PUT /tags' operation and returns the [Response]. + /// This endpoint requires the `tag.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [TagUpsertDto] tagUpsertDto (required): @@ -433,6 +474,8 @@ class TagsApi { ); } + /// This endpoint requires the `tag.create` permission. + /// /// Parameters: /// /// * [TagUpsertDto] tagUpsertDto (required): diff --git a/mobile/openapi/lib/api/timeline_api.dart b/mobile/openapi/lib/api/timeline_api.dart index 042bc70401..2d142e3d67 100644 --- a/mobile/openapi/lib/api/timeline_api.dart +++ b/mobile/openapi/lib/api/timeline_api.dart @@ -16,7 +16,10 @@ class TimelineApi { final ApiClient apiClient; - /// Performs an HTTP 'GET /timeline/bucket' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] timeBucket (required): @@ -39,6 +42,8 @@ class TimelineApi { /// * [String] personId: /// Filter assets containing a specific person (face recognition) /// + /// * [String] slug: + /// /// * [String] tagId: /// Filter assets with a specific tag /// @@ -53,7 +58,7 @@ class TimelineApi { /// /// * [bool] withStacked: /// Include stacked assets in the response. When true, only primary assets from stacks are returned. - Future getTimeBucketWithHttpInfo(String timeBucket, { String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? tagId, String? userId, AssetVisibility? visibility, bool? withPartners, bool? withStacked, }) async { + Future getTimeBucketWithHttpInfo(String timeBucket, { String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? slug, String? tagId, String? userId, AssetVisibility? visibility, bool? withPartners, bool? withStacked, }) async { // ignore: prefer_const_declarations final apiPath = r'/timeline/bucket'; @@ -82,6 +87,9 @@ class TimelineApi { if (personId != null) { queryParams.addAll(_queryParams('', 'personId', personId)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } if (tagId != null) { queryParams.addAll(_queryParams('', 'tagId', tagId)); } @@ -113,6 +121,8 @@ class TimelineApi { ); } + /// This endpoint requires the `asset.read` permission. + /// /// Parameters: /// /// * [String] timeBucket (required): @@ -135,6 +145,8 @@ class TimelineApi { /// * [String] personId: /// Filter assets containing a specific person (face recognition) /// + /// * [String] slug: + /// /// * [String] tagId: /// Filter assets with a specific tag /// @@ -149,8 +161,8 @@ class TimelineApi { /// /// * [bool] withStacked: /// Include stacked assets in the response. When true, only primary assets from stacks are returned. - Future getTimeBucket(String timeBucket, { String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? tagId, String? userId, AssetVisibility? visibility, bool? withPartners, bool? withStacked, }) async { - final response = await getTimeBucketWithHttpInfo(timeBucket, albumId: albumId, isFavorite: isFavorite, isTrashed: isTrashed, key: key, order: order, personId: personId, tagId: tagId, userId: userId, visibility: visibility, withPartners: withPartners, withStacked: withStacked, ); + Future getTimeBucket(String timeBucket, { String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? slug, String? tagId, String? userId, AssetVisibility? visibility, bool? withPartners, bool? withStacked, }) async { + final response = await getTimeBucketWithHttpInfo(timeBucket, albumId: albumId, isFavorite: isFavorite, isTrashed: isTrashed, key: key, order: order, personId: personId, slug: slug, tagId: tagId, userId: userId, visibility: visibility, withPartners: withPartners, withStacked: withStacked, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } @@ -164,7 +176,10 @@ class TimelineApi { return null; } - /// Performs an HTTP 'GET /timeline/buckets' operation and returns the [Response]. + /// This endpoint requires the `asset.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] albumId: @@ -184,6 +199,8 @@ class TimelineApi { /// * [String] personId: /// Filter assets containing a specific person (face recognition) /// + /// * [String] slug: + /// /// * [String] tagId: /// Filter assets with a specific tag /// @@ -198,7 +215,7 @@ class TimelineApi { /// /// * [bool] withStacked: /// Include stacked assets in the response. When true, only primary assets from stacks are returned. - Future getTimeBucketsWithHttpInfo({ String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? tagId, String? userId, AssetVisibility? visibility, bool? withPartners, bool? withStacked, }) async { + Future getTimeBucketsWithHttpInfo({ String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? slug, String? tagId, String? userId, AssetVisibility? visibility, bool? withPartners, bool? withStacked, }) async { // ignore: prefer_const_declarations final apiPath = r'/timeline/buckets'; @@ -227,6 +244,9 @@ class TimelineApi { if (personId != null) { queryParams.addAll(_queryParams('', 'personId', personId)); } + if (slug != null) { + queryParams.addAll(_queryParams('', 'slug', slug)); + } if (tagId != null) { queryParams.addAll(_queryParams('', 'tagId', tagId)); } @@ -257,6 +277,8 @@ class TimelineApi { ); } + /// This endpoint requires the `asset.read` permission. + /// /// Parameters: /// /// * [String] albumId: @@ -276,6 +298,8 @@ class TimelineApi { /// * [String] personId: /// Filter assets containing a specific person (face recognition) /// + /// * [String] slug: + /// /// * [String] tagId: /// Filter assets with a specific tag /// @@ -290,8 +314,8 @@ class TimelineApi { /// /// * [bool] withStacked: /// Include stacked assets in the response. When true, only primary assets from stacks are returned. - Future?> getTimeBuckets({ String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? tagId, String? userId, AssetVisibility? visibility, bool? withPartners, bool? withStacked, }) async { - final response = await getTimeBucketsWithHttpInfo( albumId: albumId, isFavorite: isFavorite, isTrashed: isTrashed, key: key, order: order, personId: personId, tagId: tagId, userId: userId, visibility: visibility, withPartners: withPartners, withStacked: withStacked, ); + Future?> getTimeBuckets({ String? albumId, bool? isFavorite, bool? isTrashed, String? key, AssetOrder? order, String? personId, String? slug, String? tagId, String? userId, AssetVisibility? visibility, bool? withPartners, bool? withStacked, }) async { + final response = await getTimeBucketsWithHttpInfo( albumId: albumId, isFavorite: isFavorite, isTrashed: isTrashed, key: key, order: order, personId: personId, slug: slug, tagId: tagId, userId: userId, visibility: visibility, withPartners: withPartners, withStacked: withStacked, ); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); } diff --git a/mobile/openapi/lib/api/trash_api.dart b/mobile/openapi/lib/api/trash_api.dart index 982dbcbeda..480d19960a 100644 --- a/mobile/openapi/lib/api/trash_api.dart +++ b/mobile/openapi/lib/api/trash_api.dart @@ -16,7 +16,9 @@ class TrashApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /trash/empty' operation and returns the [Response]. + /// This endpoint requires the `asset.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. Future emptyTrashWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/trash/empty'; @@ -42,6 +44,7 @@ class TrashApi { ); } + /// This endpoint requires the `asset.delete` permission. Future emptyTrash() async { final response = await emptyTrashWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -57,7 +60,10 @@ class TrashApi { return null; } - /// Performs an HTTP 'POST /trash/restore/assets' operation and returns the [Response]. + /// This endpoint requires the `asset.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): @@ -86,6 +92,8 @@ class TrashApi { ); } + /// This endpoint requires the `asset.delete` permission. + /// /// Parameters: /// /// * [BulkIdsDto] bulkIdsDto (required): @@ -104,7 +112,9 @@ class TrashApi { return null; } - /// Performs an HTTP 'POST /trash/restore' operation and returns the [Response]. + /// This endpoint requires the `asset.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. Future restoreTrashWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/trash/restore'; @@ -130,6 +140,7 @@ class TrashApi { ); } + /// This endpoint requires the `asset.delete` permission. Future restoreTrash() async { final response = await restoreTrashWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { diff --git a/mobile/openapi/lib/api/users_admin_api.dart b/mobile/openapi/lib/api/users_admin_api.dart index 58263504ce..e4fc1673ef 100644 --- a/mobile/openapi/lib/api/users_admin_api.dart +++ b/mobile/openapi/lib/api/users_admin_api.dart @@ -16,7 +16,10 @@ class UsersAdminApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /admin/users' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `adminUser.create` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [UserAdminCreateDto] userAdminCreateDto (required): @@ -45,6 +48,8 @@ class UsersAdminApi { ); } + /// This endpoint is an admin-only route, and requires the `adminUser.create` permission. + /// /// Parameters: /// /// * [UserAdminCreateDto] userAdminCreateDto (required): @@ -63,7 +68,10 @@ class UsersAdminApi { return null; } - /// Performs an HTTP 'DELETE /admin/users/{id}' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `adminUser.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -95,6 +103,8 @@ class UsersAdminApi { ); } + /// This endpoint is an admin-only route, and requires the `adminUser.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -115,7 +125,10 @@ class UsersAdminApi { return null; } - /// Performs an HTTP 'GET /admin/users/{id}' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `adminUser.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -145,6 +158,8 @@ class UsersAdminApi { ); } + /// This endpoint is an admin-only route, and requires the `adminUser.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -163,7 +178,10 @@ class UsersAdminApi { return null; } - /// Performs an HTTP 'GET /admin/users/{id}/preferences' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `adminUser.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -193,6 +211,8 @@ class UsersAdminApi { ); } + /// This endpoint is an admin-only route, and requires the `adminUser.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -211,7 +231,10 @@ class UsersAdminApi { return null; } - /// Performs an HTTP 'GET /admin/users/{id}/statistics' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `adminUser.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -257,6 +280,8 @@ class UsersAdminApi { ); } + /// This endpoint is an admin-only route, and requires the `adminUser.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -281,7 +306,10 @@ class UsersAdminApi { return null; } - /// Performs an HTTP 'POST /admin/users/{id}/restore' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `adminUser.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -311,6 +339,8 @@ class UsersAdminApi { ); } + /// This endpoint is an admin-only route, and requires the `adminUser.delete` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -329,7 +359,10 @@ class UsersAdminApi { return null; } - /// Performs an HTTP 'GET /admin/users' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `adminUser.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id: @@ -367,6 +400,8 @@ class UsersAdminApi { ); } + /// This endpoint is an admin-only route, and requires the `adminUser.read` permission. + /// /// Parameters: /// /// * [String] id: @@ -390,7 +425,10 @@ class UsersAdminApi { return null; } - /// Performs an HTTP 'PUT /admin/users/{id}' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `adminUser.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -422,6 +460,8 @@ class UsersAdminApi { ); } + /// This endpoint is an admin-only route, and requires the `adminUser.update` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -442,7 +482,10 @@ class UsersAdminApi { return null; } - /// Performs an HTTP 'PUT /admin/users/{id}/preferences' operation and returns the [Response]. + /// This endpoint is an admin-only route, and requires the `adminUser.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -474,6 +517,8 @@ class UsersAdminApi { ); } + /// This endpoint is an admin-only route, and requires the `adminUser.update` permission. + /// /// Parameters: /// /// * [String] id (required): diff --git a/mobile/openapi/lib/api/users_api.dart b/mobile/openapi/lib/api/users_api.dart index cd31617e74..c8891ba0c2 100644 --- a/mobile/openapi/lib/api/users_api.dart +++ b/mobile/openapi/lib/api/users_api.dart @@ -16,7 +16,10 @@ class UsersApi { final ApiClient apiClient; - /// Performs an HTTP 'POST /users/profile-image' operation and returns the [Response]. + /// This endpoint requires the `userProfileImage.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [MultipartFile] file (required): @@ -55,6 +58,8 @@ class UsersApi { ); } + /// This endpoint requires the `userProfileImage.update` permission. + /// /// Parameters: /// /// * [MultipartFile] file (required): @@ -73,7 +78,9 @@ class UsersApi { return null; } - /// Performs an HTTP 'DELETE /users/profile-image' operation and returns the [Response]. + /// This endpoint requires the `userProfileImage.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. Future deleteProfileImageWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/users/profile-image'; @@ -99,6 +106,7 @@ class UsersApi { ); } + /// This endpoint requires the `userProfileImage.delete` permission. Future deleteProfileImage() async { final response = await deleteProfileImageWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -106,7 +114,9 @@ class UsersApi { } } - /// Performs an HTTP 'DELETE /users/me/license' operation and returns the [Response]. + /// This endpoint requires the `userLicense.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. Future deleteUserLicenseWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/users/me/license'; @@ -132,6 +142,7 @@ class UsersApi { ); } + /// This endpoint requires the `userLicense.delete` permission. Future deleteUserLicense() async { final response = await deleteUserLicenseWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -139,7 +150,9 @@ class UsersApi { } } - /// Performs an HTTP 'DELETE /users/me/onboarding' operation and returns the [Response]. + /// This endpoint requires the `userOnboarding.delete` permission. + /// + /// Note: This method returns the HTTP [Response]. Future deleteUserOnboardingWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/users/me/onboarding'; @@ -165,6 +178,7 @@ class UsersApi { ); } + /// This endpoint requires the `userOnboarding.delete` permission. Future deleteUserOnboarding() async { final response = await deleteUserOnboardingWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -172,7 +186,9 @@ class UsersApi { } } - /// Performs an HTTP 'GET /users/me/preferences' operation and returns the [Response]. + /// This endpoint requires the `userPreference.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getMyPreferencesWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/users/me/preferences'; @@ -198,6 +214,7 @@ class UsersApi { ); } + /// This endpoint requires the `userPreference.read` permission. Future getMyPreferences() async { final response = await getMyPreferencesWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -213,7 +230,9 @@ class UsersApi { return null; } - /// Performs an HTTP 'GET /users/me' operation and returns the [Response]. + /// This endpoint requires the `user.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getMyUserWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/users/me'; @@ -239,6 +258,7 @@ class UsersApi { ); } + /// This endpoint requires the `user.read` permission. Future getMyUser() async { final response = await getMyUserWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -254,7 +274,10 @@ class UsersApi { return null; } - /// Performs an HTTP 'GET /users/{id}/profile-image' operation and returns the [Response]. + /// This endpoint requires the `userProfileImage.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -284,6 +307,8 @@ class UsersApi { ); } + /// This endpoint requires the `userProfileImage.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -302,7 +327,10 @@ class UsersApi { return null; } - /// Performs an HTTP 'GET /users/{id}' operation and returns the [Response]. + /// This endpoint requires the `user.read` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [String] id (required): @@ -332,6 +360,8 @@ class UsersApi { ); } + /// This endpoint requires the `user.read` permission. + /// /// Parameters: /// /// * [String] id (required): @@ -350,7 +380,9 @@ class UsersApi { return null; } - /// Performs an HTTP 'GET /users/me/license' operation and returns the [Response]. + /// This endpoint requires the `userLicense.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getUserLicenseWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/users/me/license'; @@ -376,6 +408,7 @@ class UsersApi { ); } + /// This endpoint requires the `userLicense.read` permission. Future getUserLicense() async { final response = await getUserLicenseWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -391,7 +424,9 @@ class UsersApi { return null; } - /// Performs an HTTP 'GET /users/me/onboarding' operation and returns the [Response]. + /// This endpoint requires the `userOnboarding.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future getUserOnboardingWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/users/me/onboarding'; @@ -417,6 +452,7 @@ class UsersApi { ); } + /// This endpoint requires the `userOnboarding.read` permission. Future getUserOnboarding() async { final response = await getUserOnboardingWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -432,7 +468,9 @@ class UsersApi { return null; } - /// Performs an HTTP 'GET /users' operation and returns the [Response]. + /// This endpoint requires the `user.read` permission. + /// + /// Note: This method returns the HTTP [Response]. Future searchUsersWithHttpInfo() async { // ignore: prefer_const_declarations final apiPath = r'/users'; @@ -458,6 +496,7 @@ class UsersApi { ); } + /// This endpoint requires the `user.read` permission. Future?> searchUsers() async { final response = await searchUsersWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { @@ -476,7 +515,10 @@ class UsersApi { return null; } - /// Performs an HTTP 'PUT /users/me/license' operation and returns the [Response]. + /// This endpoint requires the `userLicense.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [LicenseKeyDto] licenseKeyDto (required): @@ -505,6 +547,8 @@ class UsersApi { ); } + /// This endpoint requires the `userLicense.update` permission. + /// /// Parameters: /// /// * [LicenseKeyDto] licenseKeyDto (required): @@ -523,7 +567,10 @@ class UsersApi { return null; } - /// Performs an HTTP 'PUT /users/me/onboarding' operation and returns the [Response]. + /// This endpoint requires the `userOnboarding.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [OnboardingDto] onboardingDto (required): @@ -552,6 +599,8 @@ class UsersApi { ); } + /// This endpoint requires the `userOnboarding.update` permission. + /// /// Parameters: /// /// * [OnboardingDto] onboardingDto (required): @@ -570,7 +619,10 @@ class UsersApi { return null; } - /// Performs an HTTP 'PUT /users/me/preferences' operation and returns the [Response]. + /// This endpoint requires the `userPreference.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [UserPreferencesUpdateDto] userPreferencesUpdateDto (required): @@ -599,6 +651,8 @@ class UsersApi { ); } + /// This endpoint requires the `userPreference.update` permission. + /// /// Parameters: /// /// * [UserPreferencesUpdateDto] userPreferencesUpdateDto (required): @@ -617,7 +671,10 @@ class UsersApi { return null; } - /// Performs an HTTP 'PUT /users/me' operation and returns the [Response]. + /// This endpoint requires the `user.update` permission. + /// + /// Note: This method returns the HTTP [Response]. + /// /// Parameters: /// /// * [UserUpdateMeDto] userUpdateMeDto (required): @@ -646,6 +703,8 @@ class UsersApi { ); } + /// This endpoint requires the `user.update` permission. + /// /// Parameters: /// /// * [UserUpdateMeDto] userUpdateMeDto (required): diff --git a/mobile/openapi/lib/api_client.dart b/mobile/openapi/lib/api_client.dart index 603163f00e..bd306cb216 100644 --- a/mobile/openapi/lib/api_client.dart +++ b/mobile/openapi/lib/api_client.dart @@ -570,8 +570,14 @@ class ApiClient { return SyncAssetDeleteV1.fromJson(value); case 'SyncAssetExifV1': return SyncAssetExifV1.fromJson(value); + case 'SyncAssetFaceDeleteV1': + return SyncAssetFaceDeleteV1.fromJson(value); + case 'SyncAssetFaceV1': + return SyncAssetFaceV1.fromJson(value); case 'SyncAssetV1': return SyncAssetV1.fromJson(value); + case 'SyncAuthUserV1': + return SyncAuthUserV1.fromJson(value); case 'SyncEntityType': return SyncEntityTypeTypeTransformer().decode(value); case 'SyncMemoryAssetDeleteV1': @@ -726,6 +732,8 @@ class ApiClient { return UserAvatarColorTypeTransformer().decode(value); case 'UserLicense': return UserLicense.fromJson(value); + case 'UserMetadataKey': + return UserMetadataKeyTypeTransformer().decode(value); case 'UserPreferencesResponseDto': return UserPreferencesResponseDto.fromJson(value); case 'UserPreferencesUpdateDto': diff --git a/mobile/openapi/lib/api_helper.dart b/mobile/openapi/lib/api_helper.dart index 1618f4a670..098d32f4f4 100644 --- a/mobile/openapi/lib/api_helper.dart +++ b/mobile/openapi/lib/api_helper.dart @@ -151,6 +151,9 @@ String parameterToString(dynamic value) { if (value is UserAvatarColor) { return UserAvatarColorTypeTransformer().encode(value).toString(); } + if (value is UserMetadataKey) { + return UserMetadataKeyTypeTransformer().encode(value).toString(); + } if (value is UserStatus) { return UserStatusTypeTransformer().encode(value).toString(); } diff --git a/mobile/openapi/lib/model/permission.dart b/mobile/openapi/lib/model/permission.dart index a85b5002bf..ec67d81be4 100644 --- a/mobile/openapi/lib/model/permission.dart +++ b/mobile/openapi/lib/model/permission.dart @@ -36,25 +36,35 @@ class Permission { static const assetPeriodRead = Permission._(r'asset.read'); static const assetPeriodUpdate = Permission._(r'asset.update'); static const assetPeriodDelete = Permission._(r'asset.delete'); + static const assetPeriodStatistics = Permission._(r'asset.statistics'); static const assetPeriodShare = Permission._(r'asset.share'); static const assetPeriodView = Permission._(r'asset.view'); static const assetPeriodDownload = Permission._(r'asset.download'); static const assetPeriodUpload = Permission._(r'asset.upload'); + static const assetPeriodReplace = Permission._(r'asset.replace'); static const albumPeriodCreate = Permission._(r'album.create'); static const albumPeriodRead = Permission._(r'album.read'); static const albumPeriodUpdate = Permission._(r'album.update'); static const albumPeriodDelete = Permission._(r'album.delete'); static const albumPeriodStatistics = Permission._(r'album.statistics'); - static const albumPeriodAddAsset = Permission._(r'album.addAsset'); - static const albumPeriodRemoveAsset = Permission._(r'album.removeAsset'); static const albumPeriodShare = Permission._(r'album.share'); static const albumPeriodDownload = Permission._(r'album.download'); + static const albumAssetPeriodCreate = Permission._(r'albumAsset.create'); + static const albumAssetPeriodDelete = Permission._(r'albumAsset.delete'); + static const albumUserPeriodCreate = Permission._(r'albumUser.create'); + static const albumUserPeriodUpdate = Permission._(r'albumUser.update'); + static const albumUserPeriodDelete = Permission._(r'albumUser.delete'); + static const authPeriodChangePassword = Permission._(r'auth.changePassword'); static const authDevicePeriodDelete = Permission._(r'authDevice.delete'); static const archivePeriodRead = Permission._(r'archive.read'); + static const duplicatePeriodRead = Permission._(r'duplicate.read'); + static const duplicatePeriodDelete = Permission._(r'duplicate.delete'); static const facePeriodCreate = Permission._(r'face.create'); static const facePeriodRead = Permission._(r'face.read'); static const facePeriodUpdate = Permission._(r'face.update'); static const facePeriodDelete = Permission._(r'face.delete'); + static const jobPeriodCreate = Permission._(r'job.create'); + static const jobPeriodRead = Permission._(r'job.read'); static const libraryPeriodCreate = Permission._(r'library.create'); static const libraryPeriodRead = Permission._(r'library.read'); static const libraryPeriodUpdate = Permission._(r'library.update'); @@ -66,6 +76,9 @@ class Permission { static const memoryPeriodRead = Permission._(r'memory.read'); static const memoryPeriodUpdate = Permission._(r'memory.update'); static const memoryPeriodDelete = Permission._(r'memory.delete'); + static const memoryPeriodStatistics = Permission._(r'memory.statistics'); + static const memoryAssetPeriodCreate = Permission._(r'memoryAsset.create'); + static const memoryAssetPeriodDelete = Permission._(r'memoryAsset.delete'); static const notificationPeriodCreate = Permission._(r'notification.create'); static const notificationPeriodRead = Permission._(r'notification.read'); static const notificationPeriodUpdate = Permission._(r'notification.update'); @@ -81,6 +94,16 @@ class Permission { static const personPeriodStatistics = Permission._(r'person.statistics'); static const personPeriodMerge = Permission._(r'person.merge'); static const personPeriodReassign = Permission._(r'person.reassign'); + static const pinCodePeriodCreate = Permission._(r'pinCode.create'); + static const pinCodePeriodUpdate = Permission._(r'pinCode.update'); + static const pinCodePeriodDelete = Permission._(r'pinCode.delete'); + static const serverPeriodAbout = Permission._(r'server.about'); + static const serverPeriodApkLinks = Permission._(r'server.apkLinks'); + static const serverPeriodStorage = Permission._(r'server.storage'); + static const serverPeriodStatistics = Permission._(r'server.statistics'); + static const serverLicensePeriodRead = Permission._(r'serverLicense.read'); + static const serverLicensePeriodUpdate = Permission._(r'serverLicense.update'); + static const serverLicensePeriodDelete = Permission._(r'serverLicense.delete'); static const sessionPeriodCreate = Permission._(r'session.create'); static const sessionPeriodRead = Permission._(r'session.read'); static const sessionPeriodUpdate = Permission._(r'session.update'); @@ -94,6 +117,10 @@ class Permission { static const stackPeriodRead = Permission._(r'stack.read'); static const stackPeriodUpdate = Permission._(r'stack.update'); static const stackPeriodDelete = Permission._(r'stack.delete'); + static const syncPeriodStream = Permission._(r'sync.stream'); + static const syncCheckpointPeriodRead = Permission._(r'syncCheckpoint.read'); + static const syncCheckpointPeriodUpdate = Permission._(r'syncCheckpoint.update'); + static const syncCheckpointPeriodDelete = Permission._(r'syncCheckpoint.delete'); static const systemConfigPeriodRead = Permission._(r'systemConfig.read'); static const systemConfigPeriodUpdate = Permission._(r'systemConfig.update'); static const systemMetadataPeriodRead = Permission._(r'systemMetadata.read'); @@ -103,10 +130,25 @@ class Permission { static const tagPeriodUpdate = Permission._(r'tag.update'); static const tagPeriodDelete = Permission._(r'tag.delete'); static const tagPeriodAsset = Permission._(r'tag.asset'); - static const adminPeriodUserPeriodCreate = Permission._(r'admin.user.create'); - static const adminPeriodUserPeriodRead = Permission._(r'admin.user.read'); - static const adminPeriodUserPeriodUpdate = Permission._(r'admin.user.update'); - static const adminPeriodUserPeriodDelete = Permission._(r'admin.user.delete'); + static const userPeriodRead = Permission._(r'user.read'); + static const userPeriodUpdate = Permission._(r'user.update'); + static const userLicensePeriodCreate = Permission._(r'userLicense.create'); + static const userLicensePeriodRead = Permission._(r'userLicense.read'); + static const userLicensePeriodUpdate = Permission._(r'userLicense.update'); + static const userLicensePeriodDelete = Permission._(r'userLicense.delete'); + static const userOnboardingPeriodRead = Permission._(r'userOnboarding.read'); + static const userOnboardingPeriodUpdate = Permission._(r'userOnboarding.update'); + static const userOnboardingPeriodDelete = Permission._(r'userOnboarding.delete'); + static const userPreferencePeriodRead = Permission._(r'userPreference.read'); + static const userPreferencePeriodUpdate = Permission._(r'userPreference.update'); + static const userProfileImagePeriodCreate = Permission._(r'userProfileImage.create'); + static const userProfileImagePeriodRead = Permission._(r'userProfileImage.read'); + static const userProfileImagePeriodUpdate = Permission._(r'userProfileImage.update'); + static const userProfileImagePeriodDelete = Permission._(r'userProfileImage.delete'); + static const adminUserPeriodCreate = Permission._(r'adminUser.create'); + static const adminUserPeriodRead = Permission._(r'adminUser.read'); + static const adminUserPeriodUpdate = Permission._(r'adminUser.update'); + static const adminUserPeriodDelete = Permission._(r'adminUser.delete'); /// List of all possible values in this [enum][Permission]. static const values = [ @@ -123,25 +165,35 @@ class Permission { assetPeriodRead, assetPeriodUpdate, assetPeriodDelete, + assetPeriodStatistics, assetPeriodShare, assetPeriodView, assetPeriodDownload, assetPeriodUpload, + assetPeriodReplace, albumPeriodCreate, albumPeriodRead, albumPeriodUpdate, albumPeriodDelete, albumPeriodStatistics, - albumPeriodAddAsset, - albumPeriodRemoveAsset, albumPeriodShare, albumPeriodDownload, + albumAssetPeriodCreate, + albumAssetPeriodDelete, + albumUserPeriodCreate, + albumUserPeriodUpdate, + albumUserPeriodDelete, + authPeriodChangePassword, authDevicePeriodDelete, archivePeriodRead, + duplicatePeriodRead, + duplicatePeriodDelete, facePeriodCreate, facePeriodRead, facePeriodUpdate, facePeriodDelete, + jobPeriodCreate, + jobPeriodRead, libraryPeriodCreate, libraryPeriodRead, libraryPeriodUpdate, @@ -153,6 +205,9 @@ class Permission { memoryPeriodRead, memoryPeriodUpdate, memoryPeriodDelete, + memoryPeriodStatistics, + memoryAssetPeriodCreate, + memoryAssetPeriodDelete, notificationPeriodCreate, notificationPeriodRead, notificationPeriodUpdate, @@ -168,6 +223,16 @@ class Permission { personPeriodStatistics, personPeriodMerge, personPeriodReassign, + pinCodePeriodCreate, + pinCodePeriodUpdate, + pinCodePeriodDelete, + serverPeriodAbout, + serverPeriodApkLinks, + serverPeriodStorage, + serverPeriodStatistics, + serverLicensePeriodRead, + serverLicensePeriodUpdate, + serverLicensePeriodDelete, sessionPeriodCreate, sessionPeriodRead, sessionPeriodUpdate, @@ -181,6 +246,10 @@ class Permission { stackPeriodRead, stackPeriodUpdate, stackPeriodDelete, + syncPeriodStream, + syncCheckpointPeriodRead, + syncCheckpointPeriodUpdate, + syncCheckpointPeriodDelete, systemConfigPeriodRead, systemConfigPeriodUpdate, systemMetadataPeriodRead, @@ -190,10 +259,25 @@ class Permission { tagPeriodUpdate, tagPeriodDelete, tagPeriodAsset, - adminPeriodUserPeriodCreate, - adminPeriodUserPeriodRead, - adminPeriodUserPeriodUpdate, - adminPeriodUserPeriodDelete, + userPeriodRead, + userPeriodUpdate, + userLicensePeriodCreate, + userLicensePeriodRead, + userLicensePeriodUpdate, + userLicensePeriodDelete, + userOnboardingPeriodRead, + userOnboardingPeriodUpdate, + userOnboardingPeriodDelete, + userPreferencePeriodRead, + userPreferencePeriodUpdate, + userProfileImagePeriodCreate, + userProfileImagePeriodRead, + userProfileImagePeriodUpdate, + userProfileImagePeriodDelete, + adminUserPeriodCreate, + adminUserPeriodRead, + adminUserPeriodUpdate, + adminUserPeriodDelete, ]; static Permission? fromJson(dynamic value) => PermissionTypeTransformer().decode(value); @@ -245,25 +329,35 @@ class PermissionTypeTransformer { case r'asset.read': return Permission.assetPeriodRead; case r'asset.update': return Permission.assetPeriodUpdate; case r'asset.delete': return Permission.assetPeriodDelete; + case r'asset.statistics': return Permission.assetPeriodStatistics; case r'asset.share': return Permission.assetPeriodShare; case r'asset.view': return Permission.assetPeriodView; case r'asset.download': return Permission.assetPeriodDownload; case r'asset.upload': return Permission.assetPeriodUpload; + case r'asset.replace': return Permission.assetPeriodReplace; case r'album.create': return Permission.albumPeriodCreate; case r'album.read': return Permission.albumPeriodRead; case r'album.update': return Permission.albumPeriodUpdate; case r'album.delete': return Permission.albumPeriodDelete; case r'album.statistics': return Permission.albumPeriodStatistics; - case r'album.addAsset': return Permission.albumPeriodAddAsset; - case r'album.removeAsset': return Permission.albumPeriodRemoveAsset; case r'album.share': return Permission.albumPeriodShare; case r'album.download': return Permission.albumPeriodDownload; + case r'albumAsset.create': return Permission.albumAssetPeriodCreate; + case r'albumAsset.delete': return Permission.albumAssetPeriodDelete; + case r'albumUser.create': return Permission.albumUserPeriodCreate; + case r'albumUser.update': return Permission.albumUserPeriodUpdate; + case r'albumUser.delete': return Permission.albumUserPeriodDelete; + case r'auth.changePassword': return Permission.authPeriodChangePassword; case r'authDevice.delete': return Permission.authDevicePeriodDelete; case r'archive.read': return Permission.archivePeriodRead; + case r'duplicate.read': return Permission.duplicatePeriodRead; + case r'duplicate.delete': return Permission.duplicatePeriodDelete; case r'face.create': return Permission.facePeriodCreate; case r'face.read': return Permission.facePeriodRead; case r'face.update': return Permission.facePeriodUpdate; case r'face.delete': return Permission.facePeriodDelete; + case r'job.create': return Permission.jobPeriodCreate; + case r'job.read': return Permission.jobPeriodRead; case r'library.create': return Permission.libraryPeriodCreate; case r'library.read': return Permission.libraryPeriodRead; case r'library.update': return Permission.libraryPeriodUpdate; @@ -275,6 +369,9 @@ class PermissionTypeTransformer { case r'memory.read': return Permission.memoryPeriodRead; case r'memory.update': return Permission.memoryPeriodUpdate; case r'memory.delete': return Permission.memoryPeriodDelete; + case r'memory.statistics': return Permission.memoryPeriodStatistics; + case r'memoryAsset.create': return Permission.memoryAssetPeriodCreate; + case r'memoryAsset.delete': return Permission.memoryAssetPeriodDelete; case r'notification.create': return Permission.notificationPeriodCreate; case r'notification.read': return Permission.notificationPeriodRead; case r'notification.update': return Permission.notificationPeriodUpdate; @@ -290,6 +387,16 @@ class PermissionTypeTransformer { case r'person.statistics': return Permission.personPeriodStatistics; case r'person.merge': return Permission.personPeriodMerge; case r'person.reassign': return Permission.personPeriodReassign; + case r'pinCode.create': return Permission.pinCodePeriodCreate; + case r'pinCode.update': return Permission.pinCodePeriodUpdate; + case r'pinCode.delete': return Permission.pinCodePeriodDelete; + case r'server.about': return Permission.serverPeriodAbout; + case r'server.apkLinks': return Permission.serverPeriodApkLinks; + case r'server.storage': return Permission.serverPeriodStorage; + case r'server.statistics': return Permission.serverPeriodStatistics; + case r'serverLicense.read': return Permission.serverLicensePeriodRead; + case r'serverLicense.update': return Permission.serverLicensePeriodUpdate; + case r'serverLicense.delete': return Permission.serverLicensePeriodDelete; case r'session.create': return Permission.sessionPeriodCreate; case r'session.read': return Permission.sessionPeriodRead; case r'session.update': return Permission.sessionPeriodUpdate; @@ -303,6 +410,10 @@ class PermissionTypeTransformer { case r'stack.read': return Permission.stackPeriodRead; case r'stack.update': return Permission.stackPeriodUpdate; case r'stack.delete': return Permission.stackPeriodDelete; + case r'sync.stream': return Permission.syncPeriodStream; + case r'syncCheckpoint.read': return Permission.syncCheckpointPeriodRead; + case r'syncCheckpoint.update': return Permission.syncCheckpointPeriodUpdate; + case r'syncCheckpoint.delete': return Permission.syncCheckpointPeriodDelete; case r'systemConfig.read': return Permission.systemConfigPeriodRead; case r'systemConfig.update': return Permission.systemConfigPeriodUpdate; case r'systemMetadata.read': return Permission.systemMetadataPeriodRead; @@ -312,10 +423,25 @@ class PermissionTypeTransformer { case r'tag.update': return Permission.tagPeriodUpdate; case r'tag.delete': return Permission.tagPeriodDelete; case r'tag.asset': return Permission.tagPeriodAsset; - case r'admin.user.create': return Permission.adminPeriodUserPeriodCreate; - case r'admin.user.read': return Permission.adminPeriodUserPeriodRead; - case r'admin.user.update': return Permission.adminPeriodUserPeriodUpdate; - case r'admin.user.delete': return Permission.adminPeriodUserPeriodDelete; + case r'user.read': return Permission.userPeriodRead; + case r'user.update': return Permission.userPeriodUpdate; + case r'userLicense.create': return Permission.userLicensePeriodCreate; + case r'userLicense.read': return Permission.userLicensePeriodRead; + case r'userLicense.update': return Permission.userLicensePeriodUpdate; + case r'userLicense.delete': return Permission.userLicensePeriodDelete; + case r'userOnboarding.read': return Permission.userOnboardingPeriodRead; + case r'userOnboarding.update': return Permission.userOnboardingPeriodUpdate; + case r'userOnboarding.delete': return Permission.userOnboardingPeriodDelete; + case r'userPreference.read': return Permission.userPreferencePeriodRead; + case r'userPreference.update': return Permission.userPreferencePeriodUpdate; + case r'userProfileImage.create': return Permission.userProfileImagePeriodCreate; + case r'userProfileImage.read': return Permission.userProfileImagePeriodRead; + case r'userProfileImage.update': return Permission.userProfileImagePeriodUpdate; + case r'userProfileImage.delete': return Permission.userProfileImagePeriodDelete; + case r'adminUser.create': return Permission.adminUserPeriodCreate; + case r'adminUser.read': return Permission.adminUserPeriodRead; + case r'adminUser.update': return Permission.adminUserPeriodUpdate; + case r'adminUser.delete': return Permission.adminUserPeriodDelete; default: if (!allowNull) { throw ArgumentError('Unknown enum value to decode: $data'); diff --git a/mobile/openapi/lib/model/shared_link_create_dto.dart b/mobile/openapi/lib/model/shared_link_create_dto.dart index bc96b31fd2..644227bd6e 100644 --- a/mobile/openapi/lib/model/shared_link_create_dto.dart +++ b/mobile/openapi/lib/model/shared_link_create_dto.dart @@ -21,6 +21,7 @@ class SharedLinkCreateDto { this.expiresAt, this.password, this.showMetadata = true, + this.slug, required this.type, }); @@ -44,26 +45,16 @@ class SharedLinkCreateDto { List assetIds; - /// - /// Please note: This property should have been non-nullable! Since the specification file - /// does not include a default value (using the "default:" property), however, the generated - /// source code must fall back to having a nullable type. - /// Consider adding a "default:" property in the specification file to hide this note. - /// String? description; DateTime? expiresAt; - /// - /// Please note: This property should have been non-nullable! Since the specification file - /// does not include a default value (using the "default:" property), however, the generated - /// source code must fall back to having a nullable type. - /// Consider adding a "default:" property in the specification file to hide this note. - /// String? password; bool showMetadata; + String? slug; + SharedLinkType type; @override @@ -76,6 +67,7 @@ class SharedLinkCreateDto { other.expiresAt == expiresAt && other.password == password && other.showMetadata == showMetadata && + other.slug == slug && other.type == type; @override @@ -89,10 +81,11 @@ class SharedLinkCreateDto { (expiresAt == null ? 0 : expiresAt!.hashCode) + (password == null ? 0 : password!.hashCode) + (showMetadata.hashCode) + + (slug == null ? 0 : slug!.hashCode) + (type.hashCode); @override - String toString() => 'SharedLinkCreateDto[albumId=$albumId, allowDownload=$allowDownload, allowUpload=$allowUpload, assetIds=$assetIds, description=$description, expiresAt=$expiresAt, password=$password, showMetadata=$showMetadata, type=$type]'; + String toString() => 'SharedLinkCreateDto[albumId=$albumId, allowDownload=$allowDownload, allowUpload=$allowUpload, assetIds=$assetIds, description=$description, expiresAt=$expiresAt, password=$password, showMetadata=$showMetadata, slug=$slug, type=$type]'; Map toJson() { final json = {}; @@ -124,6 +117,11 @@ class SharedLinkCreateDto { // json[r'password'] = null; } json[r'showMetadata'] = this.showMetadata; + if (this.slug != null) { + json[r'slug'] = this.slug; + } else { + // json[r'slug'] = null; + } json[r'type'] = this.type; return json; } @@ -147,6 +145,7 @@ class SharedLinkCreateDto { expiresAt: mapDateTime(json, r'expiresAt', r''), password: mapValueOfType(json, r'password'), showMetadata: mapValueOfType(json, r'showMetadata') ?? true, + slug: mapValueOfType(json, r'slug'), type: SharedLinkType.fromJson(json[r'type'])!, ); } diff --git a/mobile/openapi/lib/model/shared_link_edit_dto.dart b/mobile/openapi/lib/model/shared_link_edit_dto.dart index a394ba9b3b..f13bc6977b 100644 --- a/mobile/openapi/lib/model/shared_link_edit_dto.dart +++ b/mobile/openapi/lib/model/shared_link_edit_dto.dart @@ -20,6 +20,7 @@ class SharedLinkEditDto { this.expiresAt, this.password, this.showMetadata, + this.slug, }); /// @@ -47,22 +48,10 @@ class SharedLinkEditDto { /// bool? changeExpiryTime; - /// - /// Please note: This property should have been non-nullable! Since the specification file - /// does not include a default value (using the "default:" property), however, the generated - /// source code must fall back to having a nullable type. - /// Consider adding a "default:" property in the specification file to hide this note. - /// String? description; DateTime? expiresAt; - /// - /// Please note: This property should have been non-nullable! Since the specification file - /// does not include a default value (using the "default:" property), however, the generated - /// source code must fall back to having a nullable type. - /// Consider adding a "default:" property in the specification file to hide this note. - /// String? password; /// @@ -73,6 +62,8 @@ class SharedLinkEditDto { /// bool? showMetadata; + String? slug; + @override bool operator ==(Object other) => identical(this, other) || other is SharedLinkEditDto && other.allowDownload == allowDownload && @@ -81,7 +72,8 @@ class SharedLinkEditDto { other.description == description && other.expiresAt == expiresAt && other.password == password && - other.showMetadata == showMetadata; + other.showMetadata == showMetadata && + other.slug == slug; @override int get hashCode => @@ -92,10 +84,11 @@ class SharedLinkEditDto { (description == null ? 0 : description!.hashCode) + (expiresAt == null ? 0 : expiresAt!.hashCode) + (password == null ? 0 : password!.hashCode) + - (showMetadata == null ? 0 : showMetadata!.hashCode); + (showMetadata == null ? 0 : showMetadata!.hashCode) + + (slug == null ? 0 : slug!.hashCode); @override - String toString() => 'SharedLinkEditDto[allowDownload=$allowDownload, allowUpload=$allowUpload, changeExpiryTime=$changeExpiryTime, description=$description, expiresAt=$expiresAt, password=$password, showMetadata=$showMetadata]'; + String toString() => 'SharedLinkEditDto[allowDownload=$allowDownload, allowUpload=$allowUpload, changeExpiryTime=$changeExpiryTime, description=$description, expiresAt=$expiresAt, password=$password, showMetadata=$showMetadata, slug=$slug]'; Map toJson() { final json = {}; @@ -134,6 +127,11 @@ class SharedLinkEditDto { } else { // json[r'showMetadata'] = null; } + if (this.slug != null) { + json[r'slug'] = this.slug; + } else { + // json[r'slug'] = null; + } return json; } @@ -153,6 +151,7 @@ class SharedLinkEditDto { expiresAt: mapDateTime(json, r'expiresAt', r''), password: mapValueOfType(json, r'password'), showMetadata: mapValueOfType(json, r'showMetadata'), + slug: mapValueOfType(json, r'slug'), ); } return null; diff --git a/mobile/openapi/lib/model/shared_link_response_dto.dart b/mobile/openapi/lib/model/shared_link_response_dto.dart index 9cc8b3ac80..d81e1dfa31 100644 --- a/mobile/openapi/lib/model/shared_link_response_dto.dart +++ b/mobile/openapi/lib/model/shared_link_response_dto.dart @@ -24,6 +24,7 @@ class SharedLinkResponseDto { required this.key, required this.password, required this.showMetadata, + required this.slug, this.token, required this.type, required this.userId, @@ -57,6 +58,8 @@ class SharedLinkResponseDto { bool showMetadata; + String? slug; + String? token; SharedLinkType type; @@ -76,6 +79,7 @@ class SharedLinkResponseDto { other.key == key && other.password == password && other.showMetadata == showMetadata && + other.slug == slug && other.token == token && other.type == type && other.userId == userId; @@ -94,12 +98,13 @@ class SharedLinkResponseDto { (key.hashCode) + (password == null ? 0 : password!.hashCode) + (showMetadata.hashCode) + + (slug == null ? 0 : slug!.hashCode) + (token == null ? 0 : token!.hashCode) + (type.hashCode) + (userId.hashCode); @override - String toString() => 'SharedLinkResponseDto[album=$album, allowDownload=$allowDownload, allowUpload=$allowUpload, assets=$assets, createdAt=$createdAt, description=$description, expiresAt=$expiresAt, id=$id, key=$key, password=$password, showMetadata=$showMetadata, token=$token, type=$type, userId=$userId]'; + String toString() => 'SharedLinkResponseDto[album=$album, allowDownload=$allowDownload, allowUpload=$allowUpload, assets=$assets, createdAt=$createdAt, description=$description, expiresAt=$expiresAt, id=$id, key=$key, password=$password, showMetadata=$showMetadata, slug=$slug, token=$token, type=$type, userId=$userId]'; Map toJson() { final json = {}; @@ -130,6 +135,11 @@ class SharedLinkResponseDto { // json[r'password'] = null; } json[r'showMetadata'] = this.showMetadata; + if (this.slug != null) { + json[r'slug'] = this.slug; + } else { + // json[r'slug'] = null; + } if (this.token != null) { json[r'token'] = this.token; } else { @@ -160,6 +170,7 @@ class SharedLinkResponseDto { key: mapValueOfType(json, r'key')!, password: mapValueOfType(json, r'password'), showMetadata: mapValueOfType(json, r'showMetadata')!, + slug: mapValueOfType(json, r'slug'), token: mapValueOfType(json, r'token'), type: SharedLinkType.fromJson(json[r'type'])!, userId: mapValueOfType(json, r'userId')!, @@ -220,6 +231,7 @@ class SharedLinkResponseDto { 'key', 'password', 'showMetadata', + 'slug', 'type', 'userId', }; diff --git a/mobile/openapi/lib/model/sync_asset_face_delete_v1.dart b/mobile/openapi/lib/model/sync_asset_face_delete_v1.dart new file mode 100644 index 0000000000..0992bfdcba --- /dev/null +++ b/mobile/openapi/lib/model/sync_asset_face_delete_v1.dart @@ -0,0 +1,99 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class SyncAssetFaceDeleteV1 { + /// Returns a new [SyncAssetFaceDeleteV1] instance. + SyncAssetFaceDeleteV1({ + required this.assetFaceId, + }); + + String assetFaceId; + + @override + bool operator ==(Object other) => identical(this, other) || other is SyncAssetFaceDeleteV1 && + other.assetFaceId == assetFaceId; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (assetFaceId.hashCode); + + @override + String toString() => 'SyncAssetFaceDeleteV1[assetFaceId=$assetFaceId]'; + + Map toJson() { + final json = {}; + json[r'assetFaceId'] = this.assetFaceId; + return json; + } + + /// Returns a new [SyncAssetFaceDeleteV1] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static SyncAssetFaceDeleteV1? fromJson(dynamic value) { + upgradeDto(value, "SyncAssetFaceDeleteV1"); + if (value is Map) { + final json = value.cast(); + + return SyncAssetFaceDeleteV1( + assetFaceId: mapValueOfType(json, r'assetFaceId')!, + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = SyncAssetFaceDeleteV1.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = SyncAssetFaceDeleteV1.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of SyncAssetFaceDeleteV1-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = SyncAssetFaceDeleteV1.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'assetFaceId', + }; +} + diff --git a/mobile/openapi/lib/model/sync_asset_face_v1.dart b/mobile/openapi/lib/model/sync_asset_face_v1.dart new file mode 100644 index 0000000000..60d1766e34 --- /dev/null +++ b/mobile/openapi/lib/model/sync_asset_face_v1.dart @@ -0,0 +1,175 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class SyncAssetFaceV1 { + /// Returns a new [SyncAssetFaceV1] instance. + SyncAssetFaceV1({ + required this.assetId, + required this.boundingBoxX1, + required this.boundingBoxX2, + required this.boundingBoxY1, + required this.boundingBoxY2, + required this.id, + required this.imageHeight, + required this.imageWidth, + required this.personId, + required this.sourceType, + }); + + String assetId; + + int boundingBoxX1; + + int boundingBoxX2; + + int boundingBoxY1; + + int boundingBoxY2; + + String id; + + int imageHeight; + + int imageWidth; + + String? personId; + + String sourceType; + + @override + bool operator ==(Object other) => identical(this, other) || other is SyncAssetFaceV1 && + other.assetId == assetId && + other.boundingBoxX1 == boundingBoxX1 && + other.boundingBoxX2 == boundingBoxX2 && + other.boundingBoxY1 == boundingBoxY1 && + other.boundingBoxY2 == boundingBoxY2 && + other.id == id && + other.imageHeight == imageHeight && + other.imageWidth == imageWidth && + other.personId == personId && + other.sourceType == sourceType; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (assetId.hashCode) + + (boundingBoxX1.hashCode) + + (boundingBoxX2.hashCode) + + (boundingBoxY1.hashCode) + + (boundingBoxY2.hashCode) + + (id.hashCode) + + (imageHeight.hashCode) + + (imageWidth.hashCode) + + (personId == null ? 0 : personId!.hashCode) + + (sourceType.hashCode); + + @override + String toString() => 'SyncAssetFaceV1[assetId=$assetId, boundingBoxX1=$boundingBoxX1, boundingBoxX2=$boundingBoxX2, boundingBoxY1=$boundingBoxY1, boundingBoxY2=$boundingBoxY2, id=$id, imageHeight=$imageHeight, imageWidth=$imageWidth, personId=$personId, sourceType=$sourceType]'; + + Map toJson() { + final json = {}; + json[r'assetId'] = this.assetId; + json[r'boundingBoxX1'] = this.boundingBoxX1; + json[r'boundingBoxX2'] = this.boundingBoxX2; + json[r'boundingBoxY1'] = this.boundingBoxY1; + json[r'boundingBoxY2'] = this.boundingBoxY2; + json[r'id'] = this.id; + json[r'imageHeight'] = this.imageHeight; + json[r'imageWidth'] = this.imageWidth; + if (this.personId != null) { + json[r'personId'] = this.personId; + } else { + // json[r'personId'] = null; + } + json[r'sourceType'] = this.sourceType; + return json; + } + + /// Returns a new [SyncAssetFaceV1] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static SyncAssetFaceV1? fromJson(dynamic value) { + upgradeDto(value, "SyncAssetFaceV1"); + if (value is Map) { + final json = value.cast(); + + return SyncAssetFaceV1( + assetId: mapValueOfType(json, r'assetId')!, + boundingBoxX1: mapValueOfType(json, r'boundingBoxX1')!, + boundingBoxX2: mapValueOfType(json, r'boundingBoxX2')!, + boundingBoxY1: mapValueOfType(json, r'boundingBoxY1')!, + boundingBoxY2: mapValueOfType(json, r'boundingBoxY2')!, + id: mapValueOfType(json, r'id')!, + imageHeight: mapValueOfType(json, r'imageHeight')!, + imageWidth: mapValueOfType(json, r'imageWidth')!, + personId: mapValueOfType(json, r'personId'), + sourceType: mapValueOfType(json, r'sourceType')!, + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = SyncAssetFaceV1.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = SyncAssetFaceV1.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of SyncAssetFaceV1-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = SyncAssetFaceV1.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'assetId', + 'boundingBoxX1', + 'boundingBoxX2', + 'boundingBoxY1', + 'boundingBoxY2', + 'id', + 'imageHeight', + 'imageWidth', + 'personId', + 'sourceType', + }; +} + diff --git a/mobile/openapi/lib/model/sync_asset_v1.dart b/mobile/openapi/lib/model/sync_asset_v1.dart index 4c42d08a5f..f0d5097ea4 100644 --- a/mobile/openapi/lib/model/sync_asset_v1.dart +++ b/mobile/openapi/lib/model/sync_asset_v1.dart @@ -20,6 +20,7 @@ class SyncAssetV1 { required this.fileModifiedAt, required this.id, required this.isFavorite, + required this.libraryId, required this.livePhotoVideoId, required this.localDateTime, required this.originalFileName, @@ -44,6 +45,8 @@ class SyncAssetV1 { bool isFavorite; + String? libraryId; + String? livePhotoVideoId; DateTime? localDateTime; @@ -69,6 +72,7 @@ class SyncAssetV1 { other.fileModifiedAt == fileModifiedAt && other.id == id && other.isFavorite == isFavorite && + other.libraryId == libraryId && other.livePhotoVideoId == livePhotoVideoId && other.localDateTime == localDateTime && other.originalFileName == originalFileName && @@ -88,6 +92,7 @@ class SyncAssetV1 { (fileModifiedAt == null ? 0 : fileModifiedAt!.hashCode) + (id.hashCode) + (isFavorite.hashCode) + + (libraryId == null ? 0 : libraryId!.hashCode) + (livePhotoVideoId == null ? 0 : livePhotoVideoId!.hashCode) + (localDateTime == null ? 0 : localDateTime!.hashCode) + (originalFileName.hashCode) + @@ -98,7 +103,7 @@ class SyncAssetV1 { (visibility.hashCode); @override - String toString() => 'SyncAssetV1[checksum=$checksum, deletedAt=$deletedAt, duration=$duration, fileCreatedAt=$fileCreatedAt, fileModifiedAt=$fileModifiedAt, id=$id, isFavorite=$isFavorite, livePhotoVideoId=$livePhotoVideoId, localDateTime=$localDateTime, originalFileName=$originalFileName, ownerId=$ownerId, stackId=$stackId, thumbhash=$thumbhash, type=$type, visibility=$visibility]'; + String toString() => 'SyncAssetV1[checksum=$checksum, deletedAt=$deletedAt, duration=$duration, fileCreatedAt=$fileCreatedAt, fileModifiedAt=$fileModifiedAt, id=$id, isFavorite=$isFavorite, libraryId=$libraryId, livePhotoVideoId=$livePhotoVideoId, localDateTime=$localDateTime, originalFileName=$originalFileName, ownerId=$ownerId, stackId=$stackId, thumbhash=$thumbhash, type=$type, visibility=$visibility]'; Map toJson() { final json = {}; @@ -125,6 +130,11 @@ class SyncAssetV1 { } json[r'id'] = this.id; json[r'isFavorite'] = this.isFavorite; + if (this.libraryId != null) { + json[r'libraryId'] = this.libraryId; + } else { + // json[r'libraryId'] = null; + } if (this.livePhotoVideoId != null) { json[r'livePhotoVideoId'] = this.livePhotoVideoId; } else { @@ -168,6 +178,7 @@ class SyncAssetV1 { fileModifiedAt: mapDateTime(json, r'fileModifiedAt', r''), id: mapValueOfType(json, r'id')!, isFavorite: mapValueOfType(json, r'isFavorite')!, + libraryId: mapValueOfType(json, r'libraryId'), livePhotoVideoId: mapValueOfType(json, r'livePhotoVideoId'), localDateTime: mapDateTime(json, r'localDateTime', r''), originalFileName: mapValueOfType(json, r'originalFileName')!, @@ -230,6 +241,7 @@ class SyncAssetV1 { 'fileModifiedAt', 'id', 'isFavorite', + 'libraryId', 'livePhotoVideoId', 'localDateTime', 'originalFileName', diff --git a/mobile/openapi/lib/model/sync_auth_user_v1.dart b/mobile/openapi/lib/model/sync_auth_user_v1.dart new file mode 100644 index 0000000000..1dab7f47e3 --- /dev/null +++ b/mobile/openapi/lib/model/sync_auth_user_v1.dart @@ -0,0 +1,215 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + +class SyncAuthUserV1 { + /// Returns a new [SyncAuthUserV1] instance. + SyncAuthUserV1({ + required this.avatarColor, + required this.deletedAt, + required this.email, + required this.hasProfileImage, + required this.id, + required this.isAdmin, + required this.name, + required this.oauthId, + required this.pinCode, + required this.profileChangedAt, + required this.quotaSizeInBytes, + required this.quotaUsageInBytes, + required this.storageLabel, + }); + + UserAvatarColor? avatarColor; + + DateTime? deletedAt; + + String email; + + bool hasProfileImage; + + String id; + + bool isAdmin; + + String name; + + String oauthId; + + String? pinCode; + + DateTime profileChangedAt; + + int? quotaSizeInBytes; + + int quotaUsageInBytes; + + String? storageLabel; + + @override + bool operator ==(Object other) => identical(this, other) || other is SyncAuthUserV1 && + other.avatarColor == avatarColor && + other.deletedAt == deletedAt && + other.email == email && + other.hasProfileImage == hasProfileImage && + other.id == id && + other.isAdmin == isAdmin && + other.name == name && + other.oauthId == oauthId && + other.pinCode == pinCode && + other.profileChangedAt == profileChangedAt && + other.quotaSizeInBytes == quotaSizeInBytes && + other.quotaUsageInBytes == quotaUsageInBytes && + other.storageLabel == storageLabel; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (avatarColor == null ? 0 : avatarColor!.hashCode) + + (deletedAt == null ? 0 : deletedAt!.hashCode) + + (email.hashCode) + + (hasProfileImage.hashCode) + + (id.hashCode) + + (isAdmin.hashCode) + + (name.hashCode) + + (oauthId.hashCode) + + (pinCode == null ? 0 : pinCode!.hashCode) + + (profileChangedAt.hashCode) + + (quotaSizeInBytes == null ? 0 : quotaSizeInBytes!.hashCode) + + (quotaUsageInBytes.hashCode) + + (storageLabel == null ? 0 : storageLabel!.hashCode); + + @override + String toString() => 'SyncAuthUserV1[avatarColor=$avatarColor, deletedAt=$deletedAt, email=$email, hasProfileImage=$hasProfileImage, id=$id, isAdmin=$isAdmin, name=$name, oauthId=$oauthId, pinCode=$pinCode, profileChangedAt=$profileChangedAt, quotaSizeInBytes=$quotaSizeInBytes, quotaUsageInBytes=$quotaUsageInBytes, storageLabel=$storageLabel]'; + + Map toJson() { + final json = {}; + if (this.avatarColor != null) { + json[r'avatarColor'] = this.avatarColor; + } else { + // json[r'avatarColor'] = null; + } + if (this.deletedAt != null) { + json[r'deletedAt'] = this.deletedAt!.toUtc().toIso8601String(); + } else { + // json[r'deletedAt'] = null; + } + json[r'email'] = this.email; + json[r'hasProfileImage'] = this.hasProfileImage; + json[r'id'] = this.id; + json[r'isAdmin'] = this.isAdmin; + json[r'name'] = this.name; + json[r'oauthId'] = this.oauthId; + if (this.pinCode != null) { + json[r'pinCode'] = this.pinCode; + } else { + // json[r'pinCode'] = null; + } + json[r'profileChangedAt'] = this.profileChangedAt.toUtc().toIso8601String(); + if (this.quotaSizeInBytes != null) { + json[r'quotaSizeInBytes'] = this.quotaSizeInBytes; + } else { + // json[r'quotaSizeInBytes'] = null; + } + json[r'quotaUsageInBytes'] = this.quotaUsageInBytes; + if (this.storageLabel != null) { + json[r'storageLabel'] = this.storageLabel; + } else { + // json[r'storageLabel'] = null; + } + return json; + } + + /// Returns a new [SyncAuthUserV1] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static SyncAuthUserV1? fromJson(dynamic value) { + upgradeDto(value, "SyncAuthUserV1"); + if (value is Map) { + final json = value.cast(); + + return SyncAuthUserV1( + avatarColor: UserAvatarColor.fromJson(json[r'avatarColor']), + deletedAt: mapDateTime(json, r'deletedAt', r''), + email: mapValueOfType(json, r'email')!, + hasProfileImage: mapValueOfType(json, r'hasProfileImage')!, + id: mapValueOfType(json, r'id')!, + isAdmin: mapValueOfType(json, r'isAdmin')!, + name: mapValueOfType(json, r'name')!, + oauthId: mapValueOfType(json, r'oauthId')!, + pinCode: mapValueOfType(json, r'pinCode'), + profileChangedAt: mapDateTime(json, r'profileChangedAt', r'')!, + quotaSizeInBytes: mapValueOfType(json, r'quotaSizeInBytes'), + quotaUsageInBytes: mapValueOfType(json, r'quotaUsageInBytes')!, + storageLabel: mapValueOfType(json, r'storageLabel'), + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = SyncAuthUserV1.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = SyncAuthUserV1.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of SyncAuthUserV1-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = SyncAuthUserV1.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'avatarColor', + 'deletedAt', + 'email', + 'hasProfileImage', + 'id', + 'isAdmin', + 'name', + 'oauthId', + 'pinCode', + 'profileChangedAt', + 'quotaSizeInBytes', + 'quotaUsageInBytes', + 'storageLabel', + }; +} + diff --git a/mobile/openapi/lib/model/sync_entity_type.dart b/mobile/openapi/lib/model/sync_entity_type.dart index 61f94401c7..5368126923 100644 --- a/mobile/openapi/lib/model/sync_entity_type.dart +++ b/mobile/openapi/lib/model/sync_entity_type.dart @@ -23,6 +23,7 @@ class SyncEntityType { String toJson() => value; + static const authUserV1 = SyncEntityType._(r'AuthUserV1'); static const userV1 = SyncEntityType._(r'UserV1'); static const userDeleteV1 = SyncEntityType._(r'UserDeleteV1'); static const assetV1 = SyncEntityType._(r'AssetV1'); @@ -58,6 +59,8 @@ class SyncEntityType { static const stackDeleteV1 = SyncEntityType._(r'StackDeleteV1'); static const personV1 = SyncEntityType._(r'PersonV1'); static const personDeleteV1 = SyncEntityType._(r'PersonDeleteV1'); + static const assetFaceV1 = SyncEntityType._(r'AssetFaceV1'); + static const assetFaceDeleteV1 = SyncEntityType._(r'AssetFaceDeleteV1'); static const userMetadataV1 = SyncEntityType._(r'UserMetadataV1'); static const userMetadataDeleteV1 = SyncEntityType._(r'UserMetadataDeleteV1'); static const syncAckV1 = SyncEntityType._(r'SyncAckV1'); @@ -65,6 +68,7 @@ class SyncEntityType { /// List of all possible values in this [enum][SyncEntityType]. static const values = [ + authUserV1, userV1, userDeleteV1, assetV1, @@ -100,6 +104,8 @@ class SyncEntityType { stackDeleteV1, personV1, personDeleteV1, + assetFaceV1, + assetFaceDeleteV1, userMetadataV1, userMetadataDeleteV1, syncAckV1, @@ -142,6 +148,7 @@ class SyncEntityTypeTypeTransformer { SyncEntityType? decode(dynamic data, {bool allowNull = true}) { if (data != null) { switch (data) { + case r'AuthUserV1': return SyncEntityType.authUserV1; case r'UserV1': return SyncEntityType.userV1; case r'UserDeleteV1': return SyncEntityType.userDeleteV1; case r'AssetV1': return SyncEntityType.assetV1; @@ -177,6 +184,8 @@ class SyncEntityTypeTypeTransformer { case r'StackDeleteV1': return SyncEntityType.stackDeleteV1; case r'PersonV1': return SyncEntityType.personV1; case r'PersonDeleteV1': return SyncEntityType.personDeleteV1; + case r'AssetFaceV1': return SyncEntityType.assetFaceV1; + case r'AssetFaceDeleteV1': return SyncEntityType.assetFaceDeleteV1; case r'UserMetadataV1': return SyncEntityType.userMetadataV1; case r'UserMetadataDeleteV1': return SyncEntityType.userMetadataDeleteV1; case r'SyncAckV1': return SyncEntityType.syncAckV1; diff --git a/mobile/openapi/lib/model/sync_person_v1.dart b/mobile/openapi/lib/model/sync_person_v1.dart index e86c22f64b..6749beb3e1 100644 --- a/mobile/openapi/lib/model/sync_person_v1.dart +++ b/mobile/openapi/lib/model/sync_person_v1.dart @@ -22,7 +22,6 @@ class SyncPersonV1 { required this.isHidden, required this.name, required this.ownerId, - required this.thumbnailPath, required this.updatedAt, }); @@ -44,8 +43,6 @@ class SyncPersonV1 { String ownerId; - String thumbnailPath; - DateTime updatedAt; @override @@ -59,7 +56,6 @@ class SyncPersonV1 { other.isHidden == isHidden && other.name == name && other.ownerId == ownerId && - other.thumbnailPath == thumbnailPath && other.updatedAt == updatedAt; @override @@ -74,11 +70,10 @@ class SyncPersonV1 { (isHidden.hashCode) + (name.hashCode) + (ownerId.hashCode) + - (thumbnailPath.hashCode) + (updatedAt.hashCode); @override - String toString() => 'SyncPersonV1[birthDate=$birthDate, color=$color, createdAt=$createdAt, faceAssetId=$faceAssetId, id=$id, isFavorite=$isFavorite, isHidden=$isHidden, name=$name, ownerId=$ownerId, thumbnailPath=$thumbnailPath, updatedAt=$updatedAt]'; + String toString() => 'SyncPersonV1[birthDate=$birthDate, color=$color, createdAt=$createdAt, faceAssetId=$faceAssetId, id=$id, isFavorite=$isFavorite, isHidden=$isHidden, name=$name, ownerId=$ownerId, updatedAt=$updatedAt]'; Map toJson() { final json = {}; @@ -103,7 +98,6 @@ class SyncPersonV1 { json[r'isHidden'] = this.isHidden; json[r'name'] = this.name; json[r'ownerId'] = this.ownerId; - json[r'thumbnailPath'] = this.thumbnailPath; json[r'updatedAt'] = this.updatedAt.toUtc().toIso8601String(); return json; } @@ -126,7 +120,6 @@ class SyncPersonV1 { isHidden: mapValueOfType(json, r'isHidden')!, name: mapValueOfType(json, r'name')!, ownerId: mapValueOfType(json, r'ownerId')!, - thumbnailPath: mapValueOfType(json, r'thumbnailPath')!, updatedAt: mapDateTime(json, r'updatedAt', r'')!, ); } @@ -184,7 +177,6 @@ class SyncPersonV1 { 'isHidden', 'name', 'ownerId', - 'thumbnailPath', 'updatedAt', }; } diff --git a/mobile/openapi/lib/model/sync_request_type.dart b/mobile/openapi/lib/model/sync_request_type.dart index 75ce852f9f..8a1857366e 100644 --- a/mobile/openapi/lib/model/sync_request_type.dart +++ b/mobile/openapi/lib/model/sync_request_type.dart @@ -30,6 +30,7 @@ class SyncRequestType { static const albumAssetExifsV1 = SyncRequestType._(r'AlbumAssetExifsV1'); static const assetsV1 = SyncRequestType._(r'AssetsV1'); static const assetExifsV1 = SyncRequestType._(r'AssetExifsV1'); + static const authUsersV1 = SyncRequestType._(r'AuthUsersV1'); static const memoriesV1 = SyncRequestType._(r'MemoriesV1'); static const memoryToAssetsV1 = SyncRequestType._(r'MemoryToAssetsV1'); static const partnersV1 = SyncRequestType._(r'PartnersV1'); @@ -39,6 +40,7 @@ class SyncRequestType { static const stacksV1 = SyncRequestType._(r'StacksV1'); static const usersV1 = SyncRequestType._(r'UsersV1'); static const peopleV1 = SyncRequestType._(r'PeopleV1'); + static const assetFacesV1 = SyncRequestType._(r'AssetFacesV1'); static const userMetadataV1 = SyncRequestType._(r'UserMetadataV1'); /// List of all possible values in this [enum][SyncRequestType]. @@ -50,6 +52,7 @@ class SyncRequestType { albumAssetExifsV1, assetsV1, assetExifsV1, + authUsersV1, memoriesV1, memoryToAssetsV1, partnersV1, @@ -59,6 +62,7 @@ class SyncRequestType { stacksV1, usersV1, peopleV1, + assetFacesV1, userMetadataV1, ]; @@ -105,6 +109,7 @@ class SyncRequestTypeTypeTransformer { case r'AlbumAssetExifsV1': return SyncRequestType.albumAssetExifsV1; case r'AssetsV1': return SyncRequestType.assetsV1; case r'AssetExifsV1': return SyncRequestType.assetExifsV1; + case r'AuthUsersV1': return SyncRequestType.authUsersV1; case r'MemoriesV1': return SyncRequestType.memoriesV1; case r'MemoryToAssetsV1': return SyncRequestType.memoryToAssetsV1; case r'PartnersV1': return SyncRequestType.partnersV1; @@ -114,6 +119,7 @@ class SyncRequestTypeTypeTransformer { case r'StacksV1': return SyncRequestType.stacksV1; case r'UsersV1': return SyncRequestType.usersV1; case r'PeopleV1': return SyncRequestType.peopleV1; + case r'AssetFacesV1': return SyncRequestType.assetFacesV1; case r'UserMetadataV1': return SyncRequestType.userMetadataV1; default: if (!allowNull) { diff --git a/mobile/openapi/lib/model/sync_user_metadata_delete_v1.dart b/mobile/openapi/lib/model/sync_user_metadata_delete_v1.dart index e9dd733295..f39acc617b 100644 --- a/mobile/openapi/lib/model/sync_user_metadata_delete_v1.dart +++ b/mobile/openapi/lib/model/sync_user_metadata_delete_v1.dart @@ -17,7 +17,7 @@ class SyncUserMetadataDeleteV1 { required this.userId, }); - String key; + UserMetadataKey key; String userId; @@ -51,7 +51,7 @@ class SyncUserMetadataDeleteV1 { final json = value.cast(); return SyncUserMetadataDeleteV1( - key: mapValueOfType(json, r'key')!, + key: UserMetadataKey.fromJson(json[r'key'])!, userId: mapValueOfType(json, r'userId')!, ); } diff --git a/mobile/openapi/lib/model/sync_user_metadata_v1.dart b/mobile/openapi/lib/model/sync_user_metadata_v1.dart index 0b060dc17c..cf39b6d960 100644 --- a/mobile/openapi/lib/model/sync_user_metadata_v1.dart +++ b/mobile/openapi/lib/model/sync_user_metadata_v1.dart @@ -18,7 +18,7 @@ class SyncUserMetadataV1 { required this.value, }); - String key; + UserMetadataKey key; String userId; @@ -57,7 +57,7 @@ class SyncUserMetadataV1 { final json = value.cast(); return SyncUserMetadataV1( - key: mapValueOfType(json, r'key')!, + key: UserMetadataKey.fromJson(json[r'key'])!, userId: mapValueOfType(json, r'userId')!, value: mapValueOfType(json, r'value')!, ); diff --git a/mobile/openapi/lib/model/sync_user_v1.dart b/mobile/openapi/lib/model/sync_user_v1.dart index b9b41bb723..b9fad5ae8c 100644 --- a/mobile/openapi/lib/model/sync_user_v1.dart +++ b/mobile/openapi/lib/model/sync_user_v1.dart @@ -13,48 +13,70 @@ part of openapi.api; class SyncUserV1 { /// Returns a new [SyncUserV1] instance. SyncUserV1({ + required this.avatarColor, required this.deletedAt, required this.email, + required this.hasProfileImage, required this.id, required this.name, + required this.profileChangedAt, }); + UserAvatarColor? avatarColor; + DateTime? deletedAt; String email; + bool hasProfileImage; + String id; String name; + DateTime profileChangedAt; + @override bool operator ==(Object other) => identical(this, other) || other is SyncUserV1 && + other.avatarColor == avatarColor && other.deletedAt == deletedAt && other.email == email && + other.hasProfileImage == hasProfileImage && other.id == id && - other.name == name; + other.name == name && + other.profileChangedAt == profileChangedAt; @override int get hashCode => // ignore: unnecessary_parenthesis + (avatarColor == null ? 0 : avatarColor!.hashCode) + (deletedAt == null ? 0 : deletedAt!.hashCode) + (email.hashCode) + + (hasProfileImage.hashCode) + (id.hashCode) + - (name.hashCode); + (name.hashCode) + + (profileChangedAt.hashCode); @override - String toString() => 'SyncUserV1[deletedAt=$deletedAt, email=$email, id=$id, name=$name]'; + String toString() => 'SyncUserV1[avatarColor=$avatarColor, deletedAt=$deletedAt, email=$email, hasProfileImage=$hasProfileImage, id=$id, name=$name, profileChangedAt=$profileChangedAt]'; Map toJson() { final json = {}; + if (this.avatarColor != null) { + json[r'avatarColor'] = this.avatarColor; + } else { + // json[r'avatarColor'] = null; + } if (this.deletedAt != null) { json[r'deletedAt'] = this.deletedAt!.toUtc().toIso8601String(); } else { // json[r'deletedAt'] = null; } json[r'email'] = this.email; + json[r'hasProfileImage'] = this.hasProfileImage; json[r'id'] = this.id; json[r'name'] = this.name; + json[r'profileChangedAt'] = this.profileChangedAt.toUtc().toIso8601String(); return json; } @@ -67,10 +89,13 @@ class SyncUserV1 { final json = value.cast(); return SyncUserV1( + avatarColor: UserAvatarColor.fromJson(json[r'avatarColor']), deletedAt: mapDateTime(json, r'deletedAt', r''), email: mapValueOfType(json, r'email')!, + hasProfileImage: mapValueOfType(json, r'hasProfileImage')!, id: mapValueOfType(json, r'id')!, name: mapValueOfType(json, r'name')!, + profileChangedAt: mapDateTime(json, r'profileChangedAt', r'')!, ); } return null; @@ -118,10 +143,13 @@ class SyncUserV1 { /// The list of required keys that must be present in a JSON. static const requiredKeys = { + 'avatarColor', 'deletedAt', 'email', + 'hasProfileImage', 'id', 'name', + 'profileChangedAt', }; } diff --git a/mobile/openapi/lib/model/user_metadata_key.dart b/mobile/openapi/lib/model/user_metadata_key.dart new file mode 100644 index 0000000000..845b5ae9bb --- /dev/null +++ b/mobile/openapi/lib/model/user_metadata_key.dart @@ -0,0 +1,88 @@ +// +// AUTO-GENERATED FILE, DO NOT MODIFY! +// +// @dart=2.18 + +// ignore_for_file: unused_element, unused_import +// ignore_for_file: always_put_required_named_parameters_first +// ignore_for_file: constant_identifier_names +// ignore_for_file: lines_longer_than_80_chars + +part of openapi.api; + + +class UserMetadataKey { + /// Instantiate a new enum with the provided [value]. + const UserMetadataKey._(this.value); + + /// The underlying value of this enum member. + final String value; + + @override + String toString() => value; + + String toJson() => value; + + static const preferences = UserMetadataKey._(r'preferences'); + static const license = UserMetadataKey._(r'license'); + static const onboarding = UserMetadataKey._(r'onboarding'); + + /// List of all possible values in this [enum][UserMetadataKey]. + static const values = [ + preferences, + license, + onboarding, + ]; + + static UserMetadataKey? fromJson(dynamic value) => UserMetadataKeyTypeTransformer().decode(value); + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = UserMetadataKey.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } +} + +/// Transformation class that can [encode] an instance of [UserMetadataKey] to String, +/// and [decode] dynamic data back to [UserMetadataKey]. +class UserMetadataKeyTypeTransformer { + factory UserMetadataKeyTypeTransformer() => _instance ??= const UserMetadataKeyTypeTransformer._(); + + const UserMetadataKeyTypeTransformer._(); + + String encode(UserMetadataKey data) => data.value; + + /// Decodes a [dynamic value][data] to a UserMetadataKey. + /// + /// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully, + /// then null is returned. However, if [allowNull] is false and the [dynamic value][data] + /// cannot be decoded successfully, then an [UnimplementedError] is thrown. + /// + /// The [allowNull] is very handy when an API changes and a new enum value is added or removed, + /// and users are still using an old app with the old code. + UserMetadataKey? decode(dynamic data, {bool allowNull = true}) { + if (data != null) { + switch (data) { + case r'preferences': return UserMetadataKey.preferences; + case r'license': return UserMetadataKey.license; + case r'onboarding': return UserMetadataKey.onboarding; + default: + if (!allowNull) { + throw ArgumentError('Unknown enum value to decode: $data'); + } + } + } + return null; + } + + /// Singleton [UserMetadataKeyTypeTransformer] instance. + static UserMetadataKeyTypeTransformer? _instance; +} + diff --git a/mobile/pigeon/native_sync_api.dart b/mobile/pigeon/native_sync_api.dart index 4f14b7a0b9..e84c814c3d 100644 --- a/mobile/pigeon/native_sync_api.dart +++ b/mobile/pigeon/native_sync_api.dart @@ -5,8 +5,7 @@ import 'package:pigeon/pigeon.dart'; dartOut: 'lib/platform/native_sync_api.g.dart', swiftOut: 'ios/Runner/Sync/Messages.g.swift', swiftOptions: SwiftOptions(), - kotlinOut: - 'android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt', + kotlinOut: 'android/app/src/main/kotlin/app/alextran/immich/sync/Messages.g.kt', kotlinOptions: KotlinOptions(package: 'app.alextran.immich.sync'), dartOptions: DartOptions(), dartPackageName: 'immich_mobile', @@ -24,6 +23,7 @@ class PlatformAsset { final int? height; final int durationInSeconds; final int orientation; + final bool isFavorite; const PlatformAsset({ required this.id, @@ -35,6 +35,7 @@ class PlatformAsset { this.height, this.durationInSeconds = 0, this.orientation = 0, + this.isFavorite = false, }); } diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index 95aa866ea3..aa78658006 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -1556,6 +1556,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.28.0" + scroll_date_picker: + dependency: "direct main" + description: + name: scroll_date_picker + sha256: "1b00a3e24d92c77aa84d5856cfe6a57fd5df5f645ce1a6af0feb3ec84bdffb34" + url: "https://pub.dev" + source: hosted + version: "3.8.0" scrollable_positioned_list: dependency: "direct main" description: @@ -1689,6 +1697,14 @@ packages: description: flutter source: sdk version: "0.0.0" + sliver_tools: + dependency: "direct main" + description: + name: sliver_tools + sha256: eae28220badfb9d0559207badcbbc9ad5331aac829a88cb0964d330d2a4636a6 + url: "https://pub.dev" + source: hosted + version: "0.2.12" socket_io_client: dependency: "direct main" description: @@ -2154,5 +2170,5 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.7.0 <4.0.0" - flutter: ">=3.32.6" + dart: ">=3.8.0 <4.0.0" + flutter: ">=3.32.8" diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index bbffb9f51b..020ce4676e 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -2,11 +2,11 @@ name: immich_mobile description: Immich - selfhosted backup media file on mobile phone publish_to: 'none' -version: 1.135.3+204 +version: 1.137.3+3002 environment: - sdk: '>=3.3.0 <4.0.0' - flutter: 3.32.6 + sdk: '>=3.8.0 <4.0.0' + flutter: 3.32.8 isar_version: &isar_version 3.1.8 @@ -63,6 +63,7 @@ dependencies: scrollable_positioned_list: ^0.3.8 share_handler: ^0.0.22 share_plus: ^10.1.4 + sliver_tools: ^0.2.12 socket_io_client: ^2.0.3+1 stream_transform: ^2.1.1 thumbhash: 0.1.0+1 @@ -71,6 +72,7 @@ dependencies: uuid: ^4.5.1 wakelock_plus: ^1.2.10 worker_manager: ^7.2.3 + scroll_date_picker: ^3.8.0 native_video_player: git: diff --git a/mobile/test/domain/service.mock.dart b/mobile/test/domain/service.mock.dart index f4c5a32a4b..8293faf125 100644 --- a/mobile/test/domain/service.mock.dart +++ b/mobile/test/domain/service.mock.dart @@ -2,6 +2,8 @@ import 'package:immich_mobile/domain/services/store.service.dart'; import 'package:immich_mobile/domain/services/user.service.dart'; import 'package:immich_mobile/domain/utils/background_sync.dart'; import 'package:immich_mobile/platform/native_sync_api.g.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; +import 'package:immich_mobile/services/upload.service.dart'; import 'package:mocktail/mocktail.dart'; class MockStoreService extends Mock implements StoreService {} @@ -11,3 +13,7 @@ class MockUserService extends Mock implements UserService {} class MockBackgroundSyncManager extends Mock implements BackgroundSyncManager {} class MockNativeSyncApi extends Mock implements NativeSyncApi {} + +class MockAppSettingsService extends Mock implements AppSettingsService {} + +class MockUploadService extends Mock implements UploadService {} diff --git a/mobile/test/domain/services/hash_service_test.dart b/mobile/test/domain/services/hash_service_test.dart index b2ab803ac2..7969131e7f 100644 --- a/mobile/test/domain/services/hash_service_test.dart +++ b/mobile/test/domain/services/hash_service_test.dart @@ -21,10 +21,7 @@ void main() { late MockLocalAssetRepository mockAssetRepo; late MockStorageRepository mockStorageRepo; late MockNativeSyncApi mockNativeApi; - final sortBy = { - SortLocalAlbumsBy.backupSelection, - SortLocalAlbumsBy.isIosSharedAlbum, - }; + final sortBy = {SortLocalAlbumsBy.backupSelection, SortLocalAlbumsBy.isIosSharedAlbum}; setUp(() { mockAlbumRepo = MockLocalAlbumRepository(); @@ -43,15 +40,15 @@ void main() { registerFallbackValue(LocalAssetStub.image1); when(() => mockAssetRepo.updateHashes(any())).thenAnswer((_) async => {}); + when(() => mockStorageRepo.clearCache()).thenAnswer((_) async => {}); }); group('HashService hashAssets', () { test('skips albums with no assets to hash', () async { - when(() => mockAlbumRepo.getAll(sortBy: sortBy)).thenAnswer( - (_) async => [LocalAlbumStub.recent.copyWith(assetCount: 0)], - ); - when(() => mockAlbumRepo.getAssetsToHash(LocalAlbumStub.recent.id)) - .thenAnswer((_) async => []); + when( + () => mockAlbumRepo.getAll(sortBy: sortBy), + ).thenAnswer((_) async => [LocalAlbumStub.recent.copyWith(assetCount: 0)]); + when(() => mockAlbumRepo.getAssetsToHash(LocalAlbumStub.recent.id)).thenAnswer((_) async => []); await sut.hashAssets(); @@ -64,12 +61,9 @@ void main() { test('skips assets without files', () async { final album = LocalAlbumStub.recent; final asset = LocalAssetStub.image1; - when(() => mockAlbumRepo.getAll(sortBy: sortBy)) - .thenAnswer((_) async => [album]); - when(() => mockAlbumRepo.getAssetsToHash(album.id)) - .thenAnswer((_) async => [asset]); - when(() => mockStorageRepo.getFileForAsset(asset.id)) - .thenAnswer((_) async => null); + when(() => mockAlbumRepo.getAll(sortBy: sortBy)).thenAnswer((_) async => [album]); + when(() => mockAlbumRepo.getAssetsToHash(album.id)).thenAnswer((_) async => [asset]); + when(() => mockStorageRepo.getFileForAsset(asset.id)).thenAnswer((_) async => null); await sut.hashAssets(); @@ -85,22 +79,15 @@ void main() { when(() => mockFile.length()).thenAnswer((_) async => 1000); when(() => mockFile.path).thenReturn('image-path'); - when(() => mockAlbumRepo.getAll(sortBy: sortBy)) - .thenAnswer((_) async => [album]); - when(() => mockAlbumRepo.getAssetsToHash(album.id)) - .thenAnswer((_) async => [asset]); - when(() => mockStorageRepo.getFileForAsset(asset.id)) - .thenAnswer((_) async => mockFile); - when(() => mockNativeApi.hashPaths(['image-path'])).thenAnswer( - (_) async => [hash], - ); + when(() => mockAlbumRepo.getAll(sortBy: sortBy)).thenAnswer((_) async => [album]); + when(() => mockAlbumRepo.getAssetsToHash(album.id)).thenAnswer((_) async => [asset]); + when(() => mockStorageRepo.getFileForAsset(asset.id)).thenAnswer((_) async => mockFile); + when(() => mockNativeApi.hashPaths(['image-path'])).thenAnswer((_) async => [hash]); await sut.hashAssets(); verify(() => mockNativeApi.hashPaths(['image-path'])).called(1); - final captured = verify(() => mockAssetRepo.updateHashes(captureAny())) - .captured - .first as List; + final captured = verify(() => mockAssetRepo.updateHashes(captureAny())).captured.first as List; expect(captured.length, 1); expect(captured[0].checksum, base64.encode(hash)); }); @@ -112,21 +99,15 @@ void main() { when(() => mockFile.length()).thenAnswer((_) async => 1000); when(() => mockFile.path).thenReturn('image-path'); - when(() => mockAlbumRepo.getAll(sortBy: sortBy)) - .thenAnswer((_) async => [album]); - when(() => mockAlbumRepo.getAssetsToHash(album.id)) - .thenAnswer((_) async => [asset]); - when(() => mockStorageRepo.getFileForAsset(asset.id)) - .thenAnswer((_) async => mockFile); - when(() => mockNativeApi.hashPaths(['image-path'])) - .thenAnswer((_) async => [null]); + when(() => mockAlbumRepo.getAll(sortBy: sortBy)).thenAnswer((_) async => [album]); + when(() => mockAlbumRepo.getAssetsToHash(album.id)).thenAnswer((_) async => [asset]); + when(() => mockStorageRepo.getFileForAsset(asset.id)).thenAnswer((_) async => mockFile); + when(() => mockNativeApi.hashPaths(['image-path'])).thenAnswer((_) async => [null]); when(() => mockAssetRepo.updateHashes(any())).thenAnswer((_) async => {}); await sut.hashAssets(); - final captured = verify(() => mockAssetRepo.updateHashes(captureAny())) - .captured - .first as List; + final captured = verify(() => mockAssetRepo.updateHashes(captureAny())).captured.first as List; expect(captured.length, 0); }); @@ -137,23 +118,17 @@ void main() { when(() => mockFile.length()).thenAnswer((_) async => 1000); when(() => mockFile.path).thenReturn('image-path'); - when(() => mockAlbumRepo.getAll(sortBy: sortBy)) - .thenAnswer((_) async => [album]); - when(() => mockAlbumRepo.getAssetsToHash(album.id)) - .thenAnswer((_) async => [asset]); - when(() => mockStorageRepo.getFileForAsset(asset.id)) - .thenAnswer((_) async => mockFile); + when(() => mockAlbumRepo.getAll(sortBy: sortBy)).thenAnswer((_) async => [album]); + when(() => mockAlbumRepo.getAssetsToHash(album.id)).thenAnswer((_) async => [asset]); + when(() => mockStorageRepo.getFileForAsset(asset.id)).thenAnswer((_) async => mockFile); final invalidHash = Uint8List.fromList([1, 2, 3]); - when(() => mockNativeApi.hashPaths(['image-path'])) - .thenAnswer((_) async => [invalidHash]); + when(() => mockNativeApi.hashPaths(['image-path'])).thenAnswer((_) async => [invalidHash]); when(() => mockAssetRepo.updateHashes(any())).thenAnswer((_) async => {}); await sut.hashAssets(); - final captured = verify(() => mockAssetRepo.updateHashes(captureAny())) - .captured - .first as List; + final captured = verify(() => mockAssetRepo.updateHashes(captureAny())).captured.first as List; expect(captured.length, 0); }); @@ -176,18 +151,13 @@ void main() { when(() => mockFile2.length()).thenAnswer((_) async => 100); when(() => mockFile2.path).thenReturn('path-2'); - when(() => mockAlbumRepo.getAll(sortBy: sortBy)) - .thenAnswer((_) async => [album]); - when(() => mockAlbumRepo.getAssetsToHash(album.id)) - .thenAnswer((_) async => [asset1, asset2]); - when(() => mockStorageRepo.getFileForAsset(asset1.id)) - .thenAnswer((_) async => mockFile1); - when(() => mockStorageRepo.getFileForAsset(asset2.id)) - .thenAnswer((_) async => mockFile2); + when(() => mockAlbumRepo.getAll(sortBy: sortBy)).thenAnswer((_) async => [album]); + when(() => mockAlbumRepo.getAssetsToHash(album.id)).thenAnswer((_) async => [asset1, asset2]); + when(() => mockStorageRepo.getFileForAsset(asset1.id)).thenAnswer((_) async => mockFile1); + when(() => mockStorageRepo.getFileForAsset(asset2.id)).thenAnswer((_) async => mockFile2); final hash = Uint8List.fromList(List.generate(20, (i) => i)); - when(() => mockNativeApi.hashPaths(any())) - .thenAnswer((_) async => [hash]); + when(() => mockNativeApi.hashPaths(any())).thenAnswer((_) async => [hash]); when(() => mockAssetRepo.updateHashes(any())).thenAnswer((_) async => {}); await sut.hashAssets(); @@ -216,18 +186,13 @@ void main() { when(() => mockFile2.length()).thenAnswer((_) async => 100); when(() => mockFile2.path).thenReturn('path-2'); - when(() => mockAlbumRepo.getAll(sortBy: sortBy)) - .thenAnswer((_) async => [album]); - when(() => mockAlbumRepo.getAssetsToHash(album.id)) - .thenAnswer((_) async => [asset1, asset2]); - when(() => mockStorageRepo.getFileForAsset(asset1.id)) - .thenAnswer((_) async => mockFile1); - when(() => mockStorageRepo.getFileForAsset(asset2.id)) - .thenAnswer((_) async => mockFile2); + when(() => mockAlbumRepo.getAll(sortBy: sortBy)).thenAnswer((_) async => [album]); + when(() => mockAlbumRepo.getAssetsToHash(album.id)).thenAnswer((_) async => [asset1, asset2]); + when(() => mockStorageRepo.getFileForAsset(asset1.id)).thenAnswer((_) async => mockFile1); + when(() => mockStorageRepo.getFileForAsset(asset2.id)).thenAnswer((_) async => mockFile2); final hash = Uint8List.fromList(List.generate(20, (i) => i)); - when(() => mockNativeApi.hashPaths(any())) - .thenAnswer((_) async => [hash]); + when(() => mockNativeApi.hashPaths(any())).thenAnswer((_) async => [hash]); when(() => mockAssetRepo.updateHashes(any())).thenAnswer((_) async => {}); await sut.hashAssets(); @@ -248,25 +213,18 @@ void main() { when(() => mockFile2.length()).thenAnswer((_) async => 100); when(() => mockFile2.path).thenReturn('path-2'); - when(() => mockAlbumRepo.getAll(sortBy: sortBy)) - .thenAnswer((_) async => [album]); - when(() => mockAlbumRepo.getAssetsToHash(album.id)) - .thenAnswer((_) async => [asset1, asset2]); - when(() => mockStorageRepo.getFileForAsset(asset1.id)) - .thenAnswer((_) async => mockFile1); - when(() => mockStorageRepo.getFileForAsset(asset2.id)) - .thenAnswer((_) async => mockFile2); + when(() => mockAlbumRepo.getAll(sortBy: sortBy)).thenAnswer((_) async => [album]); + when(() => mockAlbumRepo.getAssetsToHash(album.id)).thenAnswer((_) async => [asset1, asset2]); + when(() => mockStorageRepo.getFileForAsset(asset1.id)).thenAnswer((_) async => mockFile1); + when(() => mockStorageRepo.getFileForAsset(asset2.id)).thenAnswer((_) async => mockFile2); final validHash = Uint8List.fromList(List.generate(20, (i) => i)); - when(() => mockNativeApi.hashPaths(['path-1', 'path-2'])) - .thenAnswer((_) async => [validHash, null]); + when(() => mockNativeApi.hashPaths(['path-1', 'path-2'])).thenAnswer((_) async => [validHash, null]); when(() => mockAssetRepo.updateHashes(any())).thenAnswer((_) async => {}); await sut.hashAssets(); - final captured = verify(() => mockAssetRepo.updateHashes(captureAny())) - .captured - .first as List; + final captured = verify(() => mockAssetRepo.updateHashes(captureAny())).captured.first as List; expect(captured.length, 1); expect(captured.first.id, asset1.id); }); diff --git a/mobile/test/domain/services/log_service_test.dart b/mobile/test/domain/services/log_service_test.dart index 3efa717f97..87b32b8298 100644 --- a/mobile/test/domain/services/log_service_test.dart +++ b/mobile/test/domain/services/log_service_test.dart @@ -37,18 +37,13 @@ void main() { registerFallbackValue(_kInfoLog); - when(() => mockLogRepo.truncate(limit: any(named: 'limit'))) - .thenAnswer((_) async => {}); - when(() => mockStoreRepo.tryGet(StoreKey.logLevel)) - .thenAnswer((_) async => LogLevel.fine.index); + when(() => mockLogRepo.truncate(limit: any(named: 'limit'))).thenAnswer((_) async => {}); + when(() => mockStoreRepo.tryGet(StoreKey.logLevel)).thenAnswer((_) async => LogLevel.fine.index); when(() => mockLogRepo.getAll()).thenAnswer((_) async => []); when(() => mockLogRepo.insert(any())).thenAnswer((_) async => true); when(() => mockLogRepo.insertAll(any())).thenAnswer((_) async => true); - sut = await LogService.create( - logRepository: mockLogRepo, - storeRepository: mockStoreRepo, - ); + sut = await LogService.create(logRepository: mockLogRepo, storeRepository: mockStoreRepo); }); tearDown(() async { @@ -57,10 +52,7 @@ void main() { group("Log Service Init:", () { test('Truncates the existing logs on init', () { - final limit = - verify(() => mockLogRepo.truncate(limit: captureAny(named: 'limit'))) - .captured - .firstOrNull as int?; + final limit = verify(() => mockLogRepo.truncate(limit: captureAny(named: 'limit'))).captured.firstOrNull as int?; expect(limit, kLogTruncateLimit); }); @@ -72,15 +64,12 @@ void main() { group("Log Service Set Level:", () { setUp(() async { - when(() => mockStoreRepo.insert(StoreKey.logLevel, any())) - .thenAnswer((_) async => true); + when(() => mockStoreRepo.insert(StoreKey.logLevel, any())).thenAnswer((_) async => true); await sut.setLogLevel(LogLevel.shout); }); test('Updates the log level in store', () { - final index = verify( - () => mockStoreRepo.insert(StoreKey.logLevel, captureAny()), - ).captured.firstOrNull; + final index = verify(() => mockStoreRepo.insert(StoreKey.logLevel, captureAny())).captured.firstOrNull; expect(index, LogLevel.shout.index); }); @@ -92,11 +81,7 @@ void main() { group("Log Service Buffer:", () { test('Buffers logs until timer elapses', () { TestUtils.fakeAsync((time) async { - sut = await LogService.create( - logRepository: mockLogRepo, - storeRepository: mockStoreRepo, - shouldBuffer: true, - ); + sut = await LogService.create(logRepository: mockLogRepo, storeRepository: mockStoreRepo, shouldBuffer: true); final logger = Logger(_kInfoLog.logger!); logger.info(_kInfoLog.message); @@ -110,11 +95,7 @@ void main() { test('Batch inserts all logs on timer', () { TestUtils.fakeAsync((time) async { - sut = await LogService.create( - logRepository: mockLogRepo, - storeRepository: mockStoreRepo, - shouldBuffer: true, - ); + sut = await LogService.create(logRepository: mockLogRepo, storeRepository: mockStoreRepo, shouldBuffer: true); final logger = Logger(_kInfoLog.logger!); logger.info(_kInfoLog.message); @@ -131,11 +112,7 @@ void main() { test('Does not buffer when off', () { TestUtils.fakeAsync((time) async { - sut = await LogService.create( - logRepository: mockLogRepo, - storeRepository: mockStoreRepo, - shouldBuffer: false, - ); + sut = await LogService.create(logRepository: mockLogRepo, storeRepository: mockStoreRepo, shouldBuffer: false); final logger = Logger(_kInfoLog.logger!); logger.info(_kInfoLog.message); @@ -165,11 +142,7 @@ void main() { test('Combines result from both DB + Buffer', () { TestUtils.fakeAsync((time) async { - sut = await LogService.create( - logRepository: mockLogRepo, - storeRepository: mockStoreRepo, - shouldBuffer: true, - ); + sut = await LogService.create(logRepository: mockLogRepo, storeRepository: mockStoreRepo, shouldBuffer: true); final logger = Logger(_kWarnLog.logger!); logger.warning(_kWarnLog.message); diff --git a/mobile/test/domain/services/store_service_test.dart b/mobile/test/domain/services/store_service_test.dart index 7eab532ef3..d23913991c 100644 --- a/mobile/test/domain/services/store_service_test.dart +++ b/mobile/test/domain/services/store_service_test.dart @@ -73,10 +73,7 @@ void main() { }); test('Throws StoreKeyNotFoundException for nonexistent keys', () { - expect( - () => sut.get(StoreKey.currentUser), - throwsA(isA()), - ); + expect(() => sut.get(StoreKey.currentUser), throwsA(isA())); }); test('Returns the stored value for the given key or the defaultValue', () { @@ -86,24 +83,18 @@ void main() { group('Store Service put:', () { setUp(() { - when(() => mockStoreRepo.insert(any>(), any())) - .thenAnswer((_) async => true); + when(() => mockStoreRepo.insert(any>(), any())).thenAnswer((_) async => true); }); test('Skip insert when value is not modified', () async { await sut.put(StoreKey.accessToken, _kAccessToken); - verifyNever( - () => mockStoreRepo.insert(StoreKey.accessToken, any()), - ); + verifyNever(() => mockStoreRepo.insert(StoreKey.accessToken, any())); }); test('Insert value when modified', () async { final newAccessToken = _kAccessToken.toUpperCase(); await sut.put(StoreKey.accessToken, newAccessToken); - verify( - () => - mockStoreRepo.insert(StoreKey.accessToken, newAccessToken), - ).called(1); + verify(() => mockStoreRepo.insert(StoreKey.accessToken, newAccessToken)).called(1); expect(sut.tryGet(StoreKey.accessToken), newAccessToken); }); }); @@ -113,8 +104,7 @@ void main() { setUp(() { valueController = StreamController.broadcast(); - when(() => mockStoreRepo.watch(any>())) - .thenAnswer((_) => valueController.stream); + when(() => mockStoreRepo.watch(any>())).thenAnswer((_) => valueController.stream); }); tearDown(() async { @@ -123,12 +113,7 @@ void main() { test('Watches a specific key for changes', () async { final stream = sut.watch(StoreKey.accessToken); - final events = [ - _kAccessToken, - _kAccessToken.toUpperCase(), - null, - _kAccessToken.toLowerCase(), - ]; + final events = [_kAccessToken, _kAccessToken.toUpperCase(), null, _kAccessToken.toLowerCase()]; expectLater(stream, emitsInOrder(events)); @@ -143,14 +128,12 @@ void main() { group('Store Service delete:', () { setUp(() { - when(() => mockStoreRepo.delete(any>())) - .thenAnswer((_) async => true); + when(() => mockStoreRepo.delete(any>())).thenAnswer((_) async => true); }); test('Removes the value from the DB', () async { await sut.delete(StoreKey.accessToken); - verify(() => mockStoreRepo.delete(StoreKey.accessToken)) - .called(1); + verify(() => mockStoreRepo.delete(StoreKey.accessToken)).called(1); }); test('Removes the value from the cache', () async { diff --git a/mobile/test/domain/services/sync_stream_service_test.dart b/mobile/test/domain/services/sync_stream_service_test.dart index c9fd8104e4..46e585faa0 100644 --- a/mobile/test/domain/services/sync_stream_service_test.dart +++ b/mobile/test/domain/services/sync_stream_service_test.dart @@ -42,74 +42,46 @@ void main() { when(() => mockAbortCallbackWrapper()).thenReturn(false); - when(() => mockSyncApiRepo.streamChanges(any())) - .thenAnswer((invocation) async { + when(() => mockSyncApiRepo.streamChanges(any())).thenAnswer((invocation) async { handleEventsCallback = invocation.positionalArguments.first; }); when(() => mockSyncApiRepo.ack(any())).thenAnswer((_) async => {}); - when(() => mockSyncStreamRepo.updateUsersV1(any())) - .thenAnswer(successHandler); - when(() => mockSyncStreamRepo.deleteUsersV1(any())) - .thenAnswer(successHandler); - when(() => mockSyncStreamRepo.updatePartnerV1(any())) - .thenAnswer(successHandler); - when(() => mockSyncStreamRepo.deletePartnerV1(any())) - .thenAnswer(successHandler); - when(() => mockSyncStreamRepo.updateAssetsV1(any())) - .thenAnswer(successHandler); + when(() => mockSyncStreamRepo.updateUsersV1(any())).thenAnswer(successHandler); + when(() => mockSyncStreamRepo.deleteUsersV1(any())).thenAnswer(successHandler); + when(() => mockSyncStreamRepo.updatePartnerV1(any())).thenAnswer(successHandler); + when(() => mockSyncStreamRepo.deletePartnerV1(any())).thenAnswer(successHandler); + when(() => mockSyncStreamRepo.updateAssetsV1(any())).thenAnswer(successHandler); when( - () => mockSyncStreamRepo.updateAssetsV1( - any(), - debugLabel: any(named: 'debugLabel'), - ), + () => mockSyncStreamRepo.updateAssetsV1(any(), debugLabel: any(named: 'debugLabel')), ).thenAnswer(successHandler); - when(() => mockSyncStreamRepo.deleteAssetsV1(any())) - .thenAnswer(successHandler); + when(() => mockSyncStreamRepo.deleteAssetsV1(any())).thenAnswer(successHandler); when( - () => mockSyncStreamRepo.deleteAssetsV1( - any(), - debugLabel: any(named: 'debugLabel'), - ), + () => mockSyncStreamRepo.deleteAssetsV1(any(), debugLabel: any(named: 'debugLabel')), ).thenAnswer(successHandler); - when(() => mockSyncStreamRepo.updateAssetsExifV1(any())) - .thenAnswer(successHandler); + when(() => mockSyncStreamRepo.updateAssetsExifV1(any())).thenAnswer(successHandler); when( - () => mockSyncStreamRepo.updateAssetsExifV1( - any(), - debugLabel: any(named: 'debugLabel'), - ), + () => mockSyncStreamRepo.updateAssetsExifV1(any(), debugLabel: any(named: 'debugLabel')), ).thenAnswer(successHandler); - when(() => mockSyncStreamRepo.updateMemoriesV1(any())) - .thenAnswer(successHandler); - when(() => mockSyncStreamRepo.deleteMemoriesV1(any())) - .thenAnswer(successHandler); - when(() => mockSyncStreamRepo.updateMemoryAssetsV1(any())) - .thenAnswer(successHandler); - when(() => mockSyncStreamRepo.deleteMemoryAssetsV1(any())) - .thenAnswer(successHandler); + when(() => mockSyncStreamRepo.updateMemoriesV1(any())).thenAnswer(successHandler); + when(() => mockSyncStreamRepo.deleteMemoriesV1(any())).thenAnswer(successHandler); + when(() => mockSyncStreamRepo.updateMemoryAssetsV1(any())).thenAnswer(successHandler); + when(() => mockSyncStreamRepo.deleteMemoryAssetsV1(any())).thenAnswer(successHandler); when( - () => mockSyncStreamRepo.updateStacksV1( - any(), - debugLabel: any(named: 'debugLabel'), - ), + () => mockSyncStreamRepo.updateStacksV1(any(), debugLabel: any(named: 'debugLabel')), ).thenAnswer(successHandler); when( - () => mockSyncStreamRepo.deleteStacksV1( - any(), - debugLabel: any(named: 'debugLabel'), - ), + () => mockSyncStreamRepo.deleteStacksV1(any(), debugLabel: any(named: 'debugLabel')), ).thenAnswer(successHandler); - when(() => mockSyncStreamRepo.updateUserMetadatasV1(any())) - .thenAnswer(successHandler); - when(() => mockSyncStreamRepo.deleteUserMetadatasV1(any())) - .thenAnswer(successHandler); + when(() => mockSyncStreamRepo.updateUserMetadatasV1(any())).thenAnswer(successHandler); + when(() => mockSyncStreamRepo.deleteUserMetadatasV1(any())).thenAnswer(successHandler); + when(() => mockSyncStreamRepo.updatePeopleV1(any())).thenAnswer(successHandler); + when(() => mockSyncStreamRepo.deletePeopleV1(any())).thenAnswer(successHandler); + when(() => mockSyncStreamRepo.updateAssetFacesV1(any())).thenAnswer(successHandler); + when(() => mockSyncStreamRepo.deleteAssetFacesV1(any())).thenAnswer(successHandler); - sut = SyncStreamService( - syncApiRepository: mockSyncApiRepo, - syncStreamRepository: mockSyncStreamRepo, - ); + sut = SyncStreamService(syncApiRepository: mockSyncApiRepo, syncStreamRepository: mockSyncStreamRepo); }); Future simulateEvents(List events) async { @@ -118,41 +90,35 @@ void main() { } group("SyncStreamService - _handleEvents", () { - test( - "processes events and acks successfully when handlers succeed", - () async { - final events = [ - SyncStreamStub.userDeleteV1, - SyncStreamStub.userV1Admin, - SyncStreamStub.userV1User, - SyncStreamStub.partnerDeleteV1, - SyncStreamStub.partnerV1, - ]; - - await simulateEvents(events); - - verifyInOrder([ - () => mockSyncStreamRepo.deleteUsersV1(any()), - () => mockSyncApiRepo.ack(["2"]), - () => mockSyncStreamRepo.updateUsersV1(any()), - () => mockSyncApiRepo.ack(["5"]), - () => mockSyncStreamRepo.deletePartnerV1(any()), - () => mockSyncApiRepo.ack(["4"]), - () => mockSyncStreamRepo.updatePartnerV1(any()), - () => mockSyncApiRepo.ack(["3"]), - ]); - verifyNever(() => mockAbortCallbackWrapper()); - }, - ); - - test("processes final batch correctly", () async { + test("processes events and acks successfully when handlers succeed", () async { final events = [ SyncStreamStub.userDeleteV1, SyncStreamStub.userV1Admin, + SyncStreamStub.userV1User, + SyncStreamStub.partnerDeleteV1, + SyncStreamStub.partnerV1, ]; await simulateEvents(events); + verifyInOrder([ + () => mockSyncStreamRepo.deleteUsersV1(any()), + () => mockSyncApiRepo.ack(["2"]), + () => mockSyncStreamRepo.updateUsersV1(any()), + () => mockSyncApiRepo.ack(["5"]), + () => mockSyncStreamRepo.deletePartnerV1(any()), + () => mockSyncApiRepo.ack(["4"]), + () => mockSyncStreamRepo.updatePartnerV1(any()), + () => mockSyncApiRepo.ack(["3"]), + ]); + verifyNever(() => mockAbortCallbackWrapper()); + }); + + test("processes final batch correctly", () async { + final events = [SyncStreamStub.userDeleteV1, SyncStreamStub.userV1Admin]; + + await simulateEvents(events); + verifyInOrder([ () => mockSyncStreamRepo.deleteUsersV1(any()), () => mockSyncApiRepo.ack(["2"]), @@ -184,11 +150,7 @@ void main() { ); await sut.sync(); - final events = [ - SyncStreamStub.userDeleteV1, - SyncStreamStub.userV1Admin, - SyncStreamStub.partnerDeleteV1, - ]; + final events = [SyncStreamStub.userDeleteV1, SyncStreamStub.userV1Admin, SyncStreamStub.partnerDeleteV1]; when(() => mockSyncStreamRepo.deleteUsersV1(any())).thenAnswer((_) async { when(() => cancellationChecker()).thenReturn(true); @@ -205,52 +167,43 @@ void main() { verify(() => mockSyncApiRepo.ack(["2"])).called(1); }); - test( - "aborts and stops processing if cancelled before processing batch", - () async { - final cancellationChecker = _MockCancellationWrapper(); - when(() => cancellationChecker()).thenReturn(false); + test("aborts and stops processing if cancelled before processing batch", () async { + final cancellationChecker = _MockCancellationWrapper(); + when(() => cancellationChecker()).thenReturn(false); - final processingCompleter = Completer(); - bool handler1Started = false; - when(() => mockSyncStreamRepo.deleteUsersV1(any())) - .thenAnswer((_) async { - handler1Started = true; - return processingCompleter.future; - }); + final processingCompleter = Completer(); + bool handler1Started = false; + when(() => mockSyncStreamRepo.deleteUsersV1(any())).thenAnswer((_) async { + handler1Started = true; + return processingCompleter.future; + }); - sut = SyncStreamService( - syncApiRepository: mockSyncApiRepo, - syncStreamRepository: mockSyncStreamRepo, - cancelChecker: cancellationChecker.call, - ); + sut = SyncStreamService( + syncApiRepository: mockSyncApiRepo, + syncStreamRepository: mockSyncStreamRepo, + cancelChecker: cancellationChecker.call, + ); - await sut.sync(); + await sut.sync(); - final events = [ - SyncStreamStub.userDeleteV1, - SyncStreamStub.userV1Admin, - SyncStreamStub.partnerDeleteV1, - ]; + final events = [SyncStreamStub.userDeleteV1, SyncStreamStub.userV1Admin, SyncStreamStub.partnerDeleteV1]; - final processingFuture = - handleEventsCallback(events, mockAbortCallbackWrapper.call); - await pumpEventQueue(); + final processingFuture = handleEventsCallback(events, mockAbortCallbackWrapper.call); + await pumpEventQueue(); - expect(handler1Started, isTrue); + expect(handler1Started, isTrue); - // Signal cancellation while handler 1 is waiting - when(() => cancellationChecker()).thenReturn(true); - await pumpEventQueue(); + // Signal cancellation while handler 1 is waiting + when(() => cancellationChecker()).thenReturn(true); + await pumpEventQueue(); - processingCompleter.complete(); - await processingFuture; + processingCompleter.complete(); + await processingFuture; - verifyNever(() => mockSyncStreamRepo.updateUsersV1(any())); + verifyNever(() => mockSyncStreamRepo.updateUsersV1(any())); - verify(() => mockSyncApiRepo.ack(["2"])).called(1); - }, - ); + verify(() => mockSyncApiRepo.ack(["2"])).called(1); + }); test("processes memory sync events successfully", () async { final events = [ @@ -299,18 +252,11 @@ void main() { }); test("handles memory sync failure gracefully", () async { - when(() => mockSyncStreamRepo.updateMemoriesV1(any())) - .thenThrow(Exception("Memory sync failed")); + when(() => mockSyncStreamRepo.updateMemoriesV1(any())).thenThrow(Exception("Memory sync failed")); - final events = [ - SyncStreamStub.memoryV1, - SyncStreamStub.userV1Admin, - ]; + final events = [SyncStreamStub.memoryV1, SyncStreamStub.userV1Admin]; - expect( - () async => await simulateEvents(events), - throwsA(isA()), - ); + expect(() async => await simulateEvents(events), throwsA(isA())); }); test("processes memory asset events with correct data types", () async { @@ -331,8 +277,7 @@ void main() { verify(() => mockSyncApiRepo.ack(["6"])).called(1); }); - test("processes memory create/update events with correct data types", - () async { + test("processes memory create/update events with correct data types", () async { final events = [SyncStreamStub.memoryV1]; await simulateEvents(events); diff --git a/mobile/test/domain/services/user_service_test.dart b/mobile/test/domain/services/user_service_test.dart index 5cce565477..395f38a207 100644 --- a/mobile/test/domain/services/user_service_test.dart +++ b/mobile/test/domain/services/user_service_test.dart @@ -29,10 +29,8 @@ void main() { ); registerFallbackValue(UserStub.admin); - when(() => mockStoreService.get(StoreKey.currentUser)) - .thenReturn(UserStub.admin); - when(() => mockStoreService.tryGet(StoreKey.currentUser)) - .thenReturn(UserStub.admin); + when(() => mockStoreService.get(StoreKey.currentUser)).thenReturn(UserStub.admin); + when(() => mockStoreService.tryGet(StoreKey.currentUser)).thenReturn(UserStub.admin); }); group('getMyUser', () { @@ -42,8 +40,7 @@ void main() { }); test('should handle user not found scenario', () { - when(() => mockStoreService.get(StoreKey.currentUser)) - .thenThrow(Exception('User not found')); + when(() => mockStoreService.get(StoreKey.currentUser)).thenThrow(Exception('User not found')); expect(() => sut.getMyUser(), throwsA(isA())); }); @@ -56,8 +53,7 @@ void main() { }); test('should return null if user not found', () { - when(() => mockStoreService.tryGet(StoreKey.currentUser)) - .thenReturn(null); + when(() => mockStoreService.tryGet(StoreKey.currentUser)).thenReturn(null); final result = sut.tryGetMyUser(); expect(result, isNull); }); @@ -65,15 +61,13 @@ void main() { group('watchMyUser', () { test('should return user stream from store', () { - when(() => mockStoreService.watch(StoreKey.currentUser)) - .thenAnswer((_) => Stream.value(UserStub.admin)); + when(() => mockStoreService.watch(StoreKey.currentUser)).thenAnswer((_) => Stream.value(UserStub.admin)); final result = sut.watchMyUser(); expect(result, emits(UserStub.admin)); }); test('should return an empty stream if user not found', () { - when(() => mockStoreService.watch(StoreKey.currentUser)) - .thenAnswer((_) => const Stream.empty()); + when(() => mockStoreService.watch(StoreKey.currentUser)).thenAnswer((_) => const Stream.empty()); final result = sut.watchMyUser(); expect(result, emitsInOrder([])); }); @@ -81,16 +75,12 @@ void main() { group('refreshMyUser', () { test('should return user from api and store it', () async { - when(() => mockUserApiRepo.getMyUser()) - .thenAnswer((_) async => UserStub.admin); - when(() => mockStoreService.put(StoreKey.currentUser, UserStub.admin)) - .thenAnswer((_) async => true); - when(() => mockUserRepo.update(UserStub.admin)) - .thenAnswer((_) async => UserStub.admin); + when(() => mockUserApiRepo.getMyUser()).thenAnswer((_) async => UserStub.admin); + when(() => mockStoreService.put(StoreKey.currentUser, UserStub.admin)).thenAnswer((_) async => true); + when(() => mockUserRepo.update(UserStub.admin)).thenAnswer((_) async => UserStub.admin); final result = await sut.refreshMyUser(); - verify(() => mockStoreService.put(StoreKey.currentUser, UserStub.admin)) - .called(1); + verify(() => mockStoreService.put(StoreKey.currentUser, UserStub.admin)).called(1); verify(() => mockUserRepo.update(UserStub.admin)).called(1); expect(result, UserStub.admin); }); @@ -99,9 +89,7 @@ void main() { when(() => mockUserApiRepo.getMyUser()).thenAnswer((_) async => null); final result = await sut.refreshMyUser(); - verifyNever( - () => mockStoreService.put(StoreKey.currentUser, UserStub.admin), - ); + verifyNever(() => mockStoreService.put(StoreKey.currentUser, UserStub.admin)); verifyNever(() => mockUserRepo.update(UserStub.admin)); expect(result, isNull); }); @@ -110,46 +98,31 @@ void main() { group('createProfileImage', () { test('should return profile image path', () async { const profileImagePath = 'profile.jpg'; - final updatedUser = - UserStub.admin.copyWith(profileImagePath: profileImagePath); + final updatedUser = UserStub.admin; when( - () => mockUserApiRepo.createProfileImage( - name: profileImagePath, - data: Uint8List(0), - ), + () => mockUserApiRepo.createProfileImage(name: profileImagePath, data: Uint8List(0)), ).thenAnswer((_) async => profileImagePath); - when(() => mockStoreService.put(StoreKey.currentUser, updatedUser)) - .thenAnswer((_) async => true); - when(() => mockUserRepo.update(updatedUser)) - .thenAnswer((_) async => UserStub.admin); + when(() => mockStoreService.put(StoreKey.currentUser, updatedUser)).thenAnswer((_) async => true); + when(() => mockUserRepo.update(updatedUser)).thenAnswer((_) async => UserStub.admin); - final result = - await sut.createProfileImage(profileImagePath, Uint8List(0)); + final result = await sut.createProfileImage(profileImagePath, Uint8List(0)); - verify(() => mockStoreService.put(StoreKey.currentUser, updatedUser)) - .called(1); + verify(() => mockStoreService.put(StoreKey.currentUser, updatedUser)).called(1); verify(() => mockUserRepo.update(updatedUser)).called(1); expect(result, profileImagePath); }); test('should return null if profile image creation fails', () async { const profileImagePath = 'profile.jpg'; - final updatedUser = - UserStub.admin.copyWith(profileImagePath: profileImagePath); + final updatedUser = UserStub.admin; when( - () => mockUserApiRepo.createProfileImage( - name: profileImagePath, - data: Uint8List(0), - ), + () => mockUserApiRepo.createProfileImage(name: profileImagePath, data: Uint8List(0)), ).thenThrow(Exception('Failed to create profile image')); - final result = - await sut.createProfileImage(profileImagePath, Uint8List(0)); - verifyNever( - () => mockStoreService.put(StoreKey.currentUser, updatedUser), - ); + final result = await sut.createProfileImage(profileImagePath, Uint8List(0)); + verifyNever(() => mockStoreService.put(StoreKey.currentUser, updatedUser)); verifyNever(() => mockUserRepo.update(updatedUser)); expect(result, isNull); }); diff --git a/mobile/test/drift/main/generated/schema.dart b/mobile/test/drift/main/generated/schema.dart index b2b7404b5a..d59002bf56 100644 --- a/mobile/test/drift/main/generated/schema.dart +++ b/mobile/test/drift/main/generated/schema.dart @@ -5,6 +5,10 @@ import 'package:drift/drift.dart'; import 'package:drift/internal/migrations.dart'; import 'schema_v1.dart' as v1; import 'schema_v2.dart' as v2; +import 'schema_v3.dart' as v3; +import 'schema_v4.dart' as v4; +import 'schema_v5.dart' as v5; +import 'schema_v6.dart' as v6; class GeneratedHelper implements SchemaInstantiationHelper { @override @@ -14,10 +18,18 @@ class GeneratedHelper implements SchemaInstantiationHelper { return v1.DatabaseAtV1(db); case 2: return v2.DatabaseAtV2(db); + case 3: + return v3.DatabaseAtV3(db); + case 4: + return v4.DatabaseAtV4(db); + case 5: + return v5.DatabaseAtV5(db); + case 6: + return v6.DatabaseAtV6(db); default: throw MissingSchemaException(version, versions); } } - static const versions = const [1, 2]; + static const versions = const [1, 2, 3, 4, 5, 6]; } diff --git a/mobile/test/drift/main/generated/schema_v1.dart b/mobile/test/drift/main/generated/schema_v1.dart index 38dbb9f827..ca9e6ca1b0 100644 --- a/mobile/test/drift/main/generated/schema_v1.dart +++ b/mobile/test/drift/main/generated/schema_v1.dart @@ -9,48 +9,78 @@ class UserEntity extends Table with TableInfo { final String? _alias; UserEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn name = GeneratedColumn( - 'name', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn isAdmin = GeneratedColumn( - 'is_admin', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("is_admin" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + 'is_admin', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); late final GeneratedColumn email = GeneratedColumn( - 'email', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'email', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn profileImagePath = GeneratedColumn( - 'profile_image_path', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'profile_image_path', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn quotaSizeInBytes = GeneratedColumn( - 'quota_size_in_bytes', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'quota_size_in_bytes', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn quotaUsageInBytes = GeneratedColumn( - 'quota_usage_in_bytes', aliasedName, false, - type: DriftSqlType.int, - requiredDuringInsert: false, - defaultValue: const CustomExpression('0')); + 'quota_usage_in_bytes', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); @override List get $columns => [ - id, - name, - isAdmin, - email, - profileImagePath, - updatedAt, - quotaSizeInBytes, - quotaUsageInBytes - ]; + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -62,22 +92,38 @@ class UserEntity extends Table with TableInfo { UserEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return UserEntityData( - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}name'])!, - isAdmin: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}is_admin'])!, - email: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}email'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + isAdmin: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_admin'], + )!, + email: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}email'], + )!, profileImagePath: attachedDatabase.typeMapping.read( - DriftSqlType.string, data['${effectivePrefix}profile_image_path']), - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + DriftSqlType.string, + data['${effectivePrefix}profile_image_path'], + ), + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, quotaSizeInBytes: attachedDatabase.typeMapping.read( - DriftSqlType.int, data['${effectivePrefix}quota_size_in_bytes']), + DriftSqlType.int, + data['${effectivePrefix}quota_size_in_bytes'], + ), quotaUsageInBytes: attachedDatabase.typeMapping.read( - DriftSqlType.int, data['${effectivePrefix}quota_usage_in_bytes'])!, + DriftSqlType.int, + data['${effectivePrefix}quota_usage_in_bytes'], + )!, ); } @@ -101,15 +147,16 @@ class UserEntityData extends DataClass implements Insertable { final DateTime updatedAt; final int? quotaSizeInBytes; final int quotaUsageInBytes; - const UserEntityData( - {required this.id, - required this.name, - required this.isAdmin, - required this.email, - this.profileImagePath, - required this.updatedAt, - this.quotaSizeInBytes, - required this.quotaUsageInBytes}); + const UserEntityData({ + required this.id, + required this.name, + required this.isAdmin, + required this.email, + this.profileImagePath, + required this.updatedAt, + this.quotaSizeInBytes, + required this.quotaUsageInBytes, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -128,8 +175,10 @@ class UserEntityData extends DataClass implements Insertable { return map; } - factory UserEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory UserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return UserEntityData( id: serializer.fromJson(json['id']), @@ -157,29 +206,29 @@ class UserEntityData extends DataClass implements Insertable { }; } - UserEntityData copyWith( - {String? id, - String? name, - bool? isAdmin, - String? email, - Value profileImagePath = const Value.absent(), - DateTime? updatedAt, - Value quotaSizeInBytes = const Value.absent(), - int? quotaUsageInBytes}) => - UserEntityData( - id: id ?? this.id, - name: name ?? this.name, - isAdmin: isAdmin ?? this.isAdmin, - email: email ?? this.email, - profileImagePath: profileImagePath.present - ? profileImagePath.value - : this.profileImagePath, - updatedAt: updatedAt ?? this.updatedAt, - quotaSizeInBytes: quotaSizeInBytes.present - ? quotaSizeInBytes.value - : this.quotaSizeInBytes, - quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, - ); + UserEntityData copyWith({ + String? id, + String? name, + bool? isAdmin, + String? email, + Value profileImagePath = const Value.absent(), + DateTime? updatedAt, + Value quotaSizeInBytes = const Value.absent(), + int? quotaUsageInBytes, + }) => UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + profileImagePath: profileImagePath.present + ? profileImagePath.value + : this.profileImagePath, + updatedAt: updatedAt ?? this.updatedAt, + quotaSizeInBytes: quotaSizeInBytes.present + ? quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + ); UserEntityData copyWithCompanion(UserEntityCompanion data) { return UserEntityData( id: data.id.present ? data.id.value : this.id, @@ -215,8 +264,16 @@ class UserEntityData extends DataClass implements Insertable { } @override - int get hashCode => Object.hash(id, name, isAdmin, email, profileImagePath, - updatedAt, quotaSizeInBytes, quotaUsageInBytes); + int get hashCode => Object.hash( + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -259,9 +316,9 @@ class UserEntityCompanion extends UpdateCompanion { this.updatedAt = const Value.absent(), this.quotaSizeInBytes = const Value.absent(), this.quotaUsageInBytes = const Value.absent(), - }) : id = Value(id), - name = Value(name), - email = Value(email); + }) : id = Value(id), + name = Value(name), + email = Value(email); static Insertable custom({ Expression? id, Expression? name, @@ -284,15 +341,16 @@ class UserEntityCompanion extends UpdateCompanion { }); } - UserEntityCompanion copyWith( - {Value? id, - Value? name, - Value? isAdmin, - Value? email, - Value? profileImagePath, - Value? updatedAt, - Value? quotaSizeInBytes, - Value? quotaUsageInBytes}) { + UserEntityCompanion copyWith({ + Value? id, + Value? name, + Value? isAdmin, + Value? email, + Value? profileImagePath, + Value? updatedAt, + Value? quotaSizeInBytes, + Value? quotaUsageInBytes, + }) { return UserEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -358,87 +416,154 @@ class RemoteAssetEntity extends Table final String? _alias; RemoteAssetEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn name = GeneratedColumn( - 'name', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn type = GeneratedColumn( - 'type', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); late final GeneratedColumn createdAt = GeneratedColumn( - 'created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn width = GeneratedColumn( - 'width', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn height = GeneratedColumn( - 'height', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn durationInSeconds = GeneratedColumn( - 'duration_in_seconds', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn checksum = GeneratedColumn( - 'checksum', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'checksum', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn isFavorite = GeneratedColumn( - 'is_favorite', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); late final GeneratedColumn ownerId = GeneratedColumn( - 'owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn localDateTime = - GeneratedColumn('local_date_time', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); late final GeneratedColumn thumbHash = GeneratedColumn( - 'thumb_hash', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'thumb_hash', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn deletedAt = GeneratedColumn( - 'deleted_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); late final GeneratedColumn livePhotoVideoId = GeneratedColumn( - 'live_photo_video_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'live_photo_video_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn visibility = GeneratedColumn( - 'visibility', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'visibility', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); late final GeneratedColumn stackId = GeneratedColumn( - 'stack_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'stack_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); @override List get $columns => [ - name, - type, - createdAt, - updatedAt, - width, - height, - durationInSeconds, - id, - checksum, - isFavorite, - ownerId, - localDateTime, - thumbHash, - deletedAt, - livePhotoVideoId, - visibility, - stackId - ]; + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -450,40 +575,74 @@ class RemoteAssetEntity extends Table RemoteAssetEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAssetEntityData( - name: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}name'])!, - type: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}type'])!, - createdAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - width: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}width']), - height: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}height']), + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), durationInSeconds: attachedDatabase.typeMapping.read( - DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - checksum: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}checksum'])!, - isFavorite: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - ownerId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, localDateTime: attachedDatabase.typeMapping.read( - DriftSqlType.dateTime, data['${effectivePrefix}local_date_time']), - thumbHash: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}thumb_hash']), - deletedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), + DriftSqlType.dateTime, + data['${effectivePrefix}local_date_time'], + ), + thumbHash: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumb_hash'], + ), + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), livePhotoVideoId: attachedDatabase.typeMapping.read( - DriftSqlType.string, data['${effectivePrefix}live_photo_video_id']), - visibility: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}visibility'])!, - stackId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}stack_id']), + DriftSqlType.string, + data['${effectivePrefix}live_photo_video_id'], + ), + visibility: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}visibility'], + )!, + stackId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}stack_id'], + ), ); } @@ -517,24 +676,25 @@ class RemoteAssetEntityData extends DataClass final String? livePhotoVideoId; final int visibility; final String? stackId; - const RemoteAssetEntityData( - {required this.name, - required this.type, - required this.createdAt, - required this.updatedAt, - this.width, - this.height, - this.durationInSeconds, - required this.id, - required this.checksum, - required this.isFavorite, - required this.ownerId, - this.localDateTime, - this.thumbHash, - this.deletedAt, - this.livePhotoVideoId, - required this.visibility, - this.stackId}); + const RemoteAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + required this.checksum, + required this.isFavorite, + required this.ownerId, + this.localDateTime, + this.thumbHash, + this.deletedAt, + this.livePhotoVideoId, + required this.visibility, + this.stackId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -574,8 +734,10 @@ class RemoteAssetEntityData extends DataClass return map; } - factory RemoteAssetEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory RemoteAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAssetEntityData( name: serializer.fromJson(json['name']), @@ -621,48 +783,49 @@ class RemoteAssetEntityData extends DataClass }; } - RemoteAssetEntityData copyWith( - {String? name, - int? type, - DateTime? createdAt, - DateTime? updatedAt, - Value width = const Value.absent(), - Value height = const Value.absent(), - Value durationInSeconds = const Value.absent(), - String? id, - String? checksum, - bool? isFavorite, - String? ownerId, - Value localDateTime = const Value.absent(), - Value thumbHash = const Value.absent(), - Value deletedAt = const Value.absent(), - Value livePhotoVideoId = const Value.absent(), - int? visibility, - Value stackId = const Value.absent()}) => - RemoteAssetEntityData( - name: name ?? this.name, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - width: width.present ? width.value : this.width, - height: height.present ? height.value : this.height, - durationInSeconds: durationInSeconds.present - ? durationInSeconds.value - : this.durationInSeconds, - id: id ?? this.id, - checksum: checksum ?? this.checksum, - isFavorite: isFavorite ?? this.isFavorite, - ownerId: ownerId ?? this.ownerId, - localDateTime: - localDateTime.present ? localDateTime.value : this.localDateTime, - thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, - deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, - livePhotoVideoId: livePhotoVideoId.present - ? livePhotoVideoId.value - : this.livePhotoVideoId, - visibility: visibility ?? this.visibility, - stackId: stackId.present ? stackId.value : this.stackId, - ); + RemoteAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + String? checksum, + bool? isFavorite, + String? ownerId, + Value localDateTime = const Value.absent(), + Value thumbHash = const Value.absent(), + Value deletedAt = const Value.absent(), + Value livePhotoVideoId = const Value.absent(), + int? visibility, + Value stackId = const Value.absent(), + }) => RemoteAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime.present + ? localDateTime.value + : this.localDateTime, + thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + livePhotoVideoId: livePhotoVideoId.present + ? livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId.present ? stackId.value : this.stackId, + ); RemoteAssetEntityData copyWithCompanion(RemoteAssetEntityCompanion data) { return RemoteAssetEntityData( name: data.name.present ? data.name.value : this.name, @@ -676,8 +839,9 @@ class RemoteAssetEntityData extends DataClass : this.durationInSeconds, id: data.id.present ? data.id.value : this.id, checksum: data.checksum.present ? data.checksum.value : this.checksum, - isFavorite: - data.isFavorite.present ? data.isFavorite.value : this.isFavorite, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, localDateTime: data.localDateTime.present ? data.localDateTime.value @@ -687,8 +851,9 @@ class RemoteAssetEntityData extends DataClass livePhotoVideoId: data.livePhotoVideoId.present ? data.livePhotoVideoId.value : this.livePhotoVideoId, - visibility: - data.visibility.present ? data.visibility.value : this.visibility, + visibility: data.visibility.present + ? data.visibility.value + : this.visibility, stackId: data.stackId.present ? data.stackId.value : this.stackId, ); } @@ -719,23 +884,24 @@ class RemoteAssetEntityData extends DataClass @override int get hashCode => Object.hash( - name, - type, - createdAt, - updatedAt, - width, - height, - durationInSeconds, - id, - checksum, - isFavorite, - ownerId, - localDateTime, - thumbHash, - deletedAt, - livePhotoVideoId, - visibility, - stackId); + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -815,12 +981,12 @@ class RemoteAssetEntityCompanion this.livePhotoVideoId = const Value.absent(), required int visibility, this.stackId = const Value.absent(), - }) : name = Value(name), - type = Value(type), - id = Value(id), - checksum = Value(checksum), - ownerId = Value(ownerId), - visibility = Value(visibility); + }) : name = Value(name), + type = Value(type), + id = Value(id), + checksum = Value(checksum), + ownerId = Value(ownerId), + visibility = Value(visibility); static Insertable custom({ Expression? name, Expression? type, @@ -861,24 +1027,25 @@ class RemoteAssetEntityCompanion }); } - RemoteAssetEntityCompanion copyWith( - {Value? name, - Value? type, - Value? createdAt, - Value? updatedAt, - Value? width, - Value? height, - Value? durationInSeconds, - Value? id, - Value? checksum, - Value? isFavorite, - Value? ownerId, - Value? localDateTime, - Value? thumbHash, - Value? deletedAt, - Value? livePhotoVideoId, - Value? visibility, - Value? stackId}) { + RemoteAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? ownerId, + Value? localDateTime, + Value? thumbHash, + Value? deletedAt, + Value? livePhotoVideoId, + Value? visibility, + Value? stackId, + }) { return RemoteAssetEntityCompanion( name: name ?? this.name, type: type ?? this.type, @@ -989,62 +1156,103 @@ class LocalAssetEntity extends Table final String? _alias; LocalAssetEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn name = GeneratedColumn( - 'name', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn type = GeneratedColumn( - 'type', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); late final GeneratedColumn createdAt = GeneratedColumn( - 'created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn width = GeneratedColumn( - 'width', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn height = GeneratedColumn( - 'height', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn durationInSeconds = GeneratedColumn( - 'duration_in_seconds', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn checksum = GeneratedColumn( - 'checksum', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'checksum', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn isFavorite = GeneratedColumn( - 'is_favorite', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); late final GeneratedColumn orientation = GeneratedColumn( - 'orientation', aliasedName, false, - type: DriftSqlType.int, - requiredDuringInsert: false, - defaultValue: const CustomExpression('0')); + 'orientation', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); @override List get $columns => [ - name, - type, - createdAt, - updatedAt, - width, - height, - durationInSeconds, - id, - checksum, - isFavorite, - orientation - ]; + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -1056,28 +1264,50 @@ class LocalAssetEntity extends Table LocalAssetEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LocalAssetEntityData( - name: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}name'])!, - type: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}type'])!, - createdAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - width: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}width']), - height: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}height']), + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), durationInSeconds: attachedDatabase.typeMapping.read( - DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - checksum: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}checksum']), - isFavorite: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - orientation: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}orientation'])!, + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}orientation'], + )!, ); } @@ -1105,18 +1335,19 @@ class LocalAssetEntityData extends DataClass final String? checksum; final bool isFavorite; final int orientation; - const LocalAssetEntityData( - {required this.name, - required this.type, - required this.createdAt, - required this.updatedAt, - this.width, - this.height, - this.durationInSeconds, - required this.id, - this.checksum, - required this.isFavorite, - required this.orientation}); + const LocalAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + this.checksum, + required this.isFavorite, + required this.orientation, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1142,8 +1373,10 @@ class LocalAssetEntityData extends DataClass return map; } - factory LocalAssetEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory LocalAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LocalAssetEntityData( name: serializer.fromJson(json['name']), @@ -1177,33 +1410,33 @@ class LocalAssetEntityData extends DataClass }; } - LocalAssetEntityData copyWith( - {String? name, - int? type, - DateTime? createdAt, - DateTime? updatedAt, - Value width = const Value.absent(), - Value height = const Value.absent(), - Value durationInSeconds = const Value.absent(), - String? id, - Value checksum = const Value.absent(), - bool? isFavorite, - int? orientation}) => - LocalAssetEntityData( - name: name ?? this.name, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - width: width.present ? width.value : this.width, - height: height.present ? height.value : this.height, - durationInSeconds: durationInSeconds.present - ? durationInSeconds.value - : this.durationInSeconds, - id: id ?? this.id, - checksum: checksum.present ? checksum.value : this.checksum, - isFavorite: isFavorite ?? this.isFavorite, - orientation: orientation ?? this.orientation, - ); + LocalAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + Value checksum = const Value.absent(), + bool? isFavorite, + int? orientation, + }) => LocalAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); LocalAssetEntityData copyWithCompanion(LocalAssetEntityCompanion data) { return LocalAssetEntityData( name: data.name.present ? data.name.value : this.name, @@ -1217,10 +1450,12 @@ class LocalAssetEntityData extends DataClass : this.durationInSeconds, id: data.id.present ? data.id.value : this.id, checksum: data.checksum.present ? data.checksum.value : this.checksum, - isFavorite: - data.isFavorite.present ? data.isFavorite.value : this.isFavorite, - orientation: - data.orientation.present ? data.orientation.value : this.orientation, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, ); } @@ -1243,8 +1478,19 @@ class LocalAssetEntityData extends DataClass } @override - int get hashCode => Object.hash(name, type, createdAt, updatedAt, width, - height, durationInSeconds, id, checksum, isFavorite, orientation); + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -1299,9 +1545,9 @@ class LocalAssetEntityCompanion extends UpdateCompanion { this.checksum = const Value.absent(), this.isFavorite = const Value.absent(), this.orientation = const Value.absent(), - }) : name = Value(name), - type = Value(type), - id = Value(id); + }) : name = Value(name), + type = Value(type), + id = Value(id); static Insertable custom({ Expression? name, Expression? type, @@ -1330,18 +1576,19 @@ class LocalAssetEntityCompanion extends UpdateCompanion { }); } - LocalAssetEntityCompanion copyWith( - {Value? name, - Value? type, - Value? createdAt, - Value? updatedAt, - Value? width, - Value? height, - Value? durationInSeconds, - Value? id, - Value? checksum, - Value? isFavorite, - Value? orientation}) { + LocalAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? orientation, + }) { return LocalAssetEntityCompanion( name: name ?? this.name, type: type ?? this.type, @@ -1421,33 +1668,56 @@ class StackEntity extends Table with TableInfo { final String? _alias; StackEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn createdAt = GeneratedColumn( - 'created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn ownerId = GeneratedColumn( - 'owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn primaryAssetId = GeneratedColumn( - 'primary_asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id)')); + 'primary_asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id)', + ), + ); @override - List get $columns => - [id, createdAt, updatedAt, ownerId, primaryAssetId]; + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + primaryAssetId, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -1459,16 +1729,26 @@ class StackEntity extends Table with TableInfo { StackEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return StackEntityData( - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, primaryAssetId: attachedDatabase.typeMapping.read( - DriftSqlType.string, data['${effectivePrefix}primary_asset_id'])!, + DriftSqlType.string, + data['${effectivePrefix}primary_asset_id'], + )!, ); } @@ -1489,12 +1769,13 @@ class StackEntityData extends DataClass implements Insertable { final DateTime updatedAt; final String ownerId; final String primaryAssetId; - const StackEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - required this.primaryAssetId}); + const StackEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1506,8 +1787,10 @@ class StackEntityData extends DataClass implements Insertable { return map; } - factory StackEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory StackEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return StackEntityData( id: serializer.fromJson(json['id']), @@ -1529,19 +1812,19 @@ class StackEntityData extends DataClass implements Insertable { }; } - StackEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - String? ownerId, - String? primaryAssetId}) => - StackEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - primaryAssetId: primaryAssetId ?? this.primaryAssetId, - ); + StackEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId, + }) => StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); StackEntityData copyWithCompanion(StackEntityCompanion data) { return StackEntityData( id: data.id.present ? data.id.value : this.id, @@ -1599,9 +1882,9 @@ class StackEntityCompanion extends UpdateCompanion { this.updatedAt = const Value.absent(), required String ownerId, required String primaryAssetId, - }) : id = Value(id), - ownerId = Value(ownerId), - primaryAssetId = Value(primaryAssetId); + }) : id = Value(id), + ownerId = Value(ownerId), + primaryAssetId = Value(primaryAssetId); static Insertable custom({ Expression? id, Expression? createdAt, @@ -1618,12 +1901,13 @@ class StackEntityCompanion extends UpdateCompanion { }); } - StackEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? primaryAssetId}) { + StackEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? primaryAssetId, + }) { return StackEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -1674,17 +1958,29 @@ class UserMetadataEntity extends Table final String? _alias; UserMetadataEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn userId = GeneratedColumn( - 'user_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn key = GeneratedColumn( - 'key', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'key', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); late final GeneratedColumn value = GeneratedColumn( - 'value', aliasedName, false, - type: DriftSqlType.blob, requiredDuringInsert: true); + 'value', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + ); @override List get $columns => [userId, key, value]; @override @@ -1698,12 +1994,18 @@ class UserMetadataEntity extends Table UserMetadataEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return UserMetadataEntityData( - userId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, - key: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}key'])!, - value: attachedDatabase.typeMapping - .read(DriftSqlType.blob, data['${effectivePrefix}value'])!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + key: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}key'], + )!, + value: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}value'], + )!, ); } @@ -1723,8 +2025,11 @@ class UserMetadataEntityData extends DataClass final String userId; final int key; final Uint8List value; - const UserMetadataEntityData( - {required this.userId, required this.key, required this.value}); + const UserMetadataEntityData({ + required this.userId, + required this.key, + required this.value, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1734,8 +2039,10 @@ class UserMetadataEntityData extends DataClass return map; } - factory UserMetadataEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory UserMetadataEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return UserMetadataEntityData( userId: serializer.fromJson(json['userId']), @@ -1753,13 +2060,15 @@ class UserMetadataEntityData extends DataClass }; } - UserMetadataEntityData copyWith( - {String? userId, int? key, Uint8List? value}) => - UserMetadataEntityData( - userId: userId ?? this.userId, - key: key ?? this.key, - value: value ?? this.value, - ); + UserMetadataEntityData copyWith({ + String? userId, + int? key, + Uint8List? value, + }) => UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { return UserMetadataEntityData( userId: data.userId.present ? data.userId.value : this.userId, @@ -1803,9 +2112,9 @@ class UserMetadataEntityCompanion required String userId, required int key, required Uint8List value, - }) : userId = Value(userId), - key = Value(key), - value = Value(value); + }) : userId = Value(userId), + key = Value(key), + value = Value(value); static Insertable custom({ Expression? userId, Expression? key, @@ -1818,8 +2127,11 @@ class UserMetadataEntityCompanion }); } - UserMetadataEntityCompanion copyWith( - {Value? userId, Value? key, Value? value}) { + UserMetadataEntityCompanion copyWith({ + Value? userId, + Value? key, + Value? value, + }) { return UserMetadataEntityCompanion( userId: userId ?? this.userId, key: key ?? this.key, @@ -1860,24 +2172,36 @@ class PartnerEntity extends Table final String? _alias; PartnerEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn sharedById = GeneratedColumn( - 'shared_by_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'shared_by_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn sharedWithId = GeneratedColumn( - 'shared_with_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'shared_with_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn inTimeline = GeneratedColumn( - 'in_timeline', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("in_timeline" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + 'in_timeline', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); @override List get $columns => [sharedById, sharedWithId, inTimeline]; @override @@ -1891,12 +2215,18 @@ class PartnerEntity extends Table PartnerEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return PartnerEntityData( - sharedById: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}shared_by_id'])!, - sharedWithId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}shared_with_id'])!, - inTimeline: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}in_timeline'])!, + sharedById: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_by_id'], + )!, + sharedWithId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_with_id'], + )!, + inTimeline: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}in_timeline'], + )!, ); } @@ -1916,10 +2246,11 @@ class PartnerEntityData extends DataClass final String sharedById; final String sharedWithId; final bool inTimeline; - const PartnerEntityData( - {required this.sharedById, - required this.sharedWithId, - required this.inTimeline}); + const PartnerEntityData({ + required this.sharedById, + required this.sharedWithId, + required this.inTimeline, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1929,8 +2260,10 @@ class PartnerEntityData extends DataClass return map; } - factory PartnerEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory PartnerEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return PartnerEntityData( sharedById: serializer.fromJson(json['sharedById']), @@ -1948,22 +2281,26 @@ class PartnerEntityData extends DataClass }; } - PartnerEntityData copyWith( - {String? sharedById, String? sharedWithId, bool? inTimeline}) => - PartnerEntityData( - sharedById: sharedById ?? this.sharedById, - sharedWithId: sharedWithId ?? this.sharedWithId, - inTimeline: inTimeline ?? this.inTimeline, - ); + PartnerEntityData copyWith({ + String? sharedById, + String? sharedWithId, + bool? inTimeline, + }) => PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { return PartnerEntityData( - sharedById: - data.sharedById.present ? data.sharedById.value : this.sharedById, + sharedById: data.sharedById.present + ? data.sharedById.value + : this.sharedById, sharedWithId: data.sharedWithId.present ? data.sharedWithId.value : this.sharedWithId, - inTimeline: - data.inTimeline.present ? data.inTimeline.value : this.inTimeline, + inTimeline: data.inTimeline.present + ? data.inTimeline.value + : this.inTimeline, ); } @@ -2001,8 +2338,8 @@ class PartnerEntityCompanion extends UpdateCompanion { required String sharedById, required String sharedWithId, this.inTimeline = const Value.absent(), - }) : sharedById = Value(sharedById), - sharedWithId = Value(sharedWithId); + }) : sharedById = Value(sharedById), + sharedWithId = Value(sharedWithId); static Insertable custom({ Expression? sharedById, Expression? sharedWithId, @@ -2015,10 +2352,11 @@ class PartnerEntityCompanion extends UpdateCompanion { }); } - PartnerEntityCompanion copyWith( - {Value? sharedById, - Value? sharedWithId, - Value? inTimeline}) { + PartnerEntityCompanion copyWith({ + Value? sharedById, + Value? sharedWithId, + Value? inTimeline, + }) { return PartnerEntityCompanion( sharedById: sharedById ?? this.sharedById, sharedWithId: sharedWithId ?? this.sharedWithId, @@ -2059,35 +2397,64 @@ class LocalAlbumEntity extends Table final String? _alias; LocalAlbumEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn name = GeneratedColumn( - 'name', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn backupSelection = GeneratedColumn( - 'backup_selection', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'backup_selection', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); late final GeneratedColumn isIosSharedAlbum = GeneratedColumn( - 'is_ios_shared_album', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'CHECK ("is_ios_shared_album" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + 'is_ios_shared_album', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); late final GeneratedColumn marker_ = GeneratedColumn( - 'marker', aliasedName, true, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("marker" IN (0, 1))')); + 'marker', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); @override - List get $columns => - [id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_]; + List get $columns => [ + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -2099,18 +2466,30 @@ class LocalAlbumEntity extends Table LocalAlbumEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LocalAlbumEntityData( - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}name'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - backupSelection: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}backup_selection'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + backupSelection: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}backup_selection'], + )!, isIosSharedAlbum: attachedDatabase.typeMapping.read( - DriftSqlType.bool, data['${effectivePrefix}is_ios_shared_album'])!, - marker_: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}marker']), + DriftSqlType.bool, + data['${effectivePrefix}is_ios_shared_album'], + )!, + marker_: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), ); } @@ -2133,13 +2512,14 @@ class LocalAlbumEntityData extends DataClass final int backupSelection; final bool isIosSharedAlbum; final bool? marker_; - const LocalAlbumEntityData( - {required this.id, - required this.name, - required this.updatedAt, - required this.backupSelection, - required this.isIosSharedAlbum, - this.marker_}); + const LocalAlbumEntityData({ + required this.id, + required this.name, + required this.updatedAt, + required this.backupSelection, + required this.isIosSharedAlbum, + this.marker_, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -2154,8 +2534,10 @@ class LocalAlbumEntityData extends DataClass return map; } - factory LocalAlbumEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory LocalAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LocalAlbumEntityData( id: serializer.fromJson(json['id']), @@ -2179,21 +2561,21 @@ class LocalAlbumEntityData extends DataClass }; } - LocalAlbumEntityData copyWith( - {String? id, - String? name, - DateTime? updatedAt, - int? backupSelection, - bool? isIosSharedAlbum, - Value marker_ = const Value.absent()}) => - LocalAlbumEntityData( - id: id ?? this.id, - name: name ?? this.name, - updatedAt: updatedAt ?? this.updatedAt, - backupSelection: backupSelection ?? this.backupSelection, - isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, - marker_: marker_.present ? marker_.value : this.marker_, - ); + LocalAlbumEntityData copyWith({ + String? id, + String? name, + DateTime? updatedAt, + int? backupSelection, + bool? isIosSharedAlbum, + Value marker_ = const Value.absent(), + }) => LocalAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_.present ? marker_.value : this.marker_, + ); LocalAlbumEntityData copyWithCompanion(LocalAlbumEntityCompanion data) { return LocalAlbumEntityData( id: data.id.present ? data.id.value : this.id, @@ -2224,7 +2606,13 @@ class LocalAlbumEntityData extends DataClass @override int get hashCode => Object.hash( - id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_); + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -2259,9 +2647,9 @@ class LocalAlbumEntityCompanion extends UpdateCompanion { required int backupSelection, this.isIosSharedAlbum = const Value.absent(), this.marker_ = const Value.absent(), - }) : id = Value(id), - name = Value(name), - backupSelection = Value(backupSelection); + }) : id = Value(id), + name = Value(name), + backupSelection = Value(backupSelection); static Insertable custom({ Expression? id, Expression? name, @@ -2280,13 +2668,14 @@ class LocalAlbumEntityCompanion extends UpdateCompanion { }); } - LocalAlbumEntityCompanion copyWith( - {Value? id, - Value? name, - Value? updatedAt, - Value? backupSelection, - Value? isIosSharedAlbum, - Value? marker_}) { + LocalAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? updatedAt, + Value? backupSelection, + Value? isIosSharedAlbum, + Value? marker_, + }) { return LocalAlbumEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -2342,17 +2731,25 @@ class LocalAlbumAssetEntity extends Table final String? _alias; LocalAlbumAssetEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn assetId = GeneratedColumn( - 'asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES local_asset_entity (id) ON DELETE CASCADE')); + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn albumId = GeneratedColumn( - 'album_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES local_album_entity (id) ON DELETE CASCADE')); + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, albumId]; @override @@ -2363,14 +2760,20 @@ class LocalAlbumAssetEntity extends Table @override Set get $primaryKey => {assetId, albumId}; @override - LocalAlbumAssetEntityData map(Map data, - {String? tablePrefix}) { + LocalAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LocalAlbumAssetEntityData( - assetId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - albumId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, ); } @@ -2389,8 +2792,10 @@ class LocalAlbumAssetEntityData extends DataClass implements Insertable { final String assetId; final String albumId; - const LocalAlbumAssetEntityData( - {required this.assetId, required this.albumId}); + const LocalAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -2399,8 +2804,10 @@ class LocalAlbumAssetEntityData extends DataClass return map; } - factory LocalAlbumAssetEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory LocalAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LocalAlbumAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -2422,7 +2829,8 @@ class LocalAlbumAssetEntityData extends DataClass albumId: albumId ?? this.albumId, ); LocalAlbumAssetEntityData copyWithCompanion( - LocalAlbumAssetEntityCompanion data) { + LocalAlbumAssetEntityCompanion data, + ) { return LocalAlbumAssetEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, albumId: data.albumId.present ? data.albumId.value : this.albumId, @@ -2459,8 +2867,8 @@ class LocalAlbumAssetEntityCompanion LocalAlbumAssetEntityCompanion.insert({ required String assetId, required String albumId, - }) : assetId = Value(assetId), - albumId = Value(albumId); + }) : assetId = Value(assetId), + albumId = Value(albumId); static Insertable custom({ Expression? assetId, Expression? albumId, @@ -2471,8 +2879,10 @@ class LocalAlbumAssetEntityCompanion }); } - LocalAlbumAssetEntityCompanion copyWith( - {Value? assetId, Value? albumId}) { + LocalAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { return LocalAlbumAssetEntityCompanion( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, @@ -2508,99 +2918,188 @@ class RemoteExifEntity extends Table final String? _alias; RemoteExifEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn assetId = GeneratedColumn( - 'asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn city = GeneratedColumn( - 'city', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'city', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn state = GeneratedColumn( - 'state', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn country = GeneratedColumn( - 'country', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'country', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn dateTimeOriginal = - GeneratedColumn('date_time_original', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); late final GeneratedColumn description = GeneratedColumn( - 'description', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'description', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn height = GeneratedColumn( - 'height', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn width = GeneratedColumn( - 'width', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn exposureTime = GeneratedColumn( - 'exposure_time', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'exposure_time', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn fNumber = GeneratedColumn( - 'f_number', aliasedName, true, - type: DriftSqlType.double, requiredDuringInsert: false); + 'f_number', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); late final GeneratedColumn fileSize = GeneratedColumn( - 'file_size', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'file_size', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn focalLength = GeneratedColumn( - 'focal_length', aliasedName, true, - type: DriftSqlType.double, requiredDuringInsert: false); + 'focal_length', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); late final GeneratedColumn latitude = GeneratedColumn( - 'latitude', aliasedName, true, - type: DriftSqlType.double, requiredDuringInsert: false); + 'latitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); late final GeneratedColumn longitude = GeneratedColumn( - 'longitude', aliasedName, true, - type: DriftSqlType.double, requiredDuringInsert: false); + 'longitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); late final GeneratedColumn iso = GeneratedColumn( - 'iso', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'iso', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn make = GeneratedColumn( - 'make', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'make', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn model = GeneratedColumn( - 'model', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'model', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn lens = GeneratedColumn( - 'lens', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'lens', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn orientation = GeneratedColumn( - 'orientation', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'orientation', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn timeZone = GeneratedColumn( - 'time_zone', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'time_zone', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn rating = GeneratedColumn( - 'rating', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'rating', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn projectionType = GeneratedColumn( - 'projection_type', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'projection_type', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); @override List get $columns => [ - assetId, - city, - state, - country, - dateTimeOriginal, - description, - height, - width, - exposureTime, - fNumber, - fileSize, - focalLength, - latitude, - longitude, - iso, - make, - model, - lens, - orientation, - timeZone, - rating, - projectionType - ]; + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -2612,50 +3111,94 @@ class RemoteExifEntity extends Table RemoteExifEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteExifEntityData( - assetId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - city: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}city']), - state: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}state']), - country: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}country']), + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + city: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}city'], + ), + state: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}state'], + ), + country: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}country'], + ), dateTimeOriginal: attachedDatabase.typeMapping.read( - DriftSqlType.dateTime, data['${effectivePrefix}date_time_original']), - description: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}description']), - height: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}height']), - width: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}width']), - exposureTime: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}exposure_time']), - fNumber: attachedDatabase.typeMapping - .read(DriftSqlType.double, data['${effectivePrefix}f_number']), - fileSize: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}file_size']), - focalLength: attachedDatabase.typeMapping - .read(DriftSqlType.double, data['${effectivePrefix}focal_length']), - latitude: attachedDatabase.typeMapping - .read(DriftSqlType.double, data['${effectivePrefix}latitude']), - longitude: attachedDatabase.typeMapping - .read(DriftSqlType.double, data['${effectivePrefix}longitude']), - iso: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}iso']), - make: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}make']), - model: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}model']), - lens: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}lens']), - orientation: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}orientation']), - timeZone: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}time_zone']), - rating: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}rating']), - projectionType: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}projection_type']), + DriftSqlType.dateTime, + data['${effectivePrefix}date_time_original'], + ), + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + exposureTime: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}exposure_time'], + ), + fNumber: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}f_number'], + ), + fileSize: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}file_size'], + ), + focalLength: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}focal_length'], + ), + latitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}latitude'], + ), + longitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}longitude'], + ), + iso: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}iso'], + ), + make: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}make'], + ), + model: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}model'], + ), + lens: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}lens'], + ), + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}orientation'], + ), + timeZone: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}time_zone'], + ), + rating: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}rating'], + ), + projectionType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}projection_type'], + ), ); } @@ -2694,29 +3237,30 @@ class RemoteExifEntityData extends DataClass final String? timeZone; final int? rating; final String? projectionType; - const RemoteExifEntityData( - {required this.assetId, - this.city, - this.state, - this.country, - this.dateTimeOriginal, - this.description, - this.height, - this.width, - this.exposureTime, - this.fNumber, - this.fileSize, - this.focalLength, - this.latitude, - this.longitude, - this.iso, - this.make, - this.model, - this.lens, - this.orientation, - this.timeZone, - this.rating, - this.projectionType}); + const RemoteExifEntityData({ + required this.assetId, + this.city, + this.state, + this.country, + this.dateTimeOriginal, + this.description, + this.height, + this.width, + this.exposureTime, + this.fNumber, + this.fileSize, + this.focalLength, + this.latitude, + this.longitude, + this.iso, + this.make, + this.model, + this.lens, + this.orientation, + this.timeZone, + this.rating, + this.projectionType, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -2787,16 +3331,19 @@ class RemoteExifEntityData extends DataClass return map; } - factory RemoteExifEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory RemoteExifEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteExifEntityData( assetId: serializer.fromJson(json['assetId']), city: serializer.fromJson(json['city']), state: serializer.fromJson(json['state']), country: serializer.fromJson(json['country']), - dateTimeOriginal: - serializer.fromJson(json['dateTimeOriginal']), + dateTimeOriginal: serializer.fromJson( + json['dateTimeOriginal'], + ), description: serializer.fromJson(json['description']), height: serializer.fromJson(json['height']), width: serializer.fromJson(json['width']), @@ -2845,57 +3392,57 @@ class RemoteExifEntityData extends DataClass }; } - RemoteExifEntityData copyWith( - {String? assetId, - Value city = const Value.absent(), - Value state = const Value.absent(), - Value country = const Value.absent(), - Value dateTimeOriginal = const Value.absent(), - Value description = const Value.absent(), - Value height = const Value.absent(), - Value width = const Value.absent(), - Value exposureTime = const Value.absent(), - Value fNumber = const Value.absent(), - Value fileSize = const Value.absent(), - Value focalLength = const Value.absent(), - Value latitude = const Value.absent(), - Value longitude = const Value.absent(), - Value iso = const Value.absent(), - Value make = const Value.absent(), - Value model = const Value.absent(), - Value lens = const Value.absent(), - Value orientation = const Value.absent(), - Value timeZone = const Value.absent(), - Value rating = const Value.absent(), - Value projectionType = const Value.absent()}) => - RemoteExifEntityData( - assetId: assetId ?? this.assetId, - city: city.present ? city.value : this.city, - state: state.present ? state.value : this.state, - country: country.present ? country.value : this.country, - dateTimeOriginal: dateTimeOriginal.present - ? dateTimeOriginal.value - : this.dateTimeOriginal, - description: description.present ? description.value : this.description, - height: height.present ? height.value : this.height, - width: width.present ? width.value : this.width, - exposureTime: - exposureTime.present ? exposureTime.value : this.exposureTime, - fNumber: fNumber.present ? fNumber.value : this.fNumber, - fileSize: fileSize.present ? fileSize.value : this.fileSize, - focalLength: focalLength.present ? focalLength.value : this.focalLength, - latitude: latitude.present ? latitude.value : this.latitude, - longitude: longitude.present ? longitude.value : this.longitude, - iso: iso.present ? iso.value : this.iso, - make: make.present ? make.value : this.make, - model: model.present ? model.value : this.model, - lens: lens.present ? lens.value : this.lens, - orientation: orientation.present ? orientation.value : this.orientation, - timeZone: timeZone.present ? timeZone.value : this.timeZone, - rating: rating.present ? rating.value : this.rating, - projectionType: - projectionType.present ? projectionType.value : this.projectionType, - ); + RemoteExifEntityData copyWith({ + String? assetId, + Value city = const Value.absent(), + Value state = const Value.absent(), + Value country = const Value.absent(), + Value dateTimeOriginal = const Value.absent(), + Value description = const Value.absent(), + Value height = const Value.absent(), + Value width = const Value.absent(), + Value exposureTime = const Value.absent(), + Value fNumber = const Value.absent(), + Value fileSize = const Value.absent(), + Value focalLength = const Value.absent(), + Value latitude = const Value.absent(), + Value longitude = const Value.absent(), + Value iso = const Value.absent(), + Value make = const Value.absent(), + Value model = const Value.absent(), + Value lens = const Value.absent(), + Value orientation = const Value.absent(), + Value timeZone = const Value.absent(), + Value rating = const Value.absent(), + Value projectionType = const Value.absent(), + }) => RemoteExifEntityData( + assetId: assetId ?? this.assetId, + city: city.present ? city.value : this.city, + state: state.present ? state.value : this.state, + country: country.present ? country.value : this.country, + dateTimeOriginal: dateTimeOriginal.present + ? dateTimeOriginal.value + : this.dateTimeOriginal, + description: description.present ? description.value : this.description, + height: height.present ? height.value : this.height, + width: width.present ? width.value : this.width, + exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, + fNumber: fNumber.present ? fNumber.value : this.fNumber, + fileSize: fileSize.present ? fileSize.value : this.fileSize, + focalLength: focalLength.present ? focalLength.value : this.focalLength, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + iso: iso.present ? iso.value : this.iso, + make: make.present ? make.value : this.make, + model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, + orientation: orientation.present ? orientation.value : this.orientation, + timeZone: timeZone.present ? timeZone.value : this.timeZone, + rating: rating.present ? rating.value : this.rating, + projectionType: projectionType.present + ? projectionType.value + : this.projectionType, + ); RemoteExifEntityData copyWithCompanion(RemoteExifEntityCompanion data) { return RemoteExifEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, @@ -2905,8 +3452,9 @@ class RemoteExifEntityData extends DataClass dateTimeOriginal: data.dateTimeOriginal.present ? data.dateTimeOriginal.value : this.dateTimeOriginal, - description: - data.description.present ? data.description.value : this.description, + description: data.description.present + ? data.description.value + : this.description, height: data.height.present ? data.height.value : this.height, width: data.width.present ? data.width.value : this.width, exposureTime: data.exposureTime.present @@ -2914,16 +3462,18 @@ class RemoteExifEntityData extends DataClass : this.exposureTime, fNumber: data.fNumber.present ? data.fNumber.value : this.fNumber, fileSize: data.fileSize.present ? data.fileSize.value : this.fileSize, - focalLength: - data.focalLength.present ? data.focalLength.value : this.focalLength, + focalLength: data.focalLength.present + ? data.focalLength.value + : this.focalLength, latitude: data.latitude.present ? data.latitude.value : this.latitude, longitude: data.longitude.present ? data.longitude.value : this.longitude, iso: data.iso.present ? data.iso.value : this.iso, make: data.make.present ? data.make.value : this.make, model: data.model.present ? data.model.value : this.model, lens: data.lens.present ? data.lens.value : this.lens, - orientation: - data.orientation.present ? data.orientation.value : this.orientation, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, rating: data.rating.present ? data.rating.value : this.rating, projectionType: data.projectionType.present @@ -2963,29 +3513,29 @@ class RemoteExifEntityData extends DataClass @override int get hashCode => Object.hashAll([ - assetId, - city, - state, - country, - dateTimeOriginal, - description, - height, - width, - exposureTime, - fNumber, - fileSize, - focalLength, - latitude, - longitude, - iso, - make, - model, - lens, - orientation, - timeZone, - rating, - projectionType - ]); + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]); @override bool operator ==(Object other) => identical(this, other) || @@ -3135,29 +3685,30 @@ class RemoteExifEntityCompanion extends UpdateCompanion { }); } - RemoteExifEntityCompanion copyWith( - {Value? assetId, - Value? city, - Value? state, - Value? country, - Value? dateTimeOriginal, - Value? description, - Value? height, - Value? width, - Value? exposureTime, - Value? fNumber, - Value? fileSize, - Value? focalLength, - Value? latitude, - Value? longitude, - Value? iso, - Value? make, - Value? model, - Value? lens, - Value? orientation, - Value? timeZone, - Value? rating, - Value? projectionType}) { + RemoteExifEntityCompanion copyWith({ + Value? assetId, + Value? city, + Value? state, + Value? country, + Value? dateTimeOriginal, + Value? description, + Value? height, + Value? width, + Value? exposureTime, + Value? fNumber, + Value? fileSize, + Value? focalLength, + Value? latitude, + Value? longitude, + Value? iso, + Value? make, + Value? model, + Value? lens, + Value? orientation, + Value? timeZone, + Value? rating, + Value? projectionType, + }) { return RemoteExifEntityCompanion( assetId: assetId ?? this.assetId, city: city ?? this.city, @@ -3293,60 +3844,93 @@ class RemoteAlbumEntity extends Table final String? _alias; RemoteAlbumEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn name = GeneratedColumn( - 'name', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn description = GeneratedColumn( - 'description', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: false, - defaultValue: const CustomExpression('\'\'')); + 'description', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const CustomExpression('\'\''), + ); late final GeneratedColumn createdAt = GeneratedColumn( - 'created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn ownerId = GeneratedColumn( - 'owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn thumbnailAssetId = GeneratedColumn( - 'thumbnail_asset_id', aliasedName, true, - type: DriftSqlType.string, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL')); + 'thumbnail_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); late final GeneratedColumn isActivityEnabled = GeneratedColumn( - 'is_activity_enabled', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'CHECK ("is_activity_enabled" IN (0, 1))'), - defaultValue: const CustomExpression('1')); + 'is_activity_enabled', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const CustomExpression('1'), + ); late final GeneratedColumn order = GeneratedColumn( - 'order', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'order', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); @override List get $columns => [ - id, - name, - description, - createdAt, - updatedAt, - ownerId, - thumbnailAssetId, - isActivityEnabled, - order - ]; + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -3358,24 +3942,42 @@ class RemoteAlbumEntity extends Table RemoteAlbumEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAlbumEntityData( - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}name'])!, - description: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}description'])!, - createdAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, thumbnailAssetId: attachedDatabase.typeMapping.read( - DriftSqlType.string, data['${effectivePrefix}thumbnail_asset_id']), + DriftSqlType.string, + data['${effectivePrefix}thumbnail_asset_id'], + ), isActivityEnabled: attachedDatabase.typeMapping.read( - DriftSqlType.bool, data['${effectivePrefix}is_activity_enabled'])!, - order: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}order'])!, + DriftSqlType.bool, + data['${effectivePrefix}is_activity_enabled'], + )!, + order: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}order'], + )!, ); } @@ -3401,16 +4003,17 @@ class RemoteAlbumEntityData extends DataClass final String? thumbnailAssetId; final bool isActivityEnabled; final int order; - const RemoteAlbumEntityData( - {required this.id, - required this.name, - required this.description, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - this.thumbnailAssetId, - required this.isActivityEnabled, - required this.order}); + const RemoteAlbumEntityData({ + required this.id, + required this.name, + required this.description, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + this.thumbnailAssetId, + required this.isActivityEnabled, + required this.order, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -3428,8 +4031,10 @@ class RemoteAlbumEntityData extends DataClass return map; } - factory RemoteAlbumEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory RemoteAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAlbumEntityData( id: serializer.fromJson(json['id']), @@ -3459,35 +4064,36 @@ class RemoteAlbumEntityData extends DataClass }; } - RemoteAlbumEntityData copyWith( - {String? id, - String? name, - String? description, - DateTime? createdAt, - DateTime? updatedAt, - String? ownerId, - Value thumbnailAssetId = const Value.absent(), - bool? isActivityEnabled, - int? order}) => - RemoteAlbumEntityData( - id: id ?? this.id, - name: name ?? this.name, - description: description ?? this.description, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - thumbnailAssetId: thumbnailAssetId.present - ? thumbnailAssetId.value - : this.thumbnailAssetId, - isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, - order: order ?? this.order, - ); + RemoteAlbumEntityData copyWith({ + String? id, + String? name, + String? description, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + Value thumbnailAssetId = const Value.absent(), + bool? isActivityEnabled, + int? order, + }) => RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId.present + ? thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); RemoteAlbumEntityData copyWithCompanion(RemoteAlbumEntityCompanion data) { return RemoteAlbumEntityData( id: data.id.present ? data.id.value : this.id, name: data.name.present ? data.name.value : this.name, - description: - data.description.present ? data.description.value : this.description, + description: data.description.present + ? data.description.value + : this.description, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, @@ -3518,8 +4124,17 @@ class RemoteAlbumEntityData extends DataClass } @override - int get hashCode => Object.hash(id, name, description, createdAt, updatedAt, - ownerId, thumbnailAssetId, isActivityEnabled, order); + int get hashCode => Object.hash( + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -3567,10 +4182,10 @@ class RemoteAlbumEntityCompanion this.thumbnailAssetId = const Value.absent(), this.isActivityEnabled = const Value.absent(), required int order, - }) : id = Value(id), - name = Value(name), - ownerId = Value(ownerId), - order = Value(order); + }) : id = Value(id), + name = Value(name), + ownerId = Value(ownerId), + order = Value(order); static Insertable custom({ Expression? id, Expression? name, @@ -3595,16 +4210,17 @@ class RemoteAlbumEntityCompanion }); } - RemoteAlbumEntityCompanion copyWith( - {Value? id, - Value? name, - Value? description, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? thumbnailAssetId, - Value? isActivityEnabled, - Value? order}) { + RemoteAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? description, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? thumbnailAssetId, + Value? isActivityEnabled, + Value? order, + }) { return RemoteAlbumEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -3675,17 +4291,25 @@ class RemoteAlbumAssetEntity extends Table final String? _alias; RemoteAlbumAssetEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn assetId = GeneratedColumn( - 'asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn albumId = GeneratedColumn( - 'album_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_album_entity (id) ON DELETE CASCADE')); + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, albumId]; @override @@ -3696,14 +4320,20 @@ class RemoteAlbumAssetEntity extends Table @override Set get $primaryKey => {assetId, albumId}; @override - RemoteAlbumAssetEntityData map(Map data, - {String? tablePrefix}) { + RemoteAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAlbumAssetEntityData( - assetId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - albumId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, ); } @@ -3722,8 +4352,10 @@ class RemoteAlbumAssetEntityData extends DataClass implements Insertable { final String assetId; final String albumId; - const RemoteAlbumAssetEntityData( - {required this.assetId, required this.albumId}); + const RemoteAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -3732,8 +4364,10 @@ class RemoteAlbumAssetEntityData extends DataClass return map; } - factory RemoteAlbumAssetEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory RemoteAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAlbumAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -3755,7 +4389,8 @@ class RemoteAlbumAssetEntityData extends DataClass albumId: albumId ?? this.albumId, ); RemoteAlbumAssetEntityData copyWithCompanion( - RemoteAlbumAssetEntityCompanion data) { + RemoteAlbumAssetEntityCompanion data, + ) { return RemoteAlbumAssetEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, albumId: data.albumId.present ? data.albumId.value : this.albumId, @@ -3792,8 +4427,8 @@ class RemoteAlbumAssetEntityCompanion RemoteAlbumAssetEntityCompanion.insert({ required String assetId, required String albumId, - }) : assetId = Value(assetId), - albumId = Value(albumId); + }) : assetId = Value(assetId), + albumId = Value(albumId); static Insertable custom({ Expression? assetId, Expression? albumId, @@ -3804,8 +4439,10 @@ class RemoteAlbumAssetEntityCompanion }); } - RemoteAlbumAssetEntityCompanion copyWith( - {Value? assetId, Value? albumId}) { + RemoteAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { return RemoteAlbumAssetEntityCompanion( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, @@ -3841,20 +4478,32 @@ class RemoteAlbumUserEntity extends Table final String? _alias; RemoteAlbumUserEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn albumId = GeneratedColumn( - 'album_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_album_entity (id) ON DELETE CASCADE')); + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn userId = GeneratedColumn( - 'user_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn role = GeneratedColumn( - 'role', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'role', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); @override List get $columns => [albumId, userId, role]; @override @@ -3865,16 +4514,24 @@ class RemoteAlbumUserEntity extends Table @override Set get $primaryKey => {albumId, userId}; @override - RemoteAlbumUserEntityData map(Map data, - {String? tablePrefix}) { + RemoteAlbumUserEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAlbumUserEntityData( - albumId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, - userId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, - role: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}role'])!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + role: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}role'], + )!, ); } @@ -3894,8 +4551,11 @@ class RemoteAlbumUserEntityData extends DataClass final String albumId; final String userId; final int role; - const RemoteAlbumUserEntityData( - {required this.albumId, required this.userId, required this.role}); + const RemoteAlbumUserEntityData({ + required this.albumId, + required this.userId, + required this.role, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -3905,8 +4565,10 @@ class RemoteAlbumUserEntityData extends DataClass return map; } - factory RemoteAlbumUserEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory RemoteAlbumUserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAlbumUserEntityData( albumId: serializer.fromJson(json['albumId']), @@ -3924,15 +4586,18 @@ class RemoteAlbumUserEntityData extends DataClass }; } - RemoteAlbumUserEntityData copyWith( - {String? albumId, String? userId, int? role}) => - RemoteAlbumUserEntityData( - albumId: albumId ?? this.albumId, - userId: userId ?? this.userId, - role: role ?? this.role, - ); + RemoteAlbumUserEntityData copyWith({ + String? albumId, + String? userId, + int? role, + }) => RemoteAlbumUserEntityData( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); RemoteAlbumUserEntityData copyWithCompanion( - RemoteAlbumUserEntityCompanion data) { + RemoteAlbumUserEntityCompanion data, + ) { return RemoteAlbumUserEntityData( albumId: data.albumId.present ? data.albumId.value : this.albumId, userId: data.userId.present ? data.userId.value : this.userId, @@ -3975,9 +4640,9 @@ class RemoteAlbumUserEntityCompanion required String albumId, required String userId, required int role, - }) : albumId = Value(albumId), - userId = Value(userId), - role = Value(role); + }) : albumId = Value(albumId), + userId = Value(userId), + role = Value(role); static Insertable custom({ Expression? albumId, Expression? userId, @@ -3990,8 +4655,11 @@ class RemoteAlbumUserEntityCompanion }); } - RemoteAlbumUserEntityCompanion copyWith( - {Value? albumId, Value? userId, Value? role}) { + RemoteAlbumUserEntityCompanion copyWith({ + Value? albumId, + Value? userId, + Value? role, + }) { return RemoteAlbumUserEntityCompanion( albumId: albumId ?? this.albumId, userId: userId ?? this.userId, @@ -4032,67 +4700,113 @@ class MemoryEntity extends Table final String? _alias; MemoryEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn createdAt = GeneratedColumn( - 'created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn deletedAt = GeneratedColumn( - 'deleted_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); late final GeneratedColumn ownerId = GeneratedColumn( - 'owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn type = GeneratedColumn( - 'type', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); late final GeneratedColumn data = GeneratedColumn( - 'data', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'data', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn isSaved = GeneratedColumn( - 'is_saved', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("is_saved" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + 'is_saved', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); late final GeneratedColumn memoryAt = GeneratedColumn( - 'memory_at', aliasedName, false, - type: DriftSqlType.dateTime, requiredDuringInsert: true); + 'memory_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: true, + ); late final GeneratedColumn seenAt = GeneratedColumn( - 'seen_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + 'seen_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); late final GeneratedColumn showAt = GeneratedColumn( - 'show_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + 'show_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); late final GeneratedColumn hideAt = GeneratedColumn( - 'hide_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + 'hide_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); @override List get $columns => [ - id, - createdAt, - updatedAt, - deletedAt, - ownerId, - type, - data, - isSaved, - memoryAt, - seenAt, - showAt, - hideAt - ]; + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -4104,30 +4818,54 @@ class MemoryEntity extends Table MemoryEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return MemoryEntityData( - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - deletedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), - ownerId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - type: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}type'])!, - data: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}data'])!, - isSaved: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}is_saved'])!, - memoryAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}memory_at'])!, - seenAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}seen_at']), - showAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}show_at']), - hideAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}hide_at']), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + data: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}data'], + )!, + isSaved: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_saved'], + )!, + memoryAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}memory_at'], + )!, + seenAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}seen_at'], + ), + showAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}show_at'], + ), + hideAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}hide_at'], + ), ); } @@ -4156,19 +4894,20 @@ class MemoryEntityData extends DataClass final DateTime? seenAt; final DateTime? showAt; final DateTime? hideAt; - const MemoryEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - this.deletedAt, - required this.ownerId, - required this.type, - required this.data, - required this.isSaved, - required this.memoryAt, - this.seenAt, - this.showAt, - this.hideAt}); + const MemoryEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -4195,8 +4934,10 @@ class MemoryEntityData extends DataClass return map; } - factory MemoryEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory MemoryEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return MemoryEntityData( id: serializer.fromJson(json['id']), @@ -4232,33 +4973,33 @@ class MemoryEntityData extends DataClass }; } - MemoryEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - Value deletedAt = const Value.absent(), - String? ownerId, - int? type, - String? data, - bool? isSaved, - DateTime? memoryAt, - Value seenAt = const Value.absent(), - Value showAt = const Value.absent(), - Value hideAt = const Value.absent()}) => - MemoryEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, - ownerId: ownerId ?? this.ownerId, - type: type ?? this.type, - data: data ?? this.data, - isSaved: isSaved ?? this.isSaved, - memoryAt: memoryAt ?? this.memoryAt, - seenAt: seenAt.present ? seenAt.value : this.seenAt, - showAt: showAt.present ? showAt.value : this.showAt, - hideAt: hideAt.present ? hideAt.value : this.hideAt, - ); + MemoryEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + Value deletedAt = const Value.absent(), + String? ownerId, + int? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + Value seenAt = const Value.absent(), + Value showAt = const Value.absent(), + Value hideAt = const Value.absent(), + }) => MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); MemoryEntityData copyWithCompanion(MemoryEntityCompanion data) { return MemoryEntityData( id: data.id.present ? data.id.value : this.id, @@ -4296,8 +5037,20 @@ class MemoryEntityData extends DataClass } @override - int get hashCode => Object.hash(id, createdAt, updatedAt, deletedAt, ownerId, - type, data, isSaved, memoryAt, seenAt, showAt, hideAt); + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -4356,11 +5109,11 @@ class MemoryEntityCompanion extends UpdateCompanion { this.seenAt = const Value.absent(), this.showAt = const Value.absent(), this.hideAt = const Value.absent(), - }) : id = Value(id), - ownerId = Value(ownerId), - type = Value(type), - data = Value(data), - memoryAt = Value(memoryAt); + }) : id = Value(id), + ownerId = Value(ownerId), + type = Value(type), + data = Value(data), + memoryAt = Value(memoryAt); static Insertable custom({ Expression? id, Expression? createdAt, @@ -4391,19 +5144,20 @@ class MemoryEntityCompanion extends UpdateCompanion { }); } - MemoryEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? deletedAt, - Value? ownerId, - Value? type, - Value? data, - Value? isSaved, - Value? memoryAt, - Value? seenAt, - Value? showAt, - Value? hideAt}) { + MemoryEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? deletedAt, + Value? ownerId, + Value? type, + Value? data, + Value? isSaved, + Value? memoryAt, + Value? seenAt, + Value? showAt, + Value? hideAt, + }) { return MemoryEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -4489,17 +5243,25 @@ class MemoryAssetEntity extends Table final String? _alias; MemoryAssetEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn assetId = GeneratedColumn( - 'asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn memoryId = GeneratedColumn( - 'memory_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES memory_entity (id) ON DELETE CASCADE')); + 'memory_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, memoryId]; @override @@ -4513,10 +5275,14 @@ class MemoryAssetEntity extends Table MemoryAssetEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return MemoryAssetEntityData( - assetId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - memoryId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}memory_id'])!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + memoryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}memory_id'], + )!, ); } @@ -4544,8 +5310,10 @@ class MemoryAssetEntityData extends DataClass return map; } - factory MemoryAssetEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory MemoryAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return MemoryAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -4603,8 +5371,8 @@ class MemoryAssetEntityCompanion MemoryAssetEntityCompanion.insert({ required String assetId, required String memoryId, - }) : assetId = Value(assetId), - memoryId = Value(memoryId); + }) : assetId = Value(assetId), + memoryId = Value(memoryId); static Insertable custom({ Expression? assetId, Expression? memoryId, @@ -4615,8 +5383,10 @@ class MemoryAssetEntityCompanion }); } - MemoryAssetEntityCompanion copyWith( - {Value? assetId, Value? memoryId}) { + MemoryAssetEntityCompanion copyWith({ + Value? assetId, + Value? memoryId, + }) { return MemoryAssetEntityCompanion( assetId: assetId ?? this.assetId, memoryId: memoryId ?? this.memoryId, @@ -4645,19 +5415,540 @@ class MemoryAssetEntityCompanion } } +class PersonEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PersonEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn faceAssetId = GeneratedColumn( + 'face_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn thumbnailPath = GeneratedColumn( + 'thumbnail_path', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); + late final GeneratedColumn isHidden = GeneratedColumn( + 'is_hidden', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); + late final GeneratedColumn color = GeneratedColumn( + 'color', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn birthDate = GeneratedColumn( + 'birth_date', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + thumbnailPath, + isFavorite, + isHidden, + color, + birthDate, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'person_entity'; + @override + Set get $primaryKey => {id}; + @override + PersonEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PersonEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + faceAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}face_asset_id'], + ), + thumbnailPath: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_path'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + isHidden: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_hidden'], + )!, + color: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}color'], + ), + birthDate: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}birth_date'], + ), + ); + } + + @override + PersonEntity createAlias(String alias) { + return PersonEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PersonEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String name; + final String? faceAssetId; + final String thumbnailPath; + final bool isFavorite; + final bool isHidden; + final String? color; + final DateTime? birthDate; + const PersonEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.thumbnailPath, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['name'] = Variable(name); + if (!nullToAbsent || faceAssetId != null) { + map['face_asset_id'] = Variable(faceAssetId); + } + map['thumbnail_path'] = Variable(thumbnailPath); + map['is_favorite'] = Variable(isFavorite); + map['is_hidden'] = Variable(isHidden); + if (!nullToAbsent || color != null) { + map['color'] = Variable(color); + } + if (!nullToAbsent || birthDate != null) { + map['birth_date'] = Variable(birthDate); + } + return map; + } + + factory PersonEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PersonEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + name: serializer.fromJson(json['name']), + faceAssetId: serializer.fromJson(json['faceAssetId']), + thumbnailPath: serializer.fromJson(json['thumbnailPath']), + isFavorite: serializer.fromJson(json['isFavorite']), + isHidden: serializer.fromJson(json['isHidden']), + color: serializer.fromJson(json['color']), + birthDate: serializer.fromJson(json['birthDate']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'name': serializer.toJson(name), + 'faceAssetId': serializer.toJson(faceAssetId), + 'thumbnailPath': serializer.toJson(thumbnailPath), + 'isFavorite': serializer.toJson(isFavorite), + 'isHidden': serializer.toJson(isHidden), + 'color': serializer.toJson(color), + 'birthDate': serializer.toJson(birthDate), + }; + } + + PersonEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + Value faceAssetId = const Value.absent(), + String? thumbnailPath, + bool? isFavorite, + bool? isHidden, + Value color = const Value.absent(), + Value birthDate = const Value.absent(), + }) => PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + thumbnailPath: thumbnailPath ?? this.thumbnailPath, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); + PersonEntityData copyWithCompanion(PersonEntityCompanion data) { + return PersonEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + name: data.name.present ? data.name.value : this.name, + faceAssetId: data.faceAssetId.present + ? data.faceAssetId.value + : this.faceAssetId, + thumbnailPath: data.thumbnailPath.present + ? data.thumbnailPath.value + : this.thumbnailPath, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, + color: data.color.present ? data.color.value : this.color, + birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, + ); + } + + @override + String toString() { + return (StringBuffer('PersonEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('thumbnailPath: $thumbnailPath, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + thumbnailPath, + isFavorite, + isHidden, + color, + birthDate, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PersonEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.name == this.name && + other.faceAssetId == this.faceAssetId && + other.thumbnailPath == this.thumbnailPath && + other.isFavorite == this.isFavorite && + other.isHidden == this.isHidden && + other.color == this.color && + other.birthDate == this.birthDate); +} + +class PersonEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value name; + final Value faceAssetId; + final Value thumbnailPath; + final Value isFavorite; + final Value isHidden; + final Value color; + final Value birthDate; + const PersonEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.name = const Value.absent(), + this.faceAssetId = const Value.absent(), + this.thumbnailPath = const Value.absent(), + this.isFavorite = const Value.absent(), + this.isHidden = const Value.absent(), + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }); + PersonEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String name, + this.faceAssetId = const Value.absent(), + required String thumbnailPath, + required bool isFavorite, + required bool isHidden, + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + name = Value(name), + thumbnailPath = Value(thumbnailPath), + isFavorite = Value(isFavorite), + isHidden = Value(isHidden); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? name, + Expression? faceAssetId, + Expression? thumbnailPath, + Expression? isFavorite, + Expression? isHidden, + Expression? color, + Expression? birthDate, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (name != null) 'name': name, + if (faceAssetId != null) 'face_asset_id': faceAssetId, + if (thumbnailPath != null) 'thumbnail_path': thumbnailPath, + if (isFavorite != null) 'is_favorite': isFavorite, + if (isHidden != null) 'is_hidden': isHidden, + if (color != null) 'color': color, + if (birthDate != null) 'birth_date': birthDate, + }); + } + + PersonEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? name, + Value? faceAssetId, + Value? thumbnailPath, + Value? isFavorite, + Value? isHidden, + Value? color, + Value? birthDate, + }) { + return PersonEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId ?? this.faceAssetId, + thumbnailPath: thumbnailPath ?? this.thumbnailPath, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color ?? this.color, + birthDate: birthDate ?? this.birthDate, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (faceAssetId.present) { + map['face_asset_id'] = Variable(faceAssetId.value); + } + if (thumbnailPath.present) { + map['thumbnail_path'] = Variable(thumbnailPath.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (isHidden.present) { + map['is_hidden'] = Variable(isHidden.value); + } + if (color.present) { + map['color'] = Variable(color.value); + } + if (birthDate.present) { + map['birth_date'] = Variable(birthDate.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PersonEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('thumbnailPath: $thumbnailPath, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } +} + class DatabaseAtV1 extends GeneratedDatabase { DatabaseAtV1(QueryExecutor e) : super(e); late final UserEntity userEntity = UserEntity(this); late final RemoteAssetEntity remoteAssetEntity = RemoteAssetEntity(this); late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); late final StackEntity stackEntity = StackEntity(this); - late final Index idxLocalAssetChecksum = Index('idx_local_asset_checksum', - 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)'); + late final Index idxLocalAssetChecksum = Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); late final Index uQRemoteAssetOwnerChecksum = Index( - 'UQ_remote_asset_owner_checksum', - 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)'); - late final Index idxRemoteAssetChecksum = Index('idx_remote_asset_checksum', - 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)'); + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', + ); + late final Index idxRemoteAssetChecksum = Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); late final UserMetadataEntity userMetadataEntity = UserMetadataEntity(this); late final PartnerEntity partnerEntity = PartnerEntity(this); late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); @@ -4671,29 +5962,31 @@ class DatabaseAtV1 extends GeneratedDatabase { RemoteAlbumUserEntity(this); late final MemoryEntity memoryEntity = MemoryEntity(this); late final MemoryAssetEntity memoryAssetEntity = MemoryAssetEntity(this); + late final PersonEntity personEntity = PersonEntity(this); @override Iterable> get allTables => allSchemaEntities.whereType>(); @override List get allSchemaEntities => [ - userEntity, - remoteAssetEntity, - localAssetEntity, - stackEntity, - idxLocalAssetChecksum, - uQRemoteAssetOwnerChecksum, - idxRemoteAssetChecksum, - userMetadataEntity, - partnerEntity, - localAlbumEntity, - localAlbumAssetEntity, - remoteExifEntity, - remoteAlbumEntity, - remoteAlbumAssetEntity, - remoteAlbumUserEntity, - memoryEntity, - memoryAssetEntity - ]; + userEntity, + remoteAssetEntity, + localAssetEntity, + stackEntity, + idxLocalAssetChecksum, + uQRemoteAssetOwnerChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + localAlbumEntity, + localAlbumAssetEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + ]; @override int get schemaVersion => 1; @override diff --git a/mobile/test/drift/main/generated/schema_v2.dart b/mobile/test/drift/main/generated/schema_v2.dart index 8345cef906..903d13b9a2 100644 --- a/mobile/test/drift/main/generated/schema_v2.dart +++ b/mobile/test/drift/main/generated/schema_v2.dart @@ -9,48 +9,78 @@ class UserEntity extends Table with TableInfo { final String? _alias; UserEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn name = GeneratedColumn( - 'name', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn isAdmin = GeneratedColumn( - 'is_admin', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("is_admin" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + 'is_admin', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); late final GeneratedColumn email = GeneratedColumn( - 'email', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'email', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn profileImagePath = GeneratedColumn( - 'profile_image_path', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'profile_image_path', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn quotaSizeInBytes = GeneratedColumn( - 'quota_size_in_bytes', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'quota_size_in_bytes', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn quotaUsageInBytes = GeneratedColumn( - 'quota_usage_in_bytes', aliasedName, false, - type: DriftSqlType.int, - requiredDuringInsert: false, - defaultValue: const CustomExpression('0')); + 'quota_usage_in_bytes', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); @override List get $columns => [ - id, - name, - isAdmin, - email, - profileImagePath, - updatedAt, - quotaSizeInBytes, - quotaUsageInBytes - ]; + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -62,22 +92,38 @@ class UserEntity extends Table with TableInfo { UserEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return UserEntityData( - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}name'])!, - isAdmin: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}is_admin'])!, - email: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}email'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + isAdmin: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_admin'], + )!, + email: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}email'], + )!, profileImagePath: attachedDatabase.typeMapping.read( - DriftSqlType.string, data['${effectivePrefix}profile_image_path']), - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, + DriftSqlType.string, + data['${effectivePrefix}profile_image_path'], + ), + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, quotaSizeInBytes: attachedDatabase.typeMapping.read( - DriftSqlType.int, data['${effectivePrefix}quota_size_in_bytes']), + DriftSqlType.int, + data['${effectivePrefix}quota_size_in_bytes'], + ), quotaUsageInBytes: attachedDatabase.typeMapping.read( - DriftSqlType.int, data['${effectivePrefix}quota_usage_in_bytes'])!, + DriftSqlType.int, + data['${effectivePrefix}quota_usage_in_bytes'], + )!, ); } @@ -101,15 +147,16 @@ class UserEntityData extends DataClass implements Insertable { final DateTime updatedAt; final int? quotaSizeInBytes; final int quotaUsageInBytes; - const UserEntityData( - {required this.id, - required this.name, - required this.isAdmin, - required this.email, - this.profileImagePath, - required this.updatedAt, - this.quotaSizeInBytes, - required this.quotaUsageInBytes}); + const UserEntityData({ + required this.id, + required this.name, + required this.isAdmin, + required this.email, + this.profileImagePath, + required this.updatedAt, + this.quotaSizeInBytes, + required this.quotaUsageInBytes, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -128,8 +175,10 @@ class UserEntityData extends DataClass implements Insertable { return map; } - factory UserEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory UserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return UserEntityData( id: serializer.fromJson(json['id']), @@ -157,29 +206,29 @@ class UserEntityData extends DataClass implements Insertable { }; } - UserEntityData copyWith( - {String? id, - String? name, - bool? isAdmin, - String? email, - Value profileImagePath = const Value.absent(), - DateTime? updatedAt, - Value quotaSizeInBytes = const Value.absent(), - int? quotaUsageInBytes}) => - UserEntityData( - id: id ?? this.id, - name: name ?? this.name, - isAdmin: isAdmin ?? this.isAdmin, - email: email ?? this.email, - profileImagePath: profileImagePath.present - ? profileImagePath.value - : this.profileImagePath, - updatedAt: updatedAt ?? this.updatedAt, - quotaSizeInBytes: quotaSizeInBytes.present - ? quotaSizeInBytes.value - : this.quotaSizeInBytes, - quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, - ); + UserEntityData copyWith({ + String? id, + String? name, + bool? isAdmin, + String? email, + Value profileImagePath = const Value.absent(), + DateTime? updatedAt, + Value quotaSizeInBytes = const Value.absent(), + int? quotaUsageInBytes, + }) => UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + profileImagePath: profileImagePath.present + ? profileImagePath.value + : this.profileImagePath, + updatedAt: updatedAt ?? this.updatedAt, + quotaSizeInBytes: quotaSizeInBytes.present + ? quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + ); UserEntityData copyWithCompanion(UserEntityCompanion data) { return UserEntityData( id: data.id.present ? data.id.value : this.id, @@ -215,8 +264,16 @@ class UserEntityData extends DataClass implements Insertable { } @override - int get hashCode => Object.hash(id, name, isAdmin, email, profileImagePath, - updatedAt, quotaSizeInBytes, quotaUsageInBytes); + int get hashCode => Object.hash( + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -259,9 +316,9 @@ class UserEntityCompanion extends UpdateCompanion { this.updatedAt = const Value.absent(), this.quotaSizeInBytes = const Value.absent(), this.quotaUsageInBytes = const Value.absent(), - }) : id = Value(id), - name = Value(name), - email = Value(email); + }) : id = Value(id), + name = Value(name), + email = Value(email); static Insertable custom({ Expression? id, Expression? name, @@ -284,15 +341,16 @@ class UserEntityCompanion extends UpdateCompanion { }); } - UserEntityCompanion copyWith( - {Value? id, - Value? name, - Value? isAdmin, - Value? email, - Value? profileImagePath, - Value? updatedAt, - Value? quotaSizeInBytes, - Value? quotaUsageInBytes}) { + UserEntityCompanion copyWith({ + Value? id, + Value? name, + Value? isAdmin, + Value? email, + Value? profileImagePath, + Value? updatedAt, + Value? quotaSizeInBytes, + Value? quotaUsageInBytes, + }) { return UserEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -358,87 +416,154 @@ class RemoteAssetEntity extends Table final String? _alias; RemoteAssetEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn name = GeneratedColumn( - 'name', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn type = GeneratedColumn( - 'type', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); late final GeneratedColumn createdAt = GeneratedColumn( - 'created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn width = GeneratedColumn( - 'width', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn height = GeneratedColumn( - 'height', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn durationInSeconds = GeneratedColumn( - 'duration_in_seconds', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn checksum = GeneratedColumn( - 'checksum', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'checksum', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn isFavorite = GeneratedColumn( - 'is_favorite', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); late final GeneratedColumn ownerId = GeneratedColumn( - 'owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn localDateTime = - GeneratedColumn('local_date_time', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); late final GeneratedColumn thumbHash = GeneratedColumn( - 'thumb_hash', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'thumb_hash', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn deletedAt = GeneratedColumn( - 'deleted_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); late final GeneratedColumn livePhotoVideoId = GeneratedColumn( - 'live_photo_video_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'live_photo_video_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn visibility = GeneratedColumn( - 'visibility', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'visibility', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); late final GeneratedColumn stackId = GeneratedColumn( - 'stack_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'stack_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); @override List get $columns => [ - name, - type, - createdAt, - updatedAt, - width, - height, - durationInSeconds, - id, - checksum, - isFavorite, - ownerId, - localDateTime, - thumbHash, - deletedAt, - livePhotoVideoId, - visibility, - stackId - ]; + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -450,40 +575,74 @@ class RemoteAssetEntity extends Table RemoteAssetEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAssetEntityData( - name: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}name'])!, - type: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}type'])!, - createdAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - width: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}width']), - height: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}height']), + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), durationInSeconds: attachedDatabase.typeMapping.read( - DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - checksum: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}checksum'])!, - isFavorite: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - ownerId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, localDateTime: attachedDatabase.typeMapping.read( - DriftSqlType.dateTime, data['${effectivePrefix}local_date_time']), - thumbHash: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}thumb_hash']), - deletedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), + DriftSqlType.dateTime, + data['${effectivePrefix}local_date_time'], + ), + thumbHash: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumb_hash'], + ), + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), livePhotoVideoId: attachedDatabase.typeMapping.read( - DriftSqlType.string, data['${effectivePrefix}live_photo_video_id']), - visibility: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}visibility'])!, - stackId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}stack_id']), + DriftSqlType.string, + data['${effectivePrefix}live_photo_video_id'], + ), + visibility: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}visibility'], + )!, + stackId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}stack_id'], + ), ); } @@ -517,24 +676,25 @@ class RemoteAssetEntityData extends DataClass final String? livePhotoVideoId; final int visibility; final String? stackId; - const RemoteAssetEntityData( - {required this.name, - required this.type, - required this.createdAt, - required this.updatedAt, - this.width, - this.height, - this.durationInSeconds, - required this.id, - required this.checksum, - required this.isFavorite, - required this.ownerId, - this.localDateTime, - this.thumbHash, - this.deletedAt, - this.livePhotoVideoId, - required this.visibility, - this.stackId}); + const RemoteAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + required this.checksum, + required this.isFavorite, + required this.ownerId, + this.localDateTime, + this.thumbHash, + this.deletedAt, + this.livePhotoVideoId, + required this.visibility, + this.stackId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -574,8 +734,10 @@ class RemoteAssetEntityData extends DataClass return map; } - factory RemoteAssetEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory RemoteAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAssetEntityData( name: serializer.fromJson(json['name']), @@ -621,48 +783,49 @@ class RemoteAssetEntityData extends DataClass }; } - RemoteAssetEntityData copyWith( - {String? name, - int? type, - DateTime? createdAt, - DateTime? updatedAt, - Value width = const Value.absent(), - Value height = const Value.absent(), - Value durationInSeconds = const Value.absent(), - String? id, - String? checksum, - bool? isFavorite, - String? ownerId, - Value localDateTime = const Value.absent(), - Value thumbHash = const Value.absent(), - Value deletedAt = const Value.absent(), - Value livePhotoVideoId = const Value.absent(), - int? visibility, - Value stackId = const Value.absent()}) => - RemoteAssetEntityData( - name: name ?? this.name, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - width: width.present ? width.value : this.width, - height: height.present ? height.value : this.height, - durationInSeconds: durationInSeconds.present - ? durationInSeconds.value - : this.durationInSeconds, - id: id ?? this.id, - checksum: checksum ?? this.checksum, - isFavorite: isFavorite ?? this.isFavorite, - ownerId: ownerId ?? this.ownerId, - localDateTime: - localDateTime.present ? localDateTime.value : this.localDateTime, - thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, - deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, - livePhotoVideoId: livePhotoVideoId.present - ? livePhotoVideoId.value - : this.livePhotoVideoId, - visibility: visibility ?? this.visibility, - stackId: stackId.present ? stackId.value : this.stackId, - ); + RemoteAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + String? checksum, + bool? isFavorite, + String? ownerId, + Value localDateTime = const Value.absent(), + Value thumbHash = const Value.absent(), + Value deletedAt = const Value.absent(), + Value livePhotoVideoId = const Value.absent(), + int? visibility, + Value stackId = const Value.absent(), + }) => RemoteAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime.present + ? localDateTime.value + : this.localDateTime, + thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + livePhotoVideoId: livePhotoVideoId.present + ? livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId.present ? stackId.value : this.stackId, + ); RemoteAssetEntityData copyWithCompanion(RemoteAssetEntityCompanion data) { return RemoteAssetEntityData( name: data.name.present ? data.name.value : this.name, @@ -676,8 +839,9 @@ class RemoteAssetEntityData extends DataClass : this.durationInSeconds, id: data.id.present ? data.id.value : this.id, checksum: data.checksum.present ? data.checksum.value : this.checksum, - isFavorite: - data.isFavorite.present ? data.isFavorite.value : this.isFavorite, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, localDateTime: data.localDateTime.present ? data.localDateTime.value @@ -687,8 +851,9 @@ class RemoteAssetEntityData extends DataClass livePhotoVideoId: data.livePhotoVideoId.present ? data.livePhotoVideoId.value : this.livePhotoVideoId, - visibility: - data.visibility.present ? data.visibility.value : this.visibility, + visibility: data.visibility.present + ? data.visibility.value + : this.visibility, stackId: data.stackId.present ? data.stackId.value : this.stackId, ); } @@ -719,23 +884,24 @@ class RemoteAssetEntityData extends DataClass @override int get hashCode => Object.hash( - name, - type, - createdAt, - updatedAt, - width, - height, - durationInSeconds, - id, - checksum, - isFavorite, - ownerId, - localDateTime, - thumbHash, - deletedAt, - livePhotoVideoId, - visibility, - stackId); + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -815,12 +981,12 @@ class RemoteAssetEntityCompanion this.livePhotoVideoId = const Value.absent(), required int visibility, this.stackId = const Value.absent(), - }) : name = Value(name), - type = Value(type), - id = Value(id), - checksum = Value(checksum), - ownerId = Value(ownerId), - visibility = Value(visibility); + }) : name = Value(name), + type = Value(type), + id = Value(id), + checksum = Value(checksum), + ownerId = Value(ownerId), + visibility = Value(visibility); static Insertable custom({ Expression? name, Expression? type, @@ -861,24 +1027,25 @@ class RemoteAssetEntityCompanion }); } - RemoteAssetEntityCompanion copyWith( - {Value? name, - Value? type, - Value? createdAt, - Value? updatedAt, - Value? width, - Value? height, - Value? durationInSeconds, - Value? id, - Value? checksum, - Value? isFavorite, - Value? ownerId, - Value? localDateTime, - Value? thumbHash, - Value? deletedAt, - Value? livePhotoVideoId, - Value? visibility, - Value? stackId}) { + RemoteAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? ownerId, + Value? localDateTime, + Value? thumbHash, + Value? deletedAt, + Value? livePhotoVideoId, + Value? visibility, + Value? stackId, + }) { return RemoteAssetEntityCompanion( name: name ?? this.name, type: type ?? this.type, @@ -989,62 +1156,103 @@ class LocalAssetEntity extends Table final String? _alias; LocalAssetEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn name = GeneratedColumn( - 'name', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn type = GeneratedColumn( - 'type', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); late final GeneratedColumn createdAt = GeneratedColumn( - 'created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn width = GeneratedColumn( - 'width', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn height = GeneratedColumn( - 'height', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn durationInSeconds = GeneratedColumn( - 'duration_in_seconds', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn checksum = GeneratedColumn( - 'checksum', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'checksum', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn isFavorite = GeneratedColumn( - 'is_favorite', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("is_favorite" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); late final GeneratedColumn orientation = GeneratedColumn( - 'orientation', aliasedName, false, - type: DriftSqlType.int, - requiredDuringInsert: false, - defaultValue: const CustomExpression('0')); + 'orientation', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); @override List get $columns => [ - name, - type, - createdAt, - updatedAt, - width, - height, - durationInSeconds, - id, - checksum, - isFavorite, - orientation - ]; + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -1056,28 +1264,50 @@ class LocalAssetEntity extends Table LocalAssetEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LocalAssetEntityData( - name: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}name'])!, - type: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}type'])!, - createdAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - width: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}width']), - height: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}height']), + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), durationInSeconds: attachedDatabase.typeMapping.read( - DriftSqlType.int, data['${effectivePrefix}duration_in_seconds']), - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - checksum: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}checksum']), - isFavorite: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}is_favorite'])!, - orientation: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}orientation'])!, + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}orientation'], + )!, ); } @@ -1105,18 +1335,19 @@ class LocalAssetEntityData extends DataClass final String? checksum; final bool isFavorite; final int orientation; - const LocalAssetEntityData( - {required this.name, - required this.type, - required this.createdAt, - required this.updatedAt, - this.width, - this.height, - this.durationInSeconds, - required this.id, - this.checksum, - required this.isFavorite, - required this.orientation}); + const LocalAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + this.checksum, + required this.isFavorite, + required this.orientation, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1142,8 +1373,10 @@ class LocalAssetEntityData extends DataClass return map; } - factory LocalAssetEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory LocalAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LocalAssetEntityData( name: serializer.fromJson(json['name']), @@ -1177,33 +1410,33 @@ class LocalAssetEntityData extends DataClass }; } - LocalAssetEntityData copyWith( - {String? name, - int? type, - DateTime? createdAt, - DateTime? updatedAt, - Value width = const Value.absent(), - Value height = const Value.absent(), - Value durationInSeconds = const Value.absent(), - String? id, - Value checksum = const Value.absent(), - bool? isFavorite, - int? orientation}) => - LocalAssetEntityData( - name: name ?? this.name, - type: type ?? this.type, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - width: width.present ? width.value : this.width, - height: height.present ? height.value : this.height, - durationInSeconds: durationInSeconds.present - ? durationInSeconds.value - : this.durationInSeconds, - id: id ?? this.id, - checksum: checksum.present ? checksum.value : this.checksum, - isFavorite: isFavorite ?? this.isFavorite, - orientation: orientation ?? this.orientation, - ); + LocalAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + Value checksum = const Value.absent(), + bool? isFavorite, + int? orientation, + }) => LocalAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); LocalAssetEntityData copyWithCompanion(LocalAssetEntityCompanion data) { return LocalAssetEntityData( name: data.name.present ? data.name.value : this.name, @@ -1217,10 +1450,12 @@ class LocalAssetEntityData extends DataClass : this.durationInSeconds, id: data.id.present ? data.id.value : this.id, checksum: data.checksum.present ? data.checksum.value : this.checksum, - isFavorite: - data.isFavorite.present ? data.isFavorite.value : this.isFavorite, - orientation: - data.orientation.present ? data.orientation.value : this.orientation, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, ); } @@ -1243,8 +1478,19 @@ class LocalAssetEntityData extends DataClass } @override - int get hashCode => Object.hash(name, type, createdAt, updatedAt, width, - height, durationInSeconds, id, checksum, isFavorite, orientation); + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -1299,9 +1545,9 @@ class LocalAssetEntityCompanion extends UpdateCompanion { this.checksum = const Value.absent(), this.isFavorite = const Value.absent(), this.orientation = const Value.absent(), - }) : name = Value(name), - type = Value(type), - id = Value(id); + }) : name = Value(name), + type = Value(type), + id = Value(id); static Insertable custom({ Expression? name, Expression? type, @@ -1330,18 +1576,19 @@ class LocalAssetEntityCompanion extends UpdateCompanion { }); } - LocalAssetEntityCompanion copyWith( - {Value? name, - Value? type, - Value? createdAt, - Value? updatedAt, - Value? width, - Value? height, - Value? durationInSeconds, - Value? id, - Value? checksum, - Value? isFavorite, - Value? orientation}) { + LocalAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? orientation, + }) { return LocalAssetEntityCompanion( name: name ?? this.name, type: type ?? this.type, @@ -1421,33 +1668,56 @@ class StackEntity extends Table with TableInfo { final String? _alias; StackEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn createdAt = GeneratedColumn( - 'created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn ownerId = GeneratedColumn( - 'owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn primaryAssetId = GeneratedColumn( - 'primary_asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id)')); + 'primary_asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id)', + ), + ); @override - List get $columns => - [id, createdAt, updatedAt, ownerId, primaryAssetId]; + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + primaryAssetId, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -1459,16 +1729,26 @@ class StackEntity extends Table with TableInfo { StackEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return StackEntityData( - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, primaryAssetId: attachedDatabase.typeMapping.read( - DriftSqlType.string, data['${effectivePrefix}primary_asset_id'])!, + DriftSqlType.string, + data['${effectivePrefix}primary_asset_id'], + )!, ); } @@ -1489,12 +1769,13 @@ class StackEntityData extends DataClass implements Insertable { final DateTime updatedAt; final String ownerId; final String primaryAssetId; - const StackEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - required this.primaryAssetId}); + const StackEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1506,8 +1787,10 @@ class StackEntityData extends DataClass implements Insertable { return map; } - factory StackEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory StackEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return StackEntityData( id: serializer.fromJson(json['id']), @@ -1529,19 +1812,19 @@ class StackEntityData extends DataClass implements Insertable { }; } - StackEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - String? ownerId, - String? primaryAssetId}) => - StackEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - primaryAssetId: primaryAssetId ?? this.primaryAssetId, - ); + StackEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId, + }) => StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); StackEntityData copyWithCompanion(StackEntityCompanion data) { return StackEntityData( id: data.id.present ? data.id.value : this.id, @@ -1599,9 +1882,9 @@ class StackEntityCompanion extends UpdateCompanion { this.updatedAt = const Value.absent(), required String ownerId, required String primaryAssetId, - }) : id = Value(id), - ownerId = Value(ownerId), - primaryAssetId = Value(primaryAssetId); + }) : id = Value(id), + ownerId = Value(ownerId), + primaryAssetId = Value(primaryAssetId); static Insertable custom({ Expression? id, Expression? createdAt, @@ -1618,12 +1901,13 @@ class StackEntityCompanion extends UpdateCompanion { }); } - StackEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? primaryAssetId}) { + StackEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? primaryAssetId, + }) { return StackEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -1674,17 +1958,29 @@ class UserMetadataEntity extends Table final String? _alias; UserMetadataEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn userId = GeneratedColumn( - 'user_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn key = GeneratedColumn( - 'key', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'key', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); late final GeneratedColumn value = GeneratedColumn( - 'value', aliasedName, false, - type: DriftSqlType.blob, requiredDuringInsert: true); + 'value', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + ); @override List get $columns => [userId, key, value]; @override @@ -1698,12 +1994,18 @@ class UserMetadataEntity extends Table UserMetadataEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return UserMetadataEntityData( - userId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, - key: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}key'])!, - value: attachedDatabase.typeMapping - .read(DriftSqlType.blob, data['${effectivePrefix}value'])!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + key: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}key'], + )!, + value: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}value'], + )!, ); } @@ -1723,8 +2025,11 @@ class UserMetadataEntityData extends DataClass final String userId; final int key; final Uint8List value; - const UserMetadataEntityData( - {required this.userId, required this.key, required this.value}); + const UserMetadataEntityData({ + required this.userId, + required this.key, + required this.value, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1734,8 +2039,10 @@ class UserMetadataEntityData extends DataClass return map; } - factory UserMetadataEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory UserMetadataEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return UserMetadataEntityData( userId: serializer.fromJson(json['userId']), @@ -1753,13 +2060,15 @@ class UserMetadataEntityData extends DataClass }; } - UserMetadataEntityData copyWith( - {String? userId, int? key, Uint8List? value}) => - UserMetadataEntityData( - userId: userId ?? this.userId, - key: key ?? this.key, - value: value ?? this.value, - ); + UserMetadataEntityData copyWith({ + String? userId, + int? key, + Uint8List? value, + }) => UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { return UserMetadataEntityData( userId: data.userId.present ? data.userId.value : this.userId, @@ -1803,9 +2112,9 @@ class UserMetadataEntityCompanion required String userId, required int key, required Uint8List value, - }) : userId = Value(userId), - key = Value(key), - value = Value(value); + }) : userId = Value(userId), + key = Value(key), + value = Value(value); static Insertable custom({ Expression? userId, Expression? key, @@ -1818,8 +2127,11 @@ class UserMetadataEntityCompanion }); } - UserMetadataEntityCompanion copyWith( - {Value? userId, Value? key, Value? value}) { + UserMetadataEntityCompanion copyWith({ + Value? userId, + Value? key, + Value? value, + }) { return UserMetadataEntityCompanion( userId: userId ?? this.userId, key: key ?? this.key, @@ -1860,24 +2172,36 @@ class PartnerEntity extends Table final String? _alias; PartnerEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn sharedById = GeneratedColumn( - 'shared_by_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'shared_by_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn sharedWithId = GeneratedColumn( - 'shared_with_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'shared_with_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn inTimeline = GeneratedColumn( - 'in_timeline', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("in_timeline" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + 'in_timeline', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); @override List get $columns => [sharedById, sharedWithId, inTimeline]; @override @@ -1891,12 +2215,18 @@ class PartnerEntity extends Table PartnerEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return PartnerEntityData( - sharedById: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}shared_by_id'])!, - sharedWithId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}shared_with_id'])!, - inTimeline: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}in_timeline'])!, + sharedById: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_by_id'], + )!, + sharedWithId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_with_id'], + )!, + inTimeline: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}in_timeline'], + )!, ); } @@ -1916,10 +2246,11 @@ class PartnerEntityData extends DataClass final String sharedById; final String sharedWithId; final bool inTimeline; - const PartnerEntityData( - {required this.sharedById, - required this.sharedWithId, - required this.inTimeline}); + const PartnerEntityData({ + required this.sharedById, + required this.sharedWithId, + required this.inTimeline, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -1929,8 +2260,10 @@ class PartnerEntityData extends DataClass return map; } - factory PartnerEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory PartnerEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return PartnerEntityData( sharedById: serializer.fromJson(json['sharedById']), @@ -1948,22 +2281,26 @@ class PartnerEntityData extends DataClass }; } - PartnerEntityData copyWith( - {String? sharedById, String? sharedWithId, bool? inTimeline}) => - PartnerEntityData( - sharedById: sharedById ?? this.sharedById, - sharedWithId: sharedWithId ?? this.sharedWithId, - inTimeline: inTimeline ?? this.inTimeline, - ); + PartnerEntityData copyWith({ + String? sharedById, + String? sharedWithId, + bool? inTimeline, + }) => PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { return PartnerEntityData( - sharedById: - data.sharedById.present ? data.sharedById.value : this.sharedById, + sharedById: data.sharedById.present + ? data.sharedById.value + : this.sharedById, sharedWithId: data.sharedWithId.present ? data.sharedWithId.value : this.sharedWithId, - inTimeline: - data.inTimeline.present ? data.inTimeline.value : this.inTimeline, + inTimeline: data.inTimeline.present + ? data.inTimeline.value + : this.inTimeline, ); } @@ -2001,8 +2338,8 @@ class PartnerEntityCompanion extends UpdateCompanion { required String sharedById, required String sharedWithId, this.inTimeline = const Value.absent(), - }) : sharedById = Value(sharedById), - sharedWithId = Value(sharedWithId); + }) : sharedById = Value(sharedById), + sharedWithId = Value(sharedWithId); static Insertable custom({ Expression? sharedById, Expression? sharedWithId, @@ -2015,10 +2352,11 @@ class PartnerEntityCompanion extends UpdateCompanion { }); } - PartnerEntityCompanion copyWith( - {Value? sharedById, - Value? sharedWithId, - Value? inTimeline}) { + PartnerEntityCompanion copyWith({ + Value? sharedById, + Value? sharedWithId, + Value? inTimeline, + }) { return PartnerEntityCompanion( sharedById: sharedById ?? this.sharedById, sharedWithId: sharedWithId ?? this.sharedWithId, @@ -2059,35 +2397,64 @@ class LocalAlbumEntity extends Table final String? _alias; LocalAlbumEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn name = GeneratedColumn( - 'name', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn backupSelection = GeneratedColumn( - 'backup_selection', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'backup_selection', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); late final GeneratedColumn isIosSharedAlbum = GeneratedColumn( - 'is_ios_shared_album', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'CHECK ("is_ios_shared_album" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + 'is_ios_shared_album', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); late final GeneratedColumn marker_ = GeneratedColumn( - 'marker', aliasedName, true, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("marker" IN (0, 1))')); + 'marker', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); @override - List get $columns => - [id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_]; + List get $columns => [ + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -2099,18 +2466,30 @@ class LocalAlbumEntity extends Table LocalAlbumEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LocalAlbumEntityData( - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}name'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - backupSelection: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}backup_selection'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + backupSelection: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}backup_selection'], + )!, isIosSharedAlbum: attachedDatabase.typeMapping.read( - DriftSqlType.bool, data['${effectivePrefix}is_ios_shared_album'])!, - marker_: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}marker']), + DriftSqlType.bool, + data['${effectivePrefix}is_ios_shared_album'], + )!, + marker_: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), ); } @@ -2133,13 +2512,14 @@ class LocalAlbumEntityData extends DataClass final int backupSelection; final bool isIosSharedAlbum; final bool? marker_; - const LocalAlbumEntityData( - {required this.id, - required this.name, - required this.updatedAt, - required this.backupSelection, - required this.isIosSharedAlbum, - this.marker_}); + const LocalAlbumEntityData({ + required this.id, + required this.name, + required this.updatedAt, + required this.backupSelection, + required this.isIosSharedAlbum, + this.marker_, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -2154,8 +2534,10 @@ class LocalAlbumEntityData extends DataClass return map; } - factory LocalAlbumEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory LocalAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LocalAlbumEntityData( id: serializer.fromJson(json['id']), @@ -2179,21 +2561,21 @@ class LocalAlbumEntityData extends DataClass }; } - LocalAlbumEntityData copyWith( - {String? id, - String? name, - DateTime? updatedAt, - int? backupSelection, - bool? isIosSharedAlbum, - Value marker_ = const Value.absent()}) => - LocalAlbumEntityData( - id: id ?? this.id, - name: name ?? this.name, - updatedAt: updatedAt ?? this.updatedAt, - backupSelection: backupSelection ?? this.backupSelection, - isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, - marker_: marker_.present ? marker_.value : this.marker_, - ); + LocalAlbumEntityData copyWith({ + String? id, + String? name, + DateTime? updatedAt, + int? backupSelection, + bool? isIosSharedAlbum, + Value marker_ = const Value.absent(), + }) => LocalAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_.present ? marker_.value : this.marker_, + ); LocalAlbumEntityData copyWithCompanion(LocalAlbumEntityCompanion data) { return LocalAlbumEntityData( id: data.id.present ? data.id.value : this.id, @@ -2224,7 +2606,13 @@ class LocalAlbumEntityData extends DataClass @override int get hashCode => Object.hash( - id, name, updatedAt, backupSelection, isIosSharedAlbum, marker_); + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -2259,9 +2647,9 @@ class LocalAlbumEntityCompanion extends UpdateCompanion { required int backupSelection, this.isIosSharedAlbum = const Value.absent(), this.marker_ = const Value.absent(), - }) : id = Value(id), - name = Value(name), - backupSelection = Value(backupSelection); + }) : id = Value(id), + name = Value(name), + backupSelection = Value(backupSelection); static Insertable custom({ Expression? id, Expression? name, @@ -2280,13 +2668,14 @@ class LocalAlbumEntityCompanion extends UpdateCompanion { }); } - LocalAlbumEntityCompanion copyWith( - {Value? id, - Value? name, - Value? updatedAt, - Value? backupSelection, - Value? isIosSharedAlbum, - Value? marker_}) { + LocalAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? updatedAt, + Value? backupSelection, + Value? isIosSharedAlbum, + Value? marker_, + }) { return LocalAlbumEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -2342,17 +2731,25 @@ class LocalAlbumAssetEntity extends Table final String? _alias; LocalAlbumAssetEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn assetId = GeneratedColumn( - 'asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES local_asset_entity (id) ON DELETE CASCADE')); + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn albumId = GeneratedColumn( - 'album_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES local_album_entity (id) ON DELETE CASCADE')); + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, albumId]; @override @@ -2363,14 +2760,20 @@ class LocalAlbumAssetEntity extends Table @override Set get $primaryKey => {assetId, albumId}; @override - LocalAlbumAssetEntityData map(Map data, - {String? tablePrefix}) { + LocalAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return LocalAlbumAssetEntityData( - assetId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - albumId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, ); } @@ -2389,8 +2792,10 @@ class LocalAlbumAssetEntityData extends DataClass implements Insertable { final String assetId; final String albumId; - const LocalAlbumAssetEntityData( - {required this.assetId, required this.albumId}); + const LocalAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -2399,8 +2804,10 @@ class LocalAlbumAssetEntityData extends DataClass return map; } - factory LocalAlbumAssetEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory LocalAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return LocalAlbumAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -2422,7 +2829,8 @@ class LocalAlbumAssetEntityData extends DataClass albumId: albumId ?? this.albumId, ); LocalAlbumAssetEntityData copyWithCompanion( - LocalAlbumAssetEntityCompanion data) { + LocalAlbumAssetEntityCompanion data, + ) { return LocalAlbumAssetEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, albumId: data.albumId.present ? data.albumId.value : this.albumId, @@ -2459,8 +2867,8 @@ class LocalAlbumAssetEntityCompanion LocalAlbumAssetEntityCompanion.insert({ required String assetId, required String albumId, - }) : assetId = Value(assetId), - albumId = Value(albumId); + }) : assetId = Value(assetId), + albumId = Value(albumId); static Insertable custom({ Expression? assetId, Expression? albumId, @@ -2471,8 +2879,10 @@ class LocalAlbumAssetEntityCompanion }); } - LocalAlbumAssetEntityCompanion copyWith( - {Value? assetId, Value? albumId}) { + LocalAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { return LocalAlbumAssetEntityCompanion( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, @@ -2508,99 +2918,188 @@ class RemoteExifEntity extends Table final String? _alias; RemoteExifEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn assetId = GeneratedColumn( - 'asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn city = GeneratedColumn( - 'city', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'city', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn state = GeneratedColumn( - 'state', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn country = GeneratedColumn( - 'country', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'country', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn dateTimeOriginal = - GeneratedColumn('date_time_original', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); late final GeneratedColumn description = GeneratedColumn( - 'description', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'description', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn height = GeneratedColumn( - 'height', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn width = GeneratedColumn( - 'width', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn exposureTime = GeneratedColumn( - 'exposure_time', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'exposure_time', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn fNumber = GeneratedColumn( - 'f_number', aliasedName, true, - type: DriftSqlType.double, requiredDuringInsert: false); + 'f_number', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); late final GeneratedColumn fileSize = GeneratedColumn( - 'file_size', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'file_size', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn focalLength = GeneratedColumn( - 'focal_length', aliasedName, true, - type: DriftSqlType.double, requiredDuringInsert: false); + 'focal_length', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); late final GeneratedColumn latitude = GeneratedColumn( - 'latitude', aliasedName, true, - type: DriftSqlType.double, requiredDuringInsert: false); + 'latitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); late final GeneratedColumn longitude = GeneratedColumn( - 'longitude', aliasedName, true, - type: DriftSqlType.double, requiredDuringInsert: false); + 'longitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); late final GeneratedColumn iso = GeneratedColumn( - 'iso', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'iso', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn make = GeneratedColumn( - 'make', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'make', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn model = GeneratedColumn( - 'model', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'model', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn lens = GeneratedColumn( - 'lens', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'lens', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn orientation = GeneratedColumn( - 'orientation', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'orientation', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn timeZone = GeneratedColumn( - 'time_zone', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'time_zone', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); late final GeneratedColumn rating = GeneratedColumn( - 'rating', aliasedName, true, - type: DriftSqlType.int, requiredDuringInsert: false); + 'rating', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); late final GeneratedColumn projectionType = GeneratedColumn( - 'projection_type', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); + 'projection_type', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); @override List get $columns => [ - assetId, - city, - state, - country, - dateTimeOriginal, - description, - height, - width, - exposureTime, - fNumber, - fileSize, - focalLength, - latitude, - longitude, - iso, - make, - model, - lens, - orientation, - timeZone, - rating, - projectionType - ]; + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -2612,50 +3111,94 @@ class RemoteExifEntity extends Table RemoteExifEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteExifEntityData( - assetId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - city: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}city']), - state: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}state']), - country: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}country']), + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + city: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}city'], + ), + state: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}state'], + ), + country: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}country'], + ), dateTimeOriginal: attachedDatabase.typeMapping.read( - DriftSqlType.dateTime, data['${effectivePrefix}date_time_original']), - description: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}description']), - height: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}height']), - width: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}width']), - exposureTime: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}exposure_time']), - fNumber: attachedDatabase.typeMapping - .read(DriftSqlType.double, data['${effectivePrefix}f_number']), - fileSize: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}file_size']), - focalLength: attachedDatabase.typeMapping - .read(DriftSqlType.double, data['${effectivePrefix}focal_length']), - latitude: attachedDatabase.typeMapping - .read(DriftSqlType.double, data['${effectivePrefix}latitude']), - longitude: attachedDatabase.typeMapping - .read(DriftSqlType.double, data['${effectivePrefix}longitude']), - iso: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}iso']), - make: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}make']), - model: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}model']), - lens: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}lens']), - orientation: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}orientation']), - timeZone: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}time_zone']), - rating: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}rating']), - projectionType: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}projection_type']), + DriftSqlType.dateTime, + data['${effectivePrefix}date_time_original'], + ), + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + exposureTime: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}exposure_time'], + ), + fNumber: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}f_number'], + ), + fileSize: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}file_size'], + ), + focalLength: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}focal_length'], + ), + latitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}latitude'], + ), + longitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}longitude'], + ), + iso: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}iso'], + ), + make: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}make'], + ), + model: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}model'], + ), + lens: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}lens'], + ), + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}orientation'], + ), + timeZone: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}time_zone'], + ), + rating: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}rating'], + ), + projectionType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}projection_type'], + ), ); } @@ -2694,29 +3237,30 @@ class RemoteExifEntityData extends DataClass final String? timeZone; final int? rating; final String? projectionType; - const RemoteExifEntityData( - {required this.assetId, - this.city, - this.state, - this.country, - this.dateTimeOriginal, - this.description, - this.height, - this.width, - this.exposureTime, - this.fNumber, - this.fileSize, - this.focalLength, - this.latitude, - this.longitude, - this.iso, - this.make, - this.model, - this.lens, - this.orientation, - this.timeZone, - this.rating, - this.projectionType}); + const RemoteExifEntityData({ + required this.assetId, + this.city, + this.state, + this.country, + this.dateTimeOriginal, + this.description, + this.height, + this.width, + this.exposureTime, + this.fNumber, + this.fileSize, + this.focalLength, + this.latitude, + this.longitude, + this.iso, + this.make, + this.model, + this.lens, + this.orientation, + this.timeZone, + this.rating, + this.projectionType, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -2787,16 +3331,19 @@ class RemoteExifEntityData extends DataClass return map; } - factory RemoteExifEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory RemoteExifEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteExifEntityData( assetId: serializer.fromJson(json['assetId']), city: serializer.fromJson(json['city']), state: serializer.fromJson(json['state']), country: serializer.fromJson(json['country']), - dateTimeOriginal: - serializer.fromJson(json['dateTimeOriginal']), + dateTimeOriginal: serializer.fromJson( + json['dateTimeOriginal'], + ), description: serializer.fromJson(json['description']), height: serializer.fromJson(json['height']), width: serializer.fromJson(json['width']), @@ -2845,57 +3392,57 @@ class RemoteExifEntityData extends DataClass }; } - RemoteExifEntityData copyWith( - {String? assetId, - Value city = const Value.absent(), - Value state = const Value.absent(), - Value country = const Value.absent(), - Value dateTimeOriginal = const Value.absent(), - Value description = const Value.absent(), - Value height = const Value.absent(), - Value width = const Value.absent(), - Value exposureTime = const Value.absent(), - Value fNumber = const Value.absent(), - Value fileSize = const Value.absent(), - Value focalLength = const Value.absent(), - Value latitude = const Value.absent(), - Value longitude = const Value.absent(), - Value iso = const Value.absent(), - Value make = const Value.absent(), - Value model = const Value.absent(), - Value lens = const Value.absent(), - Value orientation = const Value.absent(), - Value timeZone = const Value.absent(), - Value rating = const Value.absent(), - Value projectionType = const Value.absent()}) => - RemoteExifEntityData( - assetId: assetId ?? this.assetId, - city: city.present ? city.value : this.city, - state: state.present ? state.value : this.state, - country: country.present ? country.value : this.country, - dateTimeOriginal: dateTimeOriginal.present - ? dateTimeOriginal.value - : this.dateTimeOriginal, - description: description.present ? description.value : this.description, - height: height.present ? height.value : this.height, - width: width.present ? width.value : this.width, - exposureTime: - exposureTime.present ? exposureTime.value : this.exposureTime, - fNumber: fNumber.present ? fNumber.value : this.fNumber, - fileSize: fileSize.present ? fileSize.value : this.fileSize, - focalLength: focalLength.present ? focalLength.value : this.focalLength, - latitude: latitude.present ? latitude.value : this.latitude, - longitude: longitude.present ? longitude.value : this.longitude, - iso: iso.present ? iso.value : this.iso, - make: make.present ? make.value : this.make, - model: model.present ? model.value : this.model, - lens: lens.present ? lens.value : this.lens, - orientation: orientation.present ? orientation.value : this.orientation, - timeZone: timeZone.present ? timeZone.value : this.timeZone, - rating: rating.present ? rating.value : this.rating, - projectionType: - projectionType.present ? projectionType.value : this.projectionType, - ); + RemoteExifEntityData copyWith({ + String? assetId, + Value city = const Value.absent(), + Value state = const Value.absent(), + Value country = const Value.absent(), + Value dateTimeOriginal = const Value.absent(), + Value description = const Value.absent(), + Value height = const Value.absent(), + Value width = const Value.absent(), + Value exposureTime = const Value.absent(), + Value fNumber = const Value.absent(), + Value fileSize = const Value.absent(), + Value focalLength = const Value.absent(), + Value latitude = const Value.absent(), + Value longitude = const Value.absent(), + Value iso = const Value.absent(), + Value make = const Value.absent(), + Value model = const Value.absent(), + Value lens = const Value.absent(), + Value orientation = const Value.absent(), + Value timeZone = const Value.absent(), + Value rating = const Value.absent(), + Value projectionType = const Value.absent(), + }) => RemoteExifEntityData( + assetId: assetId ?? this.assetId, + city: city.present ? city.value : this.city, + state: state.present ? state.value : this.state, + country: country.present ? country.value : this.country, + dateTimeOriginal: dateTimeOriginal.present + ? dateTimeOriginal.value + : this.dateTimeOriginal, + description: description.present ? description.value : this.description, + height: height.present ? height.value : this.height, + width: width.present ? width.value : this.width, + exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, + fNumber: fNumber.present ? fNumber.value : this.fNumber, + fileSize: fileSize.present ? fileSize.value : this.fileSize, + focalLength: focalLength.present ? focalLength.value : this.focalLength, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + iso: iso.present ? iso.value : this.iso, + make: make.present ? make.value : this.make, + model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, + orientation: orientation.present ? orientation.value : this.orientation, + timeZone: timeZone.present ? timeZone.value : this.timeZone, + rating: rating.present ? rating.value : this.rating, + projectionType: projectionType.present + ? projectionType.value + : this.projectionType, + ); RemoteExifEntityData copyWithCompanion(RemoteExifEntityCompanion data) { return RemoteExifEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, @@ -2905,8 +3452,9 @@ class RemoteExifEntityData extends DataClass dateTimeOriginal: data.dateTimeOriginal.present ? data.dateTimeOriginal.value : this.dateTimeOriginal, - description: - data.description.present ? data.description.value : this.description, + description: data.description.present + ? data.description.value + : this.description, height: data.height.present ? data.height.value : this.height, width: data.width.present ? data.width.value : this.width, exposureTime: data.exposureTime.present @@ -2914,16 +3462,18 @@ class RemoteExifEntityData extends DataClass : this.exposureTime, fNumber: data.fNumber.present ? data.fNumber.value : this.fNumber, fileSize: data.fileSize.present ? data.fileSize.value : this.fileSize, - focalLength: - data.focalLength.present ? data.focalLength.value : this.focalLength, + focalLength: data.focalLength.present + ? data.focalLength.value + : this.focalLength, latitude: data.latitude.present ? data.latitude.value : this.latitude, longitude: data.longitude.present ? data.longitude.value : this.longitude, iso: data.iso.present ? data.iso.value : this.iso, make: data.make.present ? data.make.value : this.make, model: data.model.present ? data.model.value : this.model, lens: data.lens.present ? data.lens.value : this.lens, - orientation: - data.orientation.present ? data.orientation.value : this.orientation, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, rating: data.rating.present ? data.rating.value : this.rating, projectionType: data.projectionType.present @@ -2963,29 +3513,29 @@ class RemoteExifEntityData extends DataClass @override int get hashCode => Object.hashAll([ - assetId, - city, - state, - country, - dateTimeOriginal, - description, - height, - width, - exposureTime, - fNumber, - fileSize, - focalLength, - latitude, - longitude, - iso, - make, - model, - lens, - orientation, - timeZone, - rating, - projectionType - ]); + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]); @override bool operator ==(Object other) => identical(this, other) || @@ -3135,29 +3685,30 @@ class RemoteExifEntityCompanion extends UpdateCompanion { }); } - RemoteExifEntityCompanion copyWith( - {Value? assetId, - Value? city, - Value? state, - Value? country, - Value? dateTimeOriginal, - Value? description, - Value? height, - Value? width, - Value? exposureTime, - Value? fNumber, - Value? fileSize, - Value? focalLength, - Value? latitude, - Value? longitude, - Value? iso, - Value? make, - Value? model, - Value? lens, - Value? orientation, - Value? timeZone, - Value? rating, - Value? projectionType}) { + RemoteExifEntityCompanion copyWith({ + Value? assetId, + Value? city, + Value? state, + Value? country, + Value? dateTimeOriginal, + Value? description, + Value? height, + Value? width, + Value? exposureTime, + Value? fNumber, + Value? fileSize, + Value? focalLength, + Value? latitude, + Value? longitude, + Value? iso, + Value? make, + Value? model, + Value? lens, + Value? orientation, + Value? timeZone, + Value? rating, + Value? projectionType, + }) { return RemoteExifEntityCompanion( assetId: assetId ?? this.assetId, city: city ?? this.city, @@ -3293,60 +3844,93 @@ class RemoteAlbumEntity extends Table final String? _alias; RemoteAlbumEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn name = GeneratedColumn( - 'name', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn description = GeneratedColumn( - 'description', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: false, - defaultValue: const CustomExpression('\'\'')); + 'description', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const CustomExpression('\'\''), + ); late final GeneratedColumn createdAt = GeneratedColumn( - 'created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn ownerId = GeneratedColumn( - 'owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn thumbnailAssetId = GeneratedColumn( - 'thumbnail_asset_id', aliasedName, true, - type: DriftSqlType.string, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL')); + 'thumbnail_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); late final GeneratedColumn isActivityEnabled = GeneratedColumn( - 'is_activity_enabled', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'CHECK ("is_activity_enabled" IN (0, 1))'), - defaultValue: const CustomExpression('1')); + 'is_activity_enabled', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const CustomExpression('1'), + ); late final GeneratedColumn order = GeneratedColumn( - 'order', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'order', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); @override List get $columns => [ - id, - name, - description, - createdAt, - updatedAt, - ownerId, - thumbnailAssetId, - isActivityEnabled, - order - ]; + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -3358,24 +3942,42 @@ class RemoteAlbumEntity extends Table RemoteAlbumEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAlbumEntityData( - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - name: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}name'])!, - description: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}description'])!, - createdAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - ownerId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, thumbnailAssetId: attachedDatabase.typeMapping.read( - DriftSqlType.string, data['${effectivePrefix}thumbnail_asset_id']), + DriftSqlType.string, + data['${effectivePrefix}thumbnail_asset_id'], + ), isActivityEnabled: attachedDatabase.typeMapping.read( - DriftSqlType.bool, data['${effectivePrefix}is_activity_enabled'])!, - order: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}order'])!, + DriftSqlType.bool, + data['${effectivePrefix}is_activity_enabled'], + )!, + order: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}order'], + )!, ); } @@ -3401,16 +4003,17 @@ class RemoteAlbumEntityData extends DataClass final String? thumbnailAssetId; final bool isActivityEnabled; final int order; - const RemoteAlbumEntityData( - {required this.id, - required this.name, - required this.description, - required this.createdAt, - required this.updatedAt, - required this.ownerId, - this.thumbnailAssetId, - required this.isActivityEnabled, - required this.order}); + const RemoteAlbumEntityData({ + required this.id, + required this.name, + required this.description, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + this.thumbnailAssetId, + required this.isActivityEnabled, + required this.order, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -3428,8 +4031,10 @@ class RemoteAlbumEntityData extends DataClass return map; } - factory RemoteAlbumEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory RemoteAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAlbumEntityData( id: serializer.fromJson(json['id']), @@ -3459,35 +4064,36 @@ class RemoteAlbumEntityData extends DataClass }; } - RemoteAlbumEntityData copyWith( - {String? id, - String? name, - String? description, - DateTime? createdAt, - DateTime? updatedAt, - String? ownerId, - Value thumbnailAssetId = const Value.absent(), - bool? isActivityEnabled, - int? order}) => - RemoteAlbumEntityData( - id: id ?? this.id, - name: name ?? this.name, - description: description ?? this.description, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - ownerId: ownerId ?? this.ownerId, - thumbnailAssetId: thumbnailAssetId.present - ? thumbnailAssetId.value - : this.thumbnailAssetId, - isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, - order: order ?? this.order, - ); + RemoteAlbumEntityData copyWith({ + String? id, + String? name, + String? description, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + Value thumbnailAssetId = const Value.absent(), + bool? isActivityEnabled, + int? order, + }) => RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId.present + ? thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); RemoteAlbumEntityData copyWithCompanion(RemoteAlbumEntityCompanion data) { return RemoteAlbumEntityData( id: data.id.present ? data.id.value : this.id, name: data.name.present ? data.name.value : this.name, - description: - data.description.present ? data.description.value : this.description, + description: data.description.present + ? data.description.value + : this.description, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, @@ -3518,8 +4124,17 @@ class RemoteAlbumEntityData extends DataClass } @override - int get hashCode => Object.hash(id, name, description, createdAt, updatedAt, - ownerId, thumbnailAssetId, isActivityEnabled, order); + int get hashCode => Object.hash( + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -3567,10 +4182,10 @@ class RemoteAlbumEntityCompanion this.thumbnailAssetId = const Value.absent(), this.isActivityEnabled = const Value.absent(), required int order, - }) : id = Value(id), - name = Value(name), - ownerId = Value(ownerId), - order = Value(order); + }) : id = Value(id), + name = Value(name), + ownerId = Value(ownerId), + order = Value(order); static Insertable custom({ Expression? id, Expression? name, @@ -3595,16 +4210,17 @@ class RemoteAlbumEntityCompanion }); } - RemoteAlbumEntityCompanion copyWith( - {Value? id, - Value? name, - Value? description, - Value? createdAt, - Value? updatedAt, - Value? ownerId, - Value? thumbnailAssetId, - Value? isActivityEnabled, - Value? order}) { + RemoteAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? description, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? thumbnailAssetId, + Value? isActivityEnabled, + Value? order, + }) { return RemoteAlbumEntityCompanion( id: id ?? this.id, name: name ?? this.name, @@ -3675,17 +4291,25 @@ class RemoteAlbumAssetEntity extends Table final String? _alias; RemoteAlbumAssetEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn assetId = GeneratedColumn( - 'asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn albumId = GeneratedColumn( - 'album_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_album_entity (id) ON DELETE CASCADE')); + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, albumId]; @override @@ -3696,14 +4320,20 @@ class RemoteAlbumAssetEntity extends Table @override Set get $primaryKey => {assetId, albumId}; @override - RemoteAlbumAssetEntityData map(Map data, - {String? tablePrefix}) { + RemoteAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAlbumAssetEntityData( - assetId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - albumId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, ); } @@ -3722,8 +4352,10 @@ class RemoteAlbumAssetEntityData extends DataClass implements Insertable { final String assetId; final String albumId; - const RemoteAlbumAssetEntityData( - {required this.assetId, required this.albumId}); + const RemoteAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -3732,8 +4364,10 @@ class RemoteAlbumAssetEntityData extends DataClass return map; } - factory RemoteAlbumAssetEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory RemoteAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAlbumAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -3755,7 +4389,8 @@ class RemoteAlbumAssetEntityData extends DataClass albumId: albumId ?? this.albumId, ); RemoteAlbumAssetEntityData copyWithCompanion( - RemoteAlbumAssetEntityCompanion data) { + RemoteAlbumAssetEntityCompanion data, + ) { return RemoteAlbumAssetEntityData( assetId: data.assetId.present ? data.assetId.value : this.assetId, albumId: data.albumId.present ? data.albumId.value : this.albumId, @@ -3792,8 +4427,8 @@ class RemoteAlbumAssetEntityCompanion RemoteAlbumAssetEntityCompanion.insert({ required String assetId, required String albumId, - }) : assetId = Value(assetId), - albumId = Value(albumId); + }) : assetId = Value(assetId), + albumId = Value(albumId); static Insertable custom({ Expression? assetId, Expression? albumId, @@ -3804,8 +4439,10 @@ class RemoteAlbumAssetEntityCompanion }); } - RemoteAlbumAssetEntityCompanion copyWith( - {Value? assetId, Value? albumId}) { + RemoteAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { return RemoteAlbumAssetEntityCompanion( assetId: assetId ?? this.assetId, albumId: albumId ?? this.albumId, @@ -3841,20 +4478,32 @@ class RemoteAlbumUserEntity extends Table final String? _alias; RemoteAlbumUserEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn albumId = GeneratedColumn( - 'album_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_album_entity (id) ON DELETE CASCADE')); + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn userId = GeneratedColumn( - 'user_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn role = GeneratedColumn( - 'role', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'role', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); @override List get $columns => [albumId, userId, role]; @override @@ -3865,16 +4514,24 @@ class RemoteAlbumUserEntity extends Table @override Set get $primaryKey => {albumId, userId}; @override - RemoteAlbumUserEntityData map(Map data, - {String? tablePrefix}) { + RemoteAlbumUserEntityData map( + Map data, { + String? tablePrefix, + }) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return RemoteAlbumUserEntityData( - albumId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}album_id'])!, - userId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}user_id'])!, - role: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}role'])!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + role: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}role'], + )!, ); } @@ -3894,8 +4551,11 @@ class RemoteAlbumUserEntityData extends DataClass final String albumId; final String userId; final int role; - const RemoteAlbumUserEntityData( - {required this.albumId, required this.userId, required this.role}); + const RemoteAlbumUserEntityData({ + required this.albumId, + required this.userId, + required this.role, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -3905,8 +4565,10 @@ class RemoteAlbumUserEntityData extends DataClass return map; } - factory RemoteAlbumUserEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory RemoteAlbumUserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return RemoteAlbumUserEntityData( albumId: serializer.fromJson(json['albumId']), @@ -3924,15 +4586,18 @@ class RemoteAlbumUserEntityData extends DataClass }; } - RemoteAlbumUserEntityData copyWith( - {String? albumId, String? userId, int? role}) => - RemoteAlbumUserEntityData( - albumId: albumId ?? this.albumId, - userId: userId ?? this.userId, - role: role ?? this.role, - ); + RemoteAlbumUserEntityData copyWith({ + String? albumId, + String? userId, + int? role, + }) => RemoteAlbumUserEntityData( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); RemoteAlbumUserEntityData copyWithCompanion( - RemoteAlbumUserEntityCompanion data) { + RemoteAlbumUserEntityCompanion data, + ) { return RemoteAlbumUserEntityData( albumId: data.albumId.present ? data.albumId.value : this.albumId, userId: data.userId.present ? data.userId.value : this.userId, @@ -3975,9 +4640,9 @@ class RemoteAlbumUserEntityCompanion required String albumId, required String userId, required int role, - }) : albumId = Value(albumId), - userId = Value(userId), - role = Value(role); + }) : albumId = Value(albumId), + userId = Value(userId), + role = Value(role); static Insertable custom({ Expression? albumId, Expression? userId, @@ -3990,8 +4655,11 @@ class RemoteAlbumUserEntityCompanion }); } - RemoteAlbumUserEntityCompanion copyWith( - {Value? albumId, Value? userId, Value? role}) { + RemoteAlbumUserEntityCompanion copyWith({ + Value? albumId, + Value? userId, + Value? role, + }) { return RemoteAlbumUserEntityCompanion( albumId: albumId ?? this.albumId, userId: userId ?? this.userId, @@ -4032,67 +4700,113 @@ class MemoryEntity extends Table final String? _alias; MemoryEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn id = GeneratedColumn( - 'id', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn createdAt = GeneratedColumn( - 'created_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn updatedAt = GeneratedColumn( - 'updated_at', aliasedName, false, - type: DriftSqlType.dateTime, - requiredDuringInsert: false, - defaultValue: const CustomExpression('CURRENT_TIMESTAMP')); + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); late final GeneratedColumn deletedAt = GeneratedColumn( - 'deleted_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); late final GeneratedColumn ownerId = GeneratedColumn( - 'owner_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES user_entity (id) ON DELETE CASCADE')); + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn type = GeneratedColumn( - 'type', aliasedName, false, - type: DriftSqlType.int, requiredDuringInsert: true); + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); late final GeneratedColumn data = GeneratedColumn( - 'data', aliasedName, false, - type: DriftSqlType.string, requiredDuringInsert: true); + 'data', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); late final GeneratedColumn isSaved = GeneratedColumn( - 'is_saved', aliasedName, false, - type: DriftSqlType.bool, - requiredDuringInsert: false, - defaultConstraints: - GeneratedColumn.constraintIsAlways('CHECK ("is_saved" IN (0, 1))'), - defaultValue: const CustomExpression('0')); + 'is_saved', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); late final GeneratedColumn memoryAt = GeneratedColumn( - 'memory_at', aliasedName, false, - type: DriftSqlType.dateTime, requiredDuringInsert: true); + 'memory_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: true, + ); late final GeneratedColumn seenAt = GeneratedColumn( - 'seen_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + 'seen_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); late final GeneratedColumn showAt = GeneratedColumn( - 'show_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + 'show_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); late final GeneratedColumn hideAt = GeneratedColumn( - 'hide_at', aliasedName, true, - type: DriftSqlType.dateTime, requiredDuringInsert: false); + 'hide_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); @override List get $columns => [ - id, - createdAt, - updatedAt, - deletedAt, - ownerId, - type, - data, - isSaved, - memoryAt, - seenAt, - showAt, - hideAt - ]; + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -4104,30 +4818,54 @@ class MemoryEntity extends Table MemoryEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return MemoryEntityData( - id: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}id'])!, - createdAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!, - updatedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!, - deletedAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}deleted_at']), - ownerId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}owner_id'])!, - type: attachedDatabase.typeMapping - .read(DriftSqlType.int, data['${effectivePrefix}type'])!, - data: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}data'])!, - isSaved: attachedDatabase.typeMapping - .read(DriftSqlType.bool, data['${effectivePrefix}is_saved'])!, - memoryAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}memory_at'])!, - seenAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}seen_at']), - showAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}show_at']), - hideAt: attachedDatabase.typeMapping - .read(DriftSqlType.dateTime, data['${effectivePrefix}hide_at']), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + data: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}data'], + )!, + isSaved: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_saved'], + )!, + memoryAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}memory_at'], + )!, + seenAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}seen_at'], + ), + showAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}show_at'], + ), + hideAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}hide_at'], + ), ); } @@ -4156,19 +4894,20 @@ class MemoryEntityData extends DataClass final DateTime? seenAt; final DateTime? showAt; final DateTime? hideAt; - const MemoryEntityData( - {required this.id, - required this.createdAt, - required this.updatedAt, - this.deletedAt, - required this.ownerId, - required this.type, - required this.data, - required this.isSaved, - required this.memoryAt, - this.seenAt, - this.showAt, - this.hideAt}); + const MemoryEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; @@ -4195,8 +4934,10 @@ class MemoryEntityData extends DataClass return map; } - factory MemoryEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory MemoryEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return MemoryEntityData( id: serializer.fromJson(json['id']), @@ -4232,33 +4973,33 @@ class MemoryEntityData extends DataClass }; } - MemoryEntityData copyWith( - {String? id, - DateTime? createdAt, - DateTime? updatedAt, - Value deletedAt = const Value.absent(), - String? ownerId, - int? type, - String? data, - bool? isSaved, - DateTime? memoryAt, - Value seenAt = const Value.absent(), - Value showAt = const Value.absent(), - Value hideAt = const Value.absent()}) => - MemoryEntityData( - id: id ?? this.id, - createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt ?? this.updatedAt, - deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, - ownerId: ownerId ?? this.ownerId, - type: type ?? this.type, - data: data ?? this.data, - isSaved: isSaved ?? this.isSaved, - memoryAt: memoryAt ?? this.memoryAt, - seenAt: seenAt.present ? seenAt.value : this.seenAt, - showAt: showAt.present ? showAt.value : this.showAt, - hideAt: hideAt.present ? hideAt.value : this.hideAt, - ); + MemoryEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + Value deletedAt = const Value.absent(), + String? ownerId, + int? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + Value seenAt = const Value.absent(), + Value showAt = const Value.absent(), + Value hideAt = const Value.absent(), + }) => MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); MemoryEntityData copyWithCompanion(MemoryEntityCompanion data) { return MemoryEntityData( id: data.id.present ? data.id.value : this.id, @@ -4296,8 +5037,20 @@ class MemoryEntityData extends DataClass } @override - int get hashCode => Object.hash(id, createdAt, updatedAt, deletedAt, ownerId, - type, data, isSaved, memoryAt, seenAt, showAt, hideAt); + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ); @override bool operator ==(Object other) => identical(this, other) || @@ -4356,11 +5109,11 @@ class MemoryEntityCompanion extends UpdateCompanion { this.seenAt = const Value.absent(), this.showAt = const Value.absent(), this.hideAt = const Value.absent(), - }) : id = Value(id), - ownerId = Value(ownerId), - type = Value(type), - data = Value(data), - memoryAt = Value(memoryAt); + }) : id = Value(id), + ownerId = Value(ownerId), + type = Value(type), + data = Value(data), + memoryAt = Value(memoryAt); static Insertable custom({ Expression? id, Expression? createdAt, @@ -4391,19 +5144,20 @@ class MemoryEntityCompanion extends UpdateCompanion { }); } - MemoryEntityCompanion copyWith( - {Value? id, - Value? createdAt, - Value? updatedAt, - Value? deletedAt, - Value? ownerId, - Value? type, - Value? data, - Value? isSaved, - Value? memoryAt, - Value? seenAt, - Value? showAt, - Value? hideAt}) { + MemoryEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? deletedAt, + Value? ownerId, + Value? type, + Value? data, + Value? isSaved, + Value? memoryAt, + Value? seenAt, + Value? showAt, + Value? hideAt, + }) { return MemoryEntityCompanion( id: id ?? this.id, createdAt: createdAt ?? this.createdAt, @@ -4489,17 +5243,25 @@ class MemoryAssetEntity extends Table final String? _alias; MemoryAssetEntity(this.attachedDatabase, [this._alias]); late final GeneratedColumn assetId = GeneratedColumn( - 'asset_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE')); + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); late final GeneratedColumn memoryId = GeneratedColumn( - 'memory_id', aliasedName, false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES memory_entity (id) ON DELETE CASCADE')); + 'memory_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); @override List get $columns => [assetId, memoryId]; @override @@ -4513,10 +5275,14 @@ class MemoryAssetEntity extends Table MemoryAssetEntityData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; return MemoryAssetEntityData( - assetId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}asset_id'])!, - memoryId: attachedDatabase.typeMapping - .read(DriftSqlType.string, data['${effectivePrefix}memory_id'])!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + memoryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}memory_id'], + )!, ); } @@ -4544,8 +5310,10 @@ class MemoryAssetEntityData extends DataClass return map; } - factory MemoryAssetEntityData.fromJson(Map json, - {ValueSerializer? serializer}) { + factory MemoryAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { serializer ??= driftRuntimeOptions.defaultSerializer; return MemoryAssetEntityData( assetId: serializer.fromJson(json['assetId']), @@ -4603,8 +5371,8 @@ class MemoryAssetEntityCompanion MemoryAssetEntityCompanion.insert({ required String assetId, required String memoryId, - }) : assetId = Value(assetId), - memoryId = Value(memoryId); + }) : assetId = Value(assetId), + memoryId = Value(memoryId); static Insertable custom({ Expression? assetId, Expression? memoryId, @@ -4615,8 +5383,10 @@ class MemoryAssetEntityCompanion }); } - MemoryAssetEntityCompanion copyWith( - {Value? assetId, Value? memoryId}) { + MemoryAssetEntityCompanion copyWith({ + Value? assetId, + Value? memoryId, + }) { return MemoryAssetEntityCompanion( assetId: assetId ?? this.assetId, memoryId: memoryId ?? this.memoryId, @@ -4645,19 +5415,540 @@ class MemoryAssetEntityCompanion } } +class PersonEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PersonEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn faceAssetId = GeneratedColumn( + 'face_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn thumbnailPath = GeneratedColumn( + 'thumbnail_path', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); + late final GeneratedColumn isHidden = GeneratedColumn( + 'is_hidden', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); + late final GeneratedColumn color = GeneratedColumn( + 'color', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn birthDate = GeneratedColumn( + 'birth_date', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + thumbnailPath, + isFavorite, + isHidden, + color, + birthDate, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'person_entity'; + @override + Set get $primaryKey => {id}; + @override + PersonEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PersonEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + faceAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}face_asset_id'], + ), + thumbnailPath: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_path'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + isHidden: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_hidden'], + )!, + color: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}color'], + ), + birthDate: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}birth_date'], + ), + ); + } + + @override + PersonEntity createAlias(String alias) { + return PersonEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PersonEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String name; + final String? faceAssetId; + final String thumbnailPath; + final bool isFavorite; + final bool isHidden; + final String? color; + final DateTime? birthDate; + const PersonEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.thumbnailPath, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['name'] = Variable(name); + if (!nullToAbsent || faceAssetId != null) { + map['face_asset_id'] = Variable(faceAssetId); + } + map['thumbnail_path'] = Variable(thumbnailPath); + map['is_favorite'] = Variable(isFavorite); + map['is_hidden'] = Variable(isHidden); + if (!nullToAbsent || color != null) { + map['color'] = Variable(color); + } + if (!nullToAbsent || birthDate != null) { + map['birth_date'] = Variable(birthDate); + } + return map; + } + + factory PersonEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PersonEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + name: serializer.fromJson(json['name']), + faceAssetId: serializer.fromJson(json['faceAssetId']), + thumbnailPath: serializer.fromJson(json['thumbnailPath']), + isFavorite: serializer.fromJson(json['isFavorite']), + isHidden: serializer.fromJson(json['isHidden']), + color: serializer.fromJson(json['color']), + birthDate: serializer.fromJson(json['birthDate']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'name': serializer.toJson(name), + 'faceAssetId': serializer.toJson(faceAssetId), + 'thumbnailPath': serializer.toJson(thumbnailPath), + 'isFavorite': serializer.toJson(isFavorite), + 'isHidden': serializer.toJson(isHidden), + 'color': serializer.toJson(color), + 'birthDate': serializer.toJson(birthDate), + }; + } + + PersonEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + Value faceAssetId = const Value.absent(), + String? thumbnailPath, + bool? isFavorite, + bool? isHidden, + Value color = const Value.absent(), + Value birthDate = const Value.absent(), + }) => PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + thumbnailPath: thumbnailPath ?? this.thumbnailPath, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); + PersonEntityData copyWithCompanion(PersonEntityCompanion data) { + return PersonEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + name: data.name.present ? data.name.value : this.name, + faceAssetId: data.faceAssetId.present + ? data.faceAssetId.value + : this.faceAssetId, + thumbnailPath: data.thumbnailPath.present + ? data.thumbnailPath.value + : this.thumbnailPath, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, + color: data.color.present ? data.color.value : this.color, + birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, + ); + } + + @override + String toString() { + return (StringBuffer('PersonEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('thumbnailPath: $thumbnailPath, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + thumbnailPath, + isFavorite, + isHidden, + color, + birthDate, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PersonEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.name == this.name && + other.faceAssetId == this.faceAssetId && + other.thumbnailPath == this.thumbnailPath && + other.isFavorite == this.isFavorite && + other.isHidden == this.isHidden && + other.color == this.color && + other.birthDate == this.birthDate); +} + +class PersonEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value name; + final Value faceAssetId; + final Value thumbnailPath; + final Value isFavorite; + final Value isHidden; + final Value color; + final Value birthDate; + const PersonEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.name = const Value.absent(), + this.faceAssetId = const Value.absent(), + this.thumbnailPath = const Value.absent(), + this.isFavorite = const Value.absent(), + this.isHidden = const Value.absent(), + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }); + PersonEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String name, + this.faceAssetId = const Value.absent(), + required String thumbnailPath, + required bool isFavorite, + required bool isHidden, + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + name = Value(name), + thumbnailPath = Value(thumbnailPath), + isFavorite = Value(isFavorite), + isHidden = Value(isHidden); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? name, + Expression? faceAssetId, + Expression? thumbnailPath, + Expression? isFavorite, + Expression? isHidden, + Expression? color, + Expression? birthDate, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (name != null) 'name': name, + if (faceAssetId != null) 'face_asset_id': faceAssetId, + if (thumbnailPath != null) 'thumbnail_path': thumbnailPath, + if (isFavorite != null) 'is_favorite': isFavorite, + if (isHidden != null) 'is_hidden': isHidden, + if (color != null) 'color': color, + if (birthDate != null) 'birth_date': birthDate, + }); + } + + PersonEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? name, + Value? faceAssetId, + Value? thumbnailPath, + Value? isFavorite, + Value? isHidden, + Value? color, + Value? birthDate, + }) { + return PersonEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId ?? this.faceAssetId, + thumbnailPath: thumbnailPath ?? this.thumbnailPath, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color ?? this.color, + birthDate: birthDate ?? this.birthDate, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (faceAssetId.present) { + map['face_asset_id'] = Variable(faceAssetId.value); + } + if (thumbnailPath.present) { + map['thumbnail_path'] = Variable(thumbnailPath.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (isHidden.present) { + map['is_hidden'] = Variable(isHidden.value); + } + if (color.present) { + map['color'] = Variable(color.value); + } + if (birthDate.present) { + map['birth_date'] = Variable(birthDate.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PersonEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('thumbnailPath: $thumbnailPath, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } +} + class DatabaseAtV2 extends GeneratedDatabase { DatabaseAtV2(QueryExecutor e) : super(e); late final UserEntity userEntity = UserEntity(this); late final RemoteAssetEntity remoteAssetEntity = RemoteAssetEntity(this); late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); late final StackEntity stackEntity = StackEntity(this); - late final Index idxLocalAssetChecksum = Index('idx_local_asset_checksum', - 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)'); + late final Index idxLocalAssetChecksum = Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); late final Index uQRemoteAssetOwnerChecksum = Index( - 'UQ_remote_asset_owner_checksum', - 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)'); - late final Index idxRemoteAssetChecksum = Index('idx_remote_asset_checksum', - 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)'); + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', + ); + late final Index idxRemoteAssetChecksum = Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); late final UserMetadataEntity userMetadataEntity = UserMetadataEntity(this); late final PartnerEntity partnerEntity = PartnerEntity(this); late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); @@ -4671,29 +5962,31 @@ class DatabaseAtV2 extends GeneratedDatabase { RemoteAlbumUserEntity(this); late final MemoryEntity memoryEntity = MemoryEntity(this); late final MemoryAssetEntity memoryAssetEntity = MemoryAssetEntity(this); + late final PersonEntity personEntity = PersonEntity(this); @override Iterable> get allTables => allSchemaEntities.whereType>(); @override List get allSchemaEntities => [ - userEntity, - remoteAssetEntity, - localAssetEntity, - stackEntity, - idxLocalAssetChecksum, - uQRemoteAssetOwnerChecksum, - idxRemoteAssetChecksum, - userMetadataEntity, - partnerEntity, - localAlbumEntity, - localAlbumAssetEntity, - remoteExifEntity, - remoteAlbumEntity, - remoteAlbumAssetEntity, - remoteAlbumUserEntity, - memoryEntity, - memoryAssetEntity - ]; + userEntity, + remoteAssetEntity, + localAssetEntity, + stackEntity, + idxLocalAssetChecksum, + uQRemoteAssetOwnerChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + localAlbumEntity, + localAlbumAssetEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + ]; @override int get schemaVersion => 2; @override diff --git a/mobile/test/drift/main/generated/schema_v3.dart b/mobile/test/drift/main/generated/schema_v3.dart new file mode 100644 index 0000000000..e4382a9fb9 --- /dev/null +++ b/mobile/test/drift/main/generated/schema_v3.dart @@ -0,0 +1,5992 @@ +// dart format width=80 +// GENERATED CODE, DO NOT EDIT BY HAND. +// ignore_for_file: type=lint +import 'package:drift/drift.dart'; + +class UserEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isAdmin = GeneratedColumn( + 'is_admin', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn email = GeneratedColumn( + 'email', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn profileImagePath = GeneratedColumn( + 'profile_image_path', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn quotaSizeInBytes = GeneratedColumn( + 'quota_size_in_bytes', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn quotaUsageInBytes = GeneratedColumn( + 'quota_usage_in_bytes', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [ + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_entity'; + @override + Set get $primaryKey => {id}; + @override + UserEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + isAdmin: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_admin'], + )!, + email: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}email'], + )!, + profileImagePath: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}profile_image_path'], + ), + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + quotaSizeInBytes: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}quota_size_in_bytes'], + ), + quotaUsageInBytes: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}quota_usage_in_bytes'], + )!, + ); + } + + @override + UserEntity createAlias(String alias) { + return UserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserEntityData extends DataClass implements Insertable { + final String id; + final String name; + final bool isAdmin; + final String email; + final String? profileImagePath; + final DateTime updatedAt; + final int? quotaSizeInBytes; + final int quotaUsageInBytes; + const UserEntityData({ + required this.id, + required this.name, + required this.isAdmin, + required this.email, + this.profileImagePath, + required this.updatedAt, + this.quotaSizeInBytes, + required this.quotaUsageInBytes, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['is_admin'] = Variable(isAdmin); + map['email'] = Variable(email); + if (!nullToAbsent || profileImagePath != null) { + map['profile_image_path'] = Variable(profileImagePath); + } + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || quotaSizeInBytes != null) { + map['quota_size_in_bytes'] = Variable(quotaSizeInBytes); + } + map['quota_usage_in_bytes'] = Variable(quotaUsageInBytes); + return map; + } + + factory UserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + isAdmin: serializer.fromJson(json['isAdmin']), + email: serializer.fromJson(json['email']), + profileImagePath: serializer.fromJson(json['profileImagePath']), + updatedAt: serializer.fromJson(json['updatedAt']), + quotaSizeInBytes: serializer.fromJson(json['quotaSizeInBytes']), + quotaUsageInBytes: serializer.fromJson(json['quotaUsageInBytes']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'isAdmin': serializer.toJson(isAdmin), + 'email': serializer.toJson(email), + 'profileImagePath': serializer.toJson(profileImagePath), + 'updatedAt': serializer.toJson(updatedAt), + 'quotaSizeInBytes': serializer.toJson(quotaSizeInBytes), + 'quotaUsageInBytes': serializer.toJson(quotaUsageInBytes), + }; + } + + UserEntityData copyWith({ + String? id, + String? name, + bool? isAdmin, + String? email, + Value profileImagePath = const Value.absent(), + DateTime? updatedAt, + Value quotaSizeInBytes = const Value.absent(), + int? quotaUsageInBytes, + }) => UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + profileImagePath: profileImagePath.present + ? profileImagePath.value + : this.profileImagePath, + updatedAt: updatedAt ?? this.updatedAt, + quotaSizeInBytes: quotaSizeInBytes.present + ? quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + ); + UserEntityData copyWithCompanion(UserEntityCompanion data) { + return UserEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + isAdmin: data.isAdmin.present ? data.isAdmin.value : this.isAdmin, + email: data.email.present ? data.email.value : this.email, + profileImagePath: data.profileImagePath.present + ? data.profileImagePath.value + : this.profileImagePath, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + quotaSizeInBytes: data.quotaSizeInBytes.present + ? data.quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: data.quotaUsageInBytes.present + ? data.quotaUsageInBytes.value + : this.quotaUsageInBytes, + ); + } + + @override + String toString() { + return (StringBuffer('UserEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isAdmin: $isAdmin, ') + ..write('email: $email, ') + ..write('profileImagePath: $profileImagePath, ') + ..write('updatedAt: $updatedAt, ') + ..write('quotaSizeInBytes: $quotaSizeInBytes, ') + ..write('quotaUsageInBytes: $quotaUsageInBytes') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserEntityData && + other.id == this.id && + other.name == this.name && + other.isAdmin == this.isAdmin && + other.email == this.email && + other.profileImagePath == this.profileImagePath && + other.updatedAt == this.updatedAt && + other.quotaSizeInBytes == this.quotaSizeInBytes && + other.quotaUsageInBytes == this.quotaUsageInBytes); +} + +class UserEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value isAdmin; + final Value email; + final Value profileImagePath; + final Value updatedAt; + final Value quotaSizeInBytes; + final Value quotaUsageInBytes; + const UserEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.isAdmin = const Value.absent(), + this.email = const Value.absent(), + this.profileImagePath = const Value.absent(), + this.updatedAt = const Value.absent(), + this.quotaSizeInBytes = const Value.absent(), + this.quotaUsageInBytes = const Value.absent(), + }); + UserEntityCompanion.insert({ + required String id, + required String name, + this.isAdmin = const Value.absent(), + required String email, + this.profileImagePath = const Value.absent(), + this.updatedAt = const Value.absent(), + this.quotaSizeInBytes = const Value.absent(), + this.quotaUsageInBytes = const Value.absent(), + }) : id = Value(id), + name = Value(name), + email = Value(email); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? isAdmin, + Expression? email, + Expression? profileImagePath, + Expression? updatedAt, + Expression? quotaSizeInBytes, + Expression? quotaUsageInBytes, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (isAdmin != null) 'is_admin': isAdmin, + if (email != null) 'email': email, + if (profileImagePath != null) 'profile_image_path': profileImagePath, + if (updatedAt != null) 'updated_at': updatedAt, + if (quotaSizeInBytes != null) 'quota_size_in_bytes': quotaSizeInBytes, + if (quotaUsageInBytes != null) 'quota_usage_in_bytes': quotaUsageInBytes, + }); + } + + UserEntityCompanion copyWith({ + Value? id, + Value? name, + Value? isAdmin, + Value? email, + Value? profileImagePath, + Value? updatedAt, + Value? quotaSizeInBytes, + Value? quotaUsageInBytes, + }) { + return UserEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + profileImagePath: profileImagePath ?? this.profileImagePath, + updatedAt: updatedAt ?? this.updatedAt, + quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (isAdmin.present) { + map['is_admin'] = Variable(isAdmin.value); + } + if (email.present) { + map['email'] = Variable(email.value); + } + if (profileImagePath.present) { + map['profile_image_path'] = Variable(profileImagePath.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (quotaSizeInBytes.present) { + map['quota_size_in_bytes'] = Variable(quotaSizeInBytes.value); + } + if (quotaUsageInBytes.present) { + map['quota_usage_in_bytes'] = Variable(quotaUsageInBytes.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isAdmin: $isAdmin, ') + ..write('email: $email, ') + ..write('profileImagePath: $profileImagePath, ') + ..write('updatedAt: $updatedAt, ') + ..write('quotaSizeInBytes: $quotaSizeInBytes, ') + ..write('quotaUsageInBytes: $quotaUsageInBytes') + ..write(')')) + .toString(); + } +} + +class RemoteAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn localDateTime = + GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn thumbHash = GeneratedColumn( + 'thumb_hash', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn livePhotoVideoId = GeneratedColumn( + 'live_photo_video_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn visibility = GeneratedColumn( + 'visibility', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn stackId = GeneratedColumn( + 'stack_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAssetEntityData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + localDateTime: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}local_date_time'], + ), + thumbHash: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumb_hash'], + ), + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + livePhotoVideoId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}live_photo_video_id'], + ), + visibility: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}visibility'], + )!, + stackId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}stack_id'], + ), + ); + } + + @override + RemoteAssetEntity createAlias(String alias) { + return RemoteAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String checksum; + final bool isFavorite; + final String ownerId; + final DateTime? localDateTime; + final String? thumbHash; + final DateTime? deletedAt; + final String? livePhotoVideoId; + final int visibility; + final String? stackId; + const RemoteAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + required this.checksum, + required this.isFavorite, + required this.ownerId, + this.localDateTime, + this.thumbHash, + this.deletedAt, + this.livePhotoVideoId, + required this.visibility, + this.stackId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + map['checksum'] = Variable(checksum); + map['is_favorite'] = Variable(isFavorite); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || localDateTime != null) { + map['local_date_time'] = Variable(localDateTime); + } + if (!nullToAbsent || thumbHash != null) { + map['thumb_hash'] = Variable(thumbHash); + } + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + if (!nullToAbsent || livePhotoVideoId != null) { + map['live_photo_video_id'] = Variable(livePhotoVideoId); + } + map['visibility'] = Variable(visibility); + if (!nullToAbsent || stackId != null) { + map['stack_id'] = Variable(stackId); + } + return map; + } + + factory RemoteAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + ownerId: serializer.fromJson(json['ownerId']), + localDateTime: serializer.fromJson(json['localDateTime']), + thumbHash: serializer.fromJson(json['thumbHash']), + deletedAt: serializer.fromJson(json['deletedAt']), + livePhotoVideoId: serializer.fromJson(json['livePhotoVideoId']), + visibility: serializer.fromJson(json['visibility']), + stackId: serializer.fromJson(json['stackId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'ownerId': serializer.toJson(ownerId), + 'localDateTime': serializer.toJson(localDateTime), + 'thumbHash': serializer.toJson(thumbHash), + 'deletedAt': serializer.toJson(deletedAt), + 'livePhotoVideoId': serializer.toJson(livePhotoVideoId), + 'visibility': serializer.toJson(visibility), + 'stackId': serializer.toJson(stackId), + }; + } + + RemoteAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + String? checksum, + bool? isFavorite, + String? ownerId, + Value localDateTime = const Value.absent(), + Value thumbHash = const Value.absent(), + Value deletedAt = const Value.absent(), + Value livePhotoVideoId = const Value.absent(), + int? visibility, + Value stackId = const Value.absent(), + }) => RemoteAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime.present + ? localDateTime.value + : this.localDateTime, + thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + livePhotoVideoId: livePhotoVideoId.present + ? livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId.present ? stackId.value : this.stackId, + ); + RemoteAssetEntityData copyWithCompanion(RemoteAssetEntityCompanion data) { + return RemoteAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + localDateTime: data.localDateTime.present + ? data.localDateTime.value + : this.localDateTime, + thumbHash: data.thumbHash.present ? data.thumbHash.value : this.thumbHash, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + livePhotoVideoId: data.livePhotoVideoId.present + ? data.livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: data.visibility.present + ? data.visibility.value + : this.visibility, + stackId: data.stackId.present ? data.stackId.value : this.stackId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.ownerId == this.ownerId && + other.localDateTime == this.localDateTime && + other.thumbHash == this.thumbHash && + other.deletedAt == this.deletedAt && + other.livePhotoVideoId == this.livePhotoVideoId && + other.visibility == this.visibility && + other.stackId == this.stackId); +} + +class RemoteAssetEntityCompanion + extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value ownerId; + final Value localDateTime; + final Value thumbHash; + final Value deletedAt; + final Value livePhotoVideoId; + final Value visibility; + final Value stackId; + const RemoteAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.ownerId = const Value.absent(), + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + this.visibility = const Value.absent(), + this.stackId = const Value.absent(), + }); + RemoteAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + required String checksum, + this.isFavorite = const Value.absent(), + required String ownerId, + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + required int visibility, + this.stackId = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id), + checksum = Value(checksum), + ownerId = Value(ownerId), + visibility = Value(visibility); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? ownerId, + Expression? localDateTime, + Expression? thumbHash, + Expression? deletedAt, + Expression? livePhotoVideoId, + Expression? visibility, + Expression? stackId, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (ownerId != null) 'owner_id': ownerId, + if (localDateTime != null) 'local_date_time': localDateTime, + if (thumbHash != null) 'thumb_hash': thumbHash, + if (deletedAt != null) 'deleted_at': deletedAt, + if (livePhotoVideoId != null) 'live_photo_video_id': livePhotoVideoId, + if (visibility != null) 'visibility': visibility, + if (stackId != null) 'stack_id': stackId, + }); + } + + RemoteAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? ownerId, + Value? localDateTime, + Value? thumbHash, + Value? deletedAt, + Value? livePhotoVideoId, + Value? visibility, + Value? stackId, + }) { + return RemoteAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime ?? this.localDateTime, + thumbHash: thumbHash ?? this.thumbHash, + deletedAt: deletedAt ?? this.deletedAt, + livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId ?? this.stackId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (localDateTime.present) { + map['local_date_time'] = Variable(localDateTime.value); + } + if (thumbHash.present) { + map['thumb_hash'] = Variable(thumbHash.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (livePhotoVideoId.present) { + map['live_photo_video_id'] = Variable(livePhotoVideoId.value); + } + if (visibility.present) { + map['visibility'] = Variable(visibility.value); + } + if (stackId.present) { + map['stack_id'] = Variable(stackId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId') + ..write(')')) + .toString(); + } +} + +class LocalAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAssetEntityData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}orientation'], + )!, + ); + } + + @override + LocalAssetEntity createAlias(String alias) { + return LocalAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String? checksum; + final bool isFavorite; + final int orientation; + const LocalAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + this.checksum, + required this.isFavorite, + required this.orientation, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + if (!nullToAbsent || checksum != null) { + map['checksum'] = Variable(checksum); + } + map['is_favorite'] = Variable(isFavorite); + map['orientation'] = Variable(orientation); + return map; + } + + factory LocalAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + orientation: serializer.fromJson(json['orientation']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'orientation': serializer.toJson(orientation), + }; + } + + LocalAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + Value checksum = const Value.absent(), + bool? isFavorite, + int? orientation, + }) => LocalAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + LocalAssetEntityData copyWithCompanion(LocalAssetEntityCompanion data) { + return LocalAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.orientation == this.orientation); +} + +class LocalAssetEntityCompanion extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value orientation; + const LocalAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }); + LocalAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? orientation, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (orientation != null) 'orientation': orientation, + }); + } + + LocalAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? orientation, + }) { + return LocalAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } +} + +class StackEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + StackEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn primaryAssetId = GeneratedColumn( + 'primary_asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + primaryAssetId, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'stack_entity'; + @override + Set get $primaryKey => {id}; + @override + StackEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StackEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + primaryAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}primary_asset_id'], + )!, + ); + } + + @override + StackEntity createAlias(String alias) { + return StackEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class StackEntityData extends DataClass implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String primaryAssetId; + const StackEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['primary_asset_id'] = Variable(primaryAssetId); + return map; + } + + factory StackEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StackEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + primaryAssetId: serializer.fromJson(json['primaryAssetId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'primaryAssetId': serializer.toJson(primaryAssetId), + }; + } + + StackEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId, + }) => StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + StackEntityData copyWithCompanion(StackEntityCompanion data) { + return StackEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + primaryAssetId: data.primaryAssetId.present + ? data.primaryAssetId.value + : this.primaryAssetId, + ); + } + + @override + String toString() { + return (StringBuffer('StackEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StackEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.primaryAssetId == this.primaryAssetId); +} + +class StackEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value primaryAssetId; + const StackEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.primaryAssetId = const Value.absent(), + }); + StackEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String primaryAssetId, + }) : id = Value(id), + ownerId = Value(ownerId), + primaryAssetId = Value(primaryAssetId); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? primaryAssetId, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (primaryAssetId != null) 'primary_asset_id': primaryAssetId, + }); + } + + StackEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? primaryAssetId, + }) { + return StackEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (primaryAssetId.present) { + map['primary_asset_id'] = Variable(primaryAssetId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StackEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } +} + +class UserMetadataEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserMetadataEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn key = GeneratedColumn( + 'key', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn value = GeneratedColumn( + 'value', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + ); + @override + List get $columns => [userId, key, value]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_metadata_entity'; + @override + Set get $primaryKey => {userId, key}; + @override + UserMetadataEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserMetadataEntityData( + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + key: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}key'], + )!, + value: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}value'], + )!, + ); + } + + @override + UserMetadataEntity createAlias(String alias) { + return UserMetadataEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserMetadataEntityData extends DataClass + implements Insertable { + final String userId; + final int key; + final Uint8List value; + const UserMetadataEntityData({ + required this.userId, + required this.key, + required this.value, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['user_id'] = Variable(userId); + map['key'] = Variable(key); + map['value'] = Variable(value); + return map; + } + + factory UserMetadataEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserMetadataEntityData( + userId: serializer.fromJson(json['userId']), + key: serializer.fromJson(json['key']), + value: serializer.fromJson(json['value']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'userId': serializer.toJson(userId), + 'key': serializer.toJson(key), + 'value': serializer.toJson(value), + }; + } + + UserMetadataEntityData copyWith({ + String? userId, + int? key, + Uint8List? value, + }) => UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { + return UserMetadataEntityData( + userId: data.userId.present ? data.userId.value : this.userId, + key: data.key.present ? data.key.value : this.key, + value: data.value.present ? data.value.value : this.value, + ); + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityData(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(userId, key, $driftBlobEquality.hash(value)); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserMetadataEntityData && + other.userId == this.userId && + other.key == this.key && + $driftBlobEquality.equals(other.value, this.value)); +} + +class UserMetadataEntityCompanion + extends UpdateCompanion { + final Value userId; + final Value key; + final Value value; + const UserMetadataEntityCompanion({ + this.userId = const Value.absent(), + this.key = const Value.absent(), + this.value = const Value.absent(), + }); + UserMetadataEntityCompanion.insert({ + required String userId, + required int key, + required Uint8List value, + }) : userId = Value(userId), + key = Value(key), + value = Value(value); + static Insertable custom({ + Expression? userId, + Expression? key, + Expression? value, + }) { + return RawValuesInsertable({ + if (userId != null) 'user_id': userId, + if (key != null) 'key': key, + if (value != null) 'value': value, + }); + } + + UserMetadataEntityCompanion copyWith({ + Value? userId, + Value? key, + Value? value, + }) { + return UserMetadataEntityCompanion( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (key.present) { + map['key'] = Variable(key.value); + } + if (value.present) { + map['value'] = Variable(value.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityCompanion(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } +} + +class PartnerEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PartnerEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn sharedById = GeneratedColumn( + 'shared_by_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn sharedWithId = GeneratedColumn( + 'shared_with_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn inTimeline = GeneratedColumn( + 'in_timeline', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [sharedById, sharedWithId, inTimeline]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'partner_entity'; + @override + Set get $primaryKey => {sharedById, sharedWithId}; + @override + PartnerEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PartnerEntityData( + sharedById: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_by_id'], + )!, + sharedWithId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_with_id'], + )!, + inTimeline: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}in_timeline'], + )!, + ); + } + + @override + PartnerEntity createAlias(String alias) { + return PartnerEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PartnerEntityData extends DataClass + implements Insertable { + final String sharedById; + final String sharedWithId; + final bool inTimeline; + const PartnerEntityData({ + required this.sharedById, + required this.sharedWithId, + required this.inTimeline, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['shared_by_id'] = Variable(sharedById); + map['shared_with_id'] = Variable(sharedWithId); + map['in_timeline'] = Variable(inTimeline); + return map; + } + + factory PartnerEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PartnerEntityData( + sharedById: serializer.fromJson(json['sharedById']), + sharedWithId: serializer.fromJson(json['sharedWithId']), + inTimeline: serializer.fromJson(json['inTimeline']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'sharedById': serializer.toJson(sharedById), + 'sharedWithId': serializer.toJson(sharedWithId), + 'inTimeline': serializer.toJson(inTimeline), + }; + } + + PartnerEntityData copyWith({ + String? sharedById, + String? sharedWithId, + bool? inTimeline, + }) => PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { + return PartnerEntityData( + sharedById: data.sharedById.present + ? data.sharedById.value + : this.sharedById, + sharedWithId: data.sharedWithId.present + ? data.sharedWithId.value + : this.sharedWithId, + inTimeline: data.inTimeline.present + ? data.inTimeline.value + : this.inTimeline, + ); + } + + @override + String toString() { + return (StringBuffer('PartnerEntityData(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(sharedById, sharedWithId, inTimeline); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PartnerEntityData && + other.sharedById == this.sharedById && + other.sharedWithId == this.sharedWithId && + other.inTimeline == this.inTimeline); +} + +class PartnerEntityCompanion extends UpdateCompanion { + final Value sharedById; + final Value sharedWithId; + final Value inTimeline; + const PartnerEntityCompanion({ + this.sharedById = const Value.absent(), + this.sharedWithId = const Value.absent(), + this.inTimeline = const Value.absent(), + }); + PartnerEntityCompanion.insert({ + required String sharedById, + required String sharedWithId, + this.inTimeline = const Value.absent(), + }) : sharedById = Value(sharedById), + sharedWithId = Value(sharedWithId); + static Insertable custom({ + Expression? sharedById, + Expression? sharedWithId, + Expression? inTimeline, + }) { + return RawValuesInsertable({ + if (sharedById != null) 'shared_by_id': sharedById, + if (sharedWithId != null) 'shared_with_id': sharedWithId, + if (inTimeline != null) 'in_timeline': inTimeline, + }); + } + + PartnerEntityCompanion copyWith({ + Value? sharedById, + Value? sharedWithId, + Value? inTimeline, + }) { + return PartnerEntityCompanion( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (sharedById.present) { + map['shared_by_id'] = Variable(sharedById.value); + } + if (sharedWithId.present) { + map['shared_with_id'] = Variable(sharedWithId.value); + } + if (inTimeline.present) { + map['in_timeline'] = Variable(inTimeline.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PartnerEntityCompanion(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } +} + +class LocalAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn backupSelection = GeneratedColumn( + 'backup_selection', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn isIosSharedAlbum = GeneratedColumn( + 'is_ios_shared_album', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn marker_ = GeneratedColumn( + 'marker', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); + @override + List get $columns => [ + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + backupSelection: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}backup_selection'], + )!, + isIosSharedAlbum: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_ios_shared_album'], + )!, + marker_: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), + ); + } + + @override + LocalAlbumEntity createAlias(String alias) { + return LocalAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final DateTime updatedAt; + final int backupSelection; + final bool isIosSharedAlbum; + final bool? marker_; + const LocalAlbumEntityData({ + required this.id, + required this.name, + required this.updatedAt, + required this.backupSelection, + required this.isIosSharedAlbum, + this.marker_, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['updated_at'] = Variable(updatedAt); + map['backup_selection'] = Variable(backupSelection); + map['is_ios_shared_album'] = Variable(isIosSharedAlbum); + if (!nullToAbsent || marker_ != null) { + map['marker'] = Variable(marker_); + } + return map; + } + + factory LocalAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + updatedAt: serializer.fromJson(json['updatedAt']), + backupSelection: serializer.fromJson(json['backupSelection']), + isIosSharedAlbum: serializer.fromJson(json['isIosSharedAlbum']), + marker_: serializer.fromJson(json['marker_']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'updatedAt': serializer.toJson(updatedAt), + 'backupSelection': serializer.toJson(backupSelection), + 'isIosSharedAlbum': serializer.toJson(isIosSharedAlbum), + 'marker_': serializer.toJson(marker_), + }; + } + + LocalAlbumEntityData copyWith({ + String? id, + String? name, + DateTime? updatedAt, + int? backupSelection, + bool? isIosSharedAlbum, + Value marker_ = const Value.absent(), + }) => LocalAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_.present ? marker_.value : this.marker_, + ); + LocalAlbumEntityData copyWithCompanion(LocalAlbumEntityCompanion data) { + return LocalAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + backupSelection: data.backupSelection.present + ? data.backupSelection.value + : this.backupSelection, + isIosSharedAlbum: data.isIosSharedAlbum.present + ? data.isIosSharedAlbum.value + : this.isIosSharedAlbum, + marker_: data.marker_.present ? data.marker_.value : this.marker_, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.updatedAt == this.updatedAt && + other.backupSelection == this.backupSelection && + other.isIosSharedAlbum == this.isIosSharedAlbum && + other.marker_ == this.marker_); +} + +class LocalAlbumEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value updatedAt; + final Value backupSelection; + final Value isIosSharedAlbum; + final Value marker_; + const LocalAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.updatedAt = const Value.absent(), + this.backupSelection = const Value.absent(), + this.isIosSharedAlbum = const Value.absent(), + this.marker_ = const Value.absent(), + }); + LocalAlbumEntityCompanion.insert({ + required String id, + required String name, + this.updatedAt = const Value.absent(), + required int backupSelection, + this.isIosSharedAlbum = const Value.absent(), + this.marker_ = const Value.absent(), + }) : id = Value(id), + name = Value(name), + backupSelection = Value(backupSelection); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? updatedAt, + Expression? backupSelection, + Expression? isIosSharedAlbum, + Expression? marker_, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (updatedAt != null) 'updated_at': updatedAt, + if (backupSelection != null) 'backup_selection': backupSelection, + if (isIosSharedAlbum != null) 'is_ios_shared_album': isIosSharedAlbum, + if (marker_ != null) 'marker': marker_, + }); + } + + LocalAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? updatedAt, + Value? backupSelection, + Value? isIosSharedAlbum, + Value? marker_, + }) { + return LocalAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_ ?? this.marker_, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (backupSelection.present) { + map['backup_selection'] = Variable(backupSelection.value); + } + if (isIosSharedAlbum.present) { + map['is_ios_shared_album'] = Variable(isIosSharedAlbum.value); + } + if (marker_.present) { + map['marker'] = Variable(marker_.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } +} + +class LocalAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + LocalAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + ); + } + + @override + LocalAlbumAssetEntity createAlias(String alias) { + return LocalAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const LocalAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory LocalAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + LocalAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + LocalAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + LocalAlbumAssetEntityData copyWithCompanion( + LocalAlbumAssetEntityCompanion data, + ) { + return LocalAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class LocalAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const LocalAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + LocalAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + LocalAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { + return LocalAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class RemoteExifEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteExifEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn city = GeneratedColumn( + 'city', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn state = GeneratedColumn( + 'state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn country = GeneratedColumn( + 'country', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn dateTimeOriginal = + GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn exposureTime = GeneratedColumn( + 'exposure_time', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn fNumber = GeneratedColumn( + 'f_number', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn fileSize = GeneratedColumn( + 'file_size', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn focalLength = GeneratedColumn( + 'focal_length', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn latitude = GeneratedColumn( + 'latitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn longitude = GeneratedColumn( + 'longitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn iso = GeneratedColumn( + 'iso', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn make = GeneratedColumn( + 'make', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn model = GeneratedColumn( + 'model', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn lens = GeneratedColumn( + 'lens', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn timeZone = GeneratedColumn( + 'time_zone', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn rating = GeneratedColumn( + 'rating', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn projectionType = GeneratedColumn( + 'projection_type', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_exif_entity'; + @override + Set get $primaryKey => {assetId}; + @override + RemoteExifEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteExifEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + city: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}city'], + ), + state: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}state'], + ), + country: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}country'], + ), + dateTimeOriginal: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}date_time_original'], + ), + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + exposureTime: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}exposure_time'], + ), + fNumber: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}f_number'], + ), + fileSize: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}file_size'], + ), + focalLength: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}focal_length'], + ), + latitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}latitude'], + ), + longitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}longitude'], + ), + iso: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}iso'], + ), + make: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}make'], + ), + model: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}model'], + ), + lens: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}lens'], + ), + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}orientation'], + ), + timeZone: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}time_zone'], + ), + rating: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}rating'], + ), + projectionType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}projection_type'], + ), + ); + } + + @override + RemoteExifEntity createAlias(String alias) { + return RemoteExifEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteExifEntityData extends DataClass + implements Insertable { + final String assetId; + final String? city; + final String? state; + final String? country; + final DateTime? dateTimeOriginal; + final String? description; + final int? height; + final int? width; + final String? exposureTime; + final double? fNumber; + final int? fileSize; + final double? focalLength; + final double? latitude; + final double? longitude; + final int? iso; + final String? make; + final String? model; + final String? lens; + final String? orientation; + final String? timeZone; + final int? rating; + final String? projectionType; + const RemoteExifEntityData({ + required this.assetId, + this.city, + this.state, + this.country, + this.dateTimeOriginal, + this.description, + this.height, + this.width, + this.exposureTime, + this.fNumber, + this.fileSize, + this.focalLength, + this.latitude, + this.longitude, + this.iso, + this.make, + this.model, + this.lens, + this.orientation, + this.timeZone, + this.rating, + this.projectionType, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || city != null) { + map['city'] = Variable(city); + } + if (!nullToAbsent || state != null) { + map['state'] = Variable(state); + } + if (!nullToAbsent || country != null) { + map['country'] = Variable(country); + } + if (!nullToAbsent || dateTimeOriginal != null) { + map['date_time_original'] = Variable(dateTimeOriginal); + } + if (!nullToAbsent || description != null) { + map['description'] = Variable(description); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || exposureTime != null) { + map['exposure_time'] = Variable(exposureTime); + } + if (!nullToAbsent || fNumber != null) { + map['f_number'] = Variable(fNumber); + } + if (!nullToAbsent || fileSize != null) { + map['file_size'] = Variable(fileSize); + } + if (!nullToAbsent || focalLength != null) { + map['focal_length'] = Variable(focalLength); + } + if (!nullToAbsent || latitude != null) { + map['latitude'] = Variable(latitude); + } + if (!nullToAbsent || longitude != null) { + map['longitude'] = Variable(longitude); + } + if (!nullToAbsent || iso != null) { + map['iso'] = Variable(iso); + } + if (!nullToAbsent || make != null) { + map['make'] = Variable(make); + } + if (!nullToAbsent || model != null) { + map['model'] = Variable(model); + } + if (!nullToAbsent || lens != null) { + map['lens'] = Variable(lens); + } + if (!nullToAbsent || orientation != null) { + map['orientation'] = Variable(orientation); + } + if (!nullToAbsent || timeZone != null) { + map['time_zone'] = Variable(timeZone); + } + if (!nullToAbsent || rating != null) { + map['rating'] = Variable(rating); + } + if (!nullToAbsent || projectionType != null) { + map['projection_type'] = Variable(projectionType); + } + return map; + } + + factory RemoteExifEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteExifEntityData( + assetId: serializer.fromJson(json['assetId']), + city: serializer.fromJson(json['city']), + state: serializer.fromJson(json['state']), + country: serializer.fromJson(json['country']), + dateTimeOriginal: serializer.fromJson( + json['dateTimeOriginal'], + ), + description: serializer.fromJson(json['description']), + height: serializer.fromJson(json['height']), + width: serializer.fromJson(json['width']), + exposureTime: serializer.fromJson(json['exposureTime']), + fNumber: serializer.fromJson(json['fNumber']), + fileSize: serializer.fromJson(json['fileSize']), + focalLength: serializer.fromJson(json['focalLength']), + latitude: serializer.fromJson(json['latitude']), + longitude: serializer.fromJson(json['longitude']), + iso: serializer.fromJson(json['iso']), + make: serializer.fromJson(json['make']), + model: serializer.fromJson(json['model']), + lens: serializer.fromJson(json['lens']), + orientation: serializer.fromJson(json['orientation']), + timeZone: serializer.fromJson(json['timeZone']), + rating: serializer.fromJson(json['rating']), + projectionType: serializer.fromJson(json['projectionType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'city': serializer.toJson(city), + 'state': serializer.toJson(state), + 'country': serializer.toJson(country), + 'dateTimeOriginal': serializer.toJson(dateTimeOriginal), + 'description': serializer.toJson(description), + 'height': serializer.toJson(height), + 'width': serializer.toJson(width), + 'exposureTime': serializer.toJson(exposureTime), + 'fNumber': serializer.toJson(fNumber), + 'fileSize': serializer.toJson(fileSize), + 'focalLength': serializer.toJson(focalLength), + 'latitude': serializer.toJson(latitude), + 'longitude': serializer.toJson(longitude), + 'iso': serializer.toJson(iso), + 'make': serializer.toJson(make), + 'model': serializer.toJson(model), + 'lens': serializer.toJson(lens), + 'orientation': serializer.toJson(orientation), + 'timeZone': serializer.toJson(timeZone), + 'rating': serializer.toJson(rating), + 'projectionType': serializer.toJson(projectionType), + }; + } + + RemoteExifEntityData copyWith({ + String? assetId, + Value city = const Value.absent(), + Value state = const Value.absent(), + Value country = const Value.absent(), + Value dateTimeOriginal = const Value.absent(), + Value description = const Value.absent(), + Value height = const Value.absent(), + Value width = const Value.absent(), + Value exposureTime = const Value.absent(), + Value fNumber = const Value.absent(), + Value fileSize = const Value.absent(), + Value focalLength = const Value.absent(), + Value latitude = const Value.absent(), + Value longitude = const Value.absent(), + Value iso = const Value.absent(), + Value make = const Value.absent(), + Value model = const Value.absent(), + Value lens = const Value.absent(), + Value orientation = const Value.absent(), + Value timeZone = const Value.absent(), + Value rating = const Value.absent(), + Value projectionType = const Value.absent(), + }) => RemoteExifEntityData( + assetId: assetId ?? this.assetId, + city: city.present ? city.value : this.city, + state: state.present ? state.value : this.state, + country: country.present ? country.value : this.country, + dateTimeOriginal: dateTimeOriginal.present + ? dateTimeOriginal.value + : this.dateTimeOriginal, + description: description.present ? description.value : this.description, + height: height.present ? height.value : this.height, + width: width.present ? width.value : this.width, + exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, + fNumber: fNumber.present ? fNumber.value : this.fNumber, + fileSize: fileSize.present ? fileSize.value : this.fileSize, + focalLength: focalLength.present ? focalLength.value : this.focalLength, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + iso: iso.present ? iso.value : this.iso, + make: make.present ? make.value : this.make, + model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, + orientation: orientation.present ? orientation.value : this.orientation, + timeZone: timeZone.present ? timeZone.value : this.timeZone, + rating: rating.present ? rating.value : this.rating, + projectionType: projectionType.present + ? projectionType.value + : this.projectionType, + ); + RemoteExifEntityData copyWithCompanion(RemoteExifEntityCompanion data) { + return RemoteExifEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + city: data.city.present ? data.city.value : this.city, + state: data.state.present ? data.state.value : this.state, + country: data.country.present ? data.country.value : this.country, + dateTimeOriginal: data.dateTimeOriginal.present + ? data.dateTimeOriginal.value + : this.dateTimeOriginal, + description: data.description.present + ? data.description.value + : this.description, + height: data.height.present ? data.height.value : this.height, + width: data.width.present ? data.width.value : this.width, + exposureTime: data.exposureTime.present + ? data.exposureTime.value + : this.exposureTime, + fNumber: data.fNumber.present ? data.fNumber.value : this.fNumber, + fileSize: data.fileSize.present ? data.fileSize.value : this.fileSize, + focalLength: data.focalLength.present + ? data.focalLength.value + : this.focalLength, + latitude: data.latitude.present ? data.latitude.value : this.latitude, + longitude: data.longitude.present ? data.longitude.value : this.longitude, + iso: data.iso.present ? data.iso.value : this.iso, + make: data.make.present ? data.make.value : this.make, + model: data.model.present ? data.model.value : this.model, + lens: data.lens.present ? data.lens.value : this.lens, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, + timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, + rating: data.rating.present ? data.rating.value : this.rating, + projectionType: data.projectionType.present + ? data.projectionType.value + : this.projectionType, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityData(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hashAll([ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteExifEntityData && + other.assetId == this.assetId && + other.city == this.city && + other.state == this.state && + other.country == this.country && + other.dateTimeOriginal == this.dateTimeOriginal && + other.description == this.description && + other.height == this.height && + other.width == this.width && + other.exposureTime == this.exposureTime && + other.fNumber == this.fNumber && + other.fileSize == this.fileSize && + other.focalLength == this.focalLength && + other.latitude == this.latitude && + other.longitude == this.longitude && + other.iso == this.iso && + other.make == this.make && + other.model == this.model && + other.lens == this.lens && + other.orientation == this.orientation && + other.timeZone == this.timeZone && + other.rating == this.rating && + other.projectionType == this.projectionType); +} + +class RemoteExifEntityCompanion extends UpdateCompanion { + final Value assetId; + final Value city; + final Value state; + final Value country; + final Value dateTimeOriginal; + final Value description; + final Value height; + final Value width; + final Value exposureTime; + final Value fNumber; + final Value fileSize; + final Value focalLength; + final Value latitude; + final Value longitude; + final Value iso; + final Value make; + final Value model; + final Value lens; + final Value orientation; + final Value timeZone; + final Value rating; + final Value projectionType; + const RemoteExifEntityCompanion({ + this.assetId = const Value.absent(), + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }); + RemoteExifEntityCompanion.insert({ + required String assetId, + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }) : assetId = Value(assetId); + static Insertable custom({ + Expression? assetId, + Expression? city, + Expression? state, + Expression? country, + Expression? dateTimeOriginal, + Expression? description, + Expression? height, + Expression? width, + Expression? exposureTime, + Expression? fNumber, + Expression? fileSize, + Expression? focalLength, + Expression? latitude, + Expression? longitude, + Expression? iso, + Expression? make, + Expression? model, + Expression? lens, + Expression? orientation, + Expression? timeZone, + Expression? rating, + Expression? projectionType, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (city != null) 'city': city, + if (state != null) 'state': state, + if (country != null) 'country': country, + if (dateTimeOriginal != null) 'date_time_original': dateTimeOriginal, + if (description != null) 'description': description, + if (height != null) 'height': height, + if (width != null) 'width': width, + if (exposureTime != null) 'exposure_time': exposureTime, + if (fNumber != null) 'f_number': fNumber, + if (fileSize != null) 'file_size': fileSize, + if (focalLength != null) 'focal_length': focalLength, + if (latitude != null) 'latitude': latitude, + if (longitude != null) 'longitude': longitude, + if (iso != null) 'iso': iso, + if (make != null) 'make': make, + if (model != null) 'model': model, + if (lens != null) 'lens': lens, + if (orientation != null) 'orientation': orientation, + if (timeZone != null) 'time_zone': timeZone, + if (rating != null) 'rating': rating, + if (projectionType != null) 'projection_type': projectionType, + }); + } + + RemoteExifEntityCompanion copyWith({ + Value? assetId, + Value? city, + Value? state, + Value? country, + Value? dateTimeOriginal, + Value? description, + Value? height, + Value? width, + Value? exposureTime, + Value? fNumber, + Value? fileSize, + Value? focalLength, + Value? latitude, + Value? longitude, + Value? iso, + Value? make, + Value? model, + Value? lens, + Value? orientation, + Value? timeZone, + Value? rating, + Value? projectionType, + }) { + return RemoteExifEntityCompanion( + assetId: assetId ?? this.assetId, + city: city ?? this.city, + state: state ?? this.state, + country: country ?? this.country, + dateTimeOriginal: dateTimeOriginal ?? this.dateTimeOriginal, + description: description ?? this.description, + height: height ?? this.height, + width: width ?? this.width, + exposureTime: exposureTime ?? this.exposureTime, + fNumber: fNumber ?? this.fNumber, + fileSize: fileSize ?? this.fileSize, + focalLength: focalLength ?? this.focalLength, + latitude: latitude ?? this.latitude, + longitude: longitude ?? this.longitude, + iso: iso ?? this.iso, + make: make ?? this.make, + model: model ?? this.model, + lens: lens ?? this.lens, + orientation: orientation ?? this.orientation, + timeZone: timeZone ?? this.timeZone, + rating: rating ?? this.rating, + projectionType: projectionType ?? this.projectionType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (city.present) { + map['city'] = Variable(city.value); + } + if (state.present) { + map['state'] = Variable(state.value); + } + if (country.present) { + map['country'] = Variable(country.value); + } + if (dateTimeOriginal.present) { + map['date_time_original'] = Variable(dateTimeOriginal.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (exposureTime.present) { + map['exposure_time'] = Variable(exposureTime.value); + } + if (fNumber.present) { + map['f_number'] = Variable(fNumber.value); + } + if (fileSize.present) { + map['file_size'] = Variable(fileSize.value); + } + if (focalLength.present) { + map['focal_length'] = Variable(focalLength.value); + } + if (latitude.present) { + map['latitude'] = Variable(latitude.value); + } + if (longitude.present) { + map['longitude'] = Variable(longitude.value); + } + if (iso.present) { + map['iso'] = Variable(iso.value); + } + if (make.present) { + map['make'] = Variable(make.value); + } + if (model.present) { + map['model'] = Variable(model.value); + } + if (lens.present) { + map['lens'] = Variable(lens.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + if (timeZone.present) { + map['time_zone'] = Variable(timeZone.value); + } + if (rating.present) { + map['rating'] = Variable(rating.value); + } + if (projectionType.present) { + map['projection_type'] = Variable(projectionType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const CustomExpression('\'\''), + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn thumbnailAssetId = GeneratedColumn( + 'thumbnail_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn isActivityEnabled = GeneratedColumn( + 'is_activity_enabled', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const CustomExpression('1'), + ); + late final GeneratedColumn order = GeneratedColumn( + 'order', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + thumbnailAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_asset_id'], + ), + isActivityEnabled: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_activity_enabled'], + )!, + order: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}order'], + )!, + ); + } + + @override + RemoteAlbumEntity createAlias(String alias) { + return RemoteAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final String description; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String? thumbnailAssetId; + final bool isActivityEnabled; + final int order; + const RemoteAlbumEntityData({ + required this.id, + required this.name, + required this.description, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + this.thumbnailAssetId, + required this.isActivityEnabled, + required this.order, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['description'] = Variable(description); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || thumbnailAssetId != null) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId); + } + map['is_activity_enabled'] = Variable(isActivityEnabled); + map['order'] = Variable(order); + return map; + } + + factory RemoteAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + description: serializer.fromJson(json['description']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + thumbnailAssetId: serializer.fromJson(json['thumbnailAssetId']), + isActivityEnabled: serializer.fromJson(json['isActivityEnabled']), + order: serializer.fromJson(json['order']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'description': serializer.toJson(description), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'thumbnailAssetId': serializer.toJson(thumbnailAssetId), + 'isActivityEnabled': serializer.toJson(isActivityEnabled), + 'order': serializer.toJson(order), + }; + } + + RemoteAlbumEntityData copyWith({ + String? id, + String? name, + String? description, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + Value thumbnailAssetId = const Value.absent(), + bool? isActivityEnabled, + int? order, + }) => RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId.present + ? thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + RemoteAlbumEntityData copyWithCompanion(RemoteAlbumEntityCompanion data) { + return RemoteAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + description: data.description.present + ? data.description.value + : this.description, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + thumbnailAssetId: data.thumbnailAssetId.present + ? data.thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: data.isActivityEnabled.present + ? data.isActivityEnabled.value + : this.isActivityEnabled, + order: data.order.present ? data.order.value : this.order, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.description == this.description && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.thumbnailAssetId == this.thumbnailAssetId && + other.isActivityEnabled == this.isActivityEnabled && + other.order == this.order); +} + +class RemoteAlbumEntityCompanion + extends UpdateCompanion { + final Value id; + final Value name; + final Value description; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value thumbnailAssetId; + final Value isActivityEnabled; + final Value order; + const RemoteAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + this.order = const Value.absent(), + }); + RemoteAlbumEntityCompanion.insert({ + required String id, + required String name, + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + required int order, + }) : id = Value(id), + name = Value(name), + ownerId = Value(ownerId), + order = Value(order); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? description, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? thumbnailAssetId, + Expression? isActivityEnabled, + Expression? order, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (description != null) 'description': description, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (thumbnailAssetId != null) 'thumbnail_asset_id': thumbnailAssetId, + if (isActivityEnabled != null) 'is_activity_enabled': isActivityEnabled, + if (order != null) 'order': order, + }); + } + + RemoteAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? description, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? thumbnailAssetId, + Value? isActivityEnabled, + Value? order, + }) { + return RemoteAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId ?? this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (thumbnailAssetId.present) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId.value); + } + if (isActivityEnabled.present) { + map['is_activity_enabled'] = Variable(isActivityEnabled.value); + } + if (order.present) { + map['order'] = Variable(order.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + RemoteAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + ); + } + + @override + RemoteAlbumAssetEntity createAlias(String alias) { + return RemoteAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const RemoteAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory RemoteAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + RemoteAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + RemoteAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + RemoteAlbumAssetEntityData copyWithCompanion( + RemoteAlbumAssetEntityCompanion data, + ) { + return RemoteAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class RemoteAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const RemoteAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + RemoteAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + RemoteAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { + return RemoteAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumUserEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumUserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn role = GeneratedColumn( + 'role', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [albumId, userId, role]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_user_entity'; + @override + Set get $primaryKey => {albumId, userId}; + @override + RemoteAlbumUserEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumUserEntityData( + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + role: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}role'], + )!, + ); + } + + @override + RemoteAlbumUserEntity createAlias(String alias) { + return RemoteAlbumUserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumUserEntityData extends DataClass + implements Insertable { + final String albumId; + final String userId; + final int role; + const RemoteAlbumUserEntityData({ + required this.albumId, + required this.userId, + required this.role, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['album_id'] = Variable(albumId); + map['user_id'] = Variable(userId); + map['role'] = Variable(role); + return map; + } + + factory RemoteAlbumUserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumUserEntityData( + albumId: serializer.fromJson(json['albumId']), + userId: serializer.fromJson(json['userId']), + role: serializer.fromJson(json['role']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'albumId': serializer.toJson(albumId), + 'userId': serializer.toJson(userId), + 'role': serializer.toJson(role), + }; + } + + RemoteAlbumUserEntityData copyWith({ + String? albumId, + String? userId, + int? role, + }) => RemoteAlbumUserEntityData( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + RemoteAlbumUserEntityData copyWithCompanion( + RemoteAlbumUserEntityCompanion data, + ) { + return RemoteAlbumUserEntityData( + albumId: data.albumId.present ? data.albumId.value : this.albumId, + userId: data.userId.present ? data.userId.value : this.userId, + role: data.role.present ? data.role.value : this.role, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityData(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(albumId, userId, role); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumUserEntityData && + other.albumId == this.albumId && + other.userId == this.userId && + other.role == this.role); +} + +class RemoteAlbumUserEntityCompanion + extends UpdateCompanion { + final Value albumId; + final Value userId; + final Value role; + const RemoteAlbumUserEntityCompanion({ + this.albumId = const Value.absent(), + this.userId = const Value.absent(), + this.role = const Value.absent(), + }); + RemoteAlbumUserEntityCompanion.insert({ + required String albumId, + required String userId, + required int role, + }) : albumId = Value(albumId), + userId = Value(userId), + role = Value(role); + static Insertable custom({ + Expression? albumId, + Expression? userId, + Expression? role, + }) { + return RawValuesInsertable({ + if (albumId != null) 'album_id': albumId, + if (userId != null) 'user_id': userId, + if (role != null) 'role': role, + }); + } + + RemoteAlbumUserEntityCompanion copyWith({ + Value? albumId, + Value? userId, + Value? role, + }) { + return RemoteAlbumUserEntityCompanion( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (role.present) { + map['role'] = Variable(role.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityCompanion(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } +} + +class MemoryEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn data = GeneratedColumn( + 'data', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isSaved = GeneratedColumn( + 'is_saved', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn memoryAt = GeneratedColumn( + 'memory_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: true, + ); + late final GeneratedColumn seenAt = GeneratedColumn( + 'seen_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn showAt = GeneratedColumn( + 'show_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn hideAt = GeneratedColumn( + 'hide_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_entity'; + @override + Set get $primaryKey => {id}; + @override + MemoryEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + data: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}data'], + )!, + isSaved: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_saved'], + )!, + memoryAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}memory_at'], + )!, + seenAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}seen_at'], + ), + showAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}show_at'], + ), + hideAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}hide_at'], + ), + ); + } + + @override + MemoryEntity createAlias(String alias) { + return MemoryEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final DateTime? deletedAt; + final String ownerId; + final int type; + final String data; + final bool isSaved; + final DateTime memoryAt; + final DateTime? seenAt; + final DateTime? showAt; + final DateTime? hideAt; + const MemoryEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + map['owner_id'] = Variable(ownerId); + map['type'] = Variable(type); + map['data'] = Variable(data); + map['is_saved'] = Variable(isSaved); + map['memory_at'] = Variable(memoryAt); + if (!nullToAbsent || seenAt != null) { + map['seen_at'] = Variable(seenAt); + } + if (!nullToAbsent || showAt != null) { + map['show_at'] = Variable(showAt); + } + if (!nullToAbsent || hideAt != null) { + map['hide_at'] = Variable(hideAt); + } + return map; + } + + factory MemoryEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + deletedAt: serializer.fromJson(json['deletedAt']), + ownerId: serializer.fromJson(json['ownerId']), + type: serializer.fromJson(json['type']), + data: serializer.fromJson(json['data']), + isSaved: serializer.fromJson(json['isSaved']), + memoryAt: serializer.fromJson(json['memoryAt']), + seenAt: serializer.fromJson(json['seenAt']), + showAt: serializer.fromJson(json['showAt']), + hideAt: serializer.fromJson(json['hideAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'deletedAt': serializer.toJson(deletedAt), + 'ownerId': serializer.toJson(ownerId), + 'type': serializer.toJson(type), + 'data': serializer.toJson(data), + 'isSaved': serializer.toJson(isSaved), + 'memoryAt': serializer.toJson(memoryAt), + 'seenAt': serializer.toJson(seenAt), + 'showAt': serializer.toJson(showAt), + 'hideAt': serializer.toJson(hideAt), + }; + } + + MemoryEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + Value deletedAt = const Value.absent(), + String? ownerId, + int? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + Value seenAt = const Value.absent(), + Value showAt = const Value.absent(), + Value hideAt = const Value.absent(), + }) => MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); + MemoryEntityData copyWithCompanion(MemoryEntityCompanion data) { + return MemoryEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + type: data.type.present ? data.type.value : this.type, + data: data.data.present ? data.data.value : this.data, + isSaved: data.isSaved.present ? data.isSaved.value : this.isSaved, + memoryAt: data.memoryAt.present ? data.memoryAt.value : this.memoryAt, + seenAt: data.seenAt.present ? data.seenAt.value : this.seenAt, + showAt: data.showAt.present ? data.showAt.value : this.showAt, + hideAt: data.hideAt.present ? data.hideAt.value : this.hideAt, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.deletedAt == this.deletedAt && + other.ownerId == this.ownerId && + other.type == this.type && + other.data == this.data && + other.isSaved == this.isSaved && + other.memoryAt == this.memoryAt && + other.seenAt == this.seenAt && + other.showAt == this.showAt && + other.hideAt == this.hideAt); +} + +class MemoryEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value deletedAt; + final Value ownerId; + final Value type; + final Value data; + final Value isSaved; + final Value memoryAt; + final Value seenAt; + final Value showAt; + final Value hideAt; + const MemoryEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.type = const Value.absent(), + this.data = const Value.absent(), + this.isSaved = const Value.absent(), + this.memoryAt = const Value.absent(), + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }); + MemoryEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + required String ownerId, + required int type, + required String data, + this.isSaved = const Value.absent(), + required DateTime memoryAt, + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + type = Value(type), + data = Value(data), + memoryAt = Value(memoryAt); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? deletedAt, + Expression? ownerId, + Expression? type, + Expression? data, + Expression? isSaved, + Expression? memoryAt, + Expression? seenAt, + Expression? showAt, + Expression? hideAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (deletedAt != null) 'deleted_at': deletedAt, + if (ownerId != null) 'owner_id': ownerId, + if (type != null) 'type': type, + if (data != null) 'data': data, + if (isSaved != null) 'is_saved': isSaved, + if (memoryAt != null) 'memory_at': memoryAt, + if (seenAt != null) 'seen_at': seenAt, + if (showAt != null) 'show_at': showAt, + if (hideAt != null) 'hide_at': hideAt, + }); + } + + MemoryEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? deletedAt, + Value? ownerId, + Value? type, + Value? data, + Value? isSaved, + Value? memoryAt, + Value? seenAt, + Value? showAt, + Value? hideAt, + }) { + return MemoryEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt ?? this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt ?? this.seenAt, + showAt: showAt ?? this.showAt, + hideAt: hideAt ?? this.hideAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (data.present) { + map['data'] = Variable(data.value); + } + if (isSaved.present) { + map['is_saved'] = Variable(isSaved.value); + } + if (memoryAt.present) { + map['memory_at'] = Variable(memoryAt.value); + } + if (seenAt.present) { + map['seen_at'] = Variable(seenAt.value); + } + if (showAt.present) { + map['show_at'] = Variable(showAt.value); + } + if (hideAt.present) { + map['hide_at'] = Variable(hideAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } +} + +class MemoryAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn memoryId = GeneratedColumn( + 'memory_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, memoryId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_asset_entity'; + @override + Set get $primaryKey => {assetId, memoryId}; + @override + MemoryAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + memoryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}memory_id'], + )!, + ); + } + + @override + MemoryAssetEntity createAlias(String alias) { + return MemoryAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String memoryId; + const MemoryAssetEntityData({required this.assetId, required this.memoryId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['memory_id'] = Variable(memoryId); + return map; + } + + factory MemoryAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + memoryId: serializer.fromJson(json['memoryId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'memoryId': serializer.toJson(memoryId), + }; + } + + MemoryAssetEntityData copyWith({String? assetId, String? memoryId}) => + MemoryAssetEntityData( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + MemoryAssetEntityData copyWithCompanion(MemoryAssetEntityCompanion data) { + return MemoryAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + memoryId: data.memoryId.present ? data.memoryId.value : this.memoryId, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, memoryId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryAssetEntityData && + other.assetId == this.assetId && + other.memoryId == this.memoryId); +} + +class MemoryAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value memoryId; + const MemoryAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.memoryId = const Value.absent(), + }); + MemoryAssetEntityCompanion.insert({ + required String assetId, + required String memoryId, + }) : assetId = Value(assetId), + memoryId = Value(memoryId); + static Insertable custom({ + Expression? assetId, + Expression? memoryId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (memoryId != null) 'memory_id': memoryId, + }); + } + + MemoryAssetEntityCompanion copyWith({ + Value? assetId, + Value? memoryId, + }) { + return MemoryAssetEntityCompanion( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (memoryId.present) { + map['memory_id'] = Variable(memoryId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } +} + +class PersonEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PersonEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn faceAssetId = GeneratedColumn( + 'face_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn thumbnailPath = GeneratedColumn( + 'thumbnail_path', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); + late final GeneratedColumn isHidden = GeneratedColumn( + 'is_hidden', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); + late final GeneratedColumn color = GeneratedColumn( + 'color', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn birthDate = GeneratedColumn( + 'birth_date', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + thumbnailPath, + isFavorite, + isHidden, + color, + birthDate, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'person_entity'; + @override + Set get $primaryKey => {id}; + @override + PersonEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PersonEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + faceAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}face_asset_id'], + ), + thumbnailPath: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_path'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + isHidden: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_hidden'], + )!, + color: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}color'], + ), + birthDate: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}birth_date'], + ), + ); + } + + @override + PersonEntity createAlias(String alias) { + return PersonEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PersonEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String name; + final String? faceAssetId; + final String thumbnailPath; + final bool isFavorite; + final bool isHidden; + final String? color; + final DateTime? birthDate; + const PersonEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.thumbnailPath, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['name'] = Variable(name); + if (!nullToAbsent || faceAssetId != null) { + map['face_asset_id'] = Variable(faceAssetId); + } + map['thumbnail_path'] = Variable(thumbnailPath); + map['is_favorite'] = Variable(isFavorite); + map['is_hidden'] = Variable(isHidden); + if (!nullToAbsent || color != null) { + map['color'] = Variable(color); + } + if (!nullToAbsent || birthDate != null) { + map['birth_date'] = Variable(birthDate); + } + return map; + } + + factory PersonEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PersonEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + name: serializer.fromJson(json['name']), + faceAssetId: serializer.fromJson(json['faceAssetId']), + thumbnailPath: serializer.fromJson(json['thumbnailPath']), + isFavorite: serializer.fromJson(json['isFavorite']), + isHidden: serializer.fromJson(json['isHidden']), + color: serializer.fromJson(json['color']), + birthDate: serializer.fromJson(json['birthDate']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'name': serializer.toJson(name), + 'faceAssetId': serializer.toJson(faceAssetId), + 'thumbnailPath': serializer.toJson(thumbnailPath), + 'isFavorite': serializer.toJson(isFavorite), + 'isHidden': serializer.toJson(isHidden), + 'color': serializer.toJson(color), + 'birthDate': serializer.toJson(birthDate), + }; + } + + PersonEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + Value faceAssetId = const Value.absent(), + String? thumbnailPath, + bool? isFavorite, + bool? isHidden, + Value color = const Value.absent(), + Value birthDate = const Value.absent(), + }) => PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + thumbnailPath: thumbnailPath ?? this.thumbnailPath, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); + PersonEntityData copyWithCompanion(PersonEntityCompanion data) { + return PersonEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + name: data.name.present ? data.name.value : this.name, + faceAssetId: data.faceAssetId.present + ? data.faceAssetId.value + : this.faceAssetId, + thumbnailPath: data.thumbnailPath.present + ? data.thumbnailPath.value + : this.thumbnailPath, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, + color: data.color.present ? data.color.value : this.color, + birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, + ); + } + + @override + String toString() { + return (StringBuffer('PersonEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('thumbnailPath: $thumbnailPath, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + thumbnailPath, + isFavorite, + isHidden, + color, + birthDate, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PersonEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.name == this.name && + other.faceAssetId == this.faceAssetId && + other.thumbnailPath == this.thumbnailPath && + other.isFavorite == this.isFavorite && + other.isHidden == this.isHidden && + other.color == this.color && + other.birthDate == this.birthDate); +} + +class PersonEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value name; + final Value faceAssetId; + final Value thumbnailPath; + final Value isFavorite; + final Value isHidden; + final Value color; + final Value birthDate; + const PersonEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.name = const Value.absent(), + this.faceAssetId = const Value.absent(), + this.thumbnailPath = const Value.absent(), + this.isFavorite = const Value.absent(), + this.isHidden = const Value.absent(), + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }); + PersonEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String name, + this.faceAssetId = const Value.absent(), + required String thumbnailPath, + required bool isFavorite, + required bool isHidden, + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + name = Value(name), + thumbnailPath = Value(thumbnailPath), + isFavorite = Value(isFavorite), + isHidden = Value(isHidden); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? name, + Expression? faceAssetId, + Expression? thumbnailPath, + Expression? isFavorite, + Expression? isHidden, + Expression? color, + Expression? birthDate, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (name != null) 'name': name, + if (faceAssetId != null) 'face_asset_id': faceAssetId, + if (thumbnailPath != null) 'thumbnail_path': thumbnailPath, + if (isFavorite != null) 'is_favorite': isFavorite, + if (isHidden != null) 'is_hidden': isHidden, + if (color != null) 'color': color, + if (birthDate != null) 'birth_date': birthDate, + }); + } + + PersonEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? name, + Value? faceAssetId, + Value? thumbnailPath, + Value? isFavorite, + Value? isHidden, + Value? color, + Value? birthDate, + }) { + return PersonEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId ?? this.faceAssetId, + thumbnailPath: thumbnailPath ?? this.thumbnailPath, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color ?? this.color, + birthDate: birthDate ?? this.birthDate, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (faceAssetId.present) { + map['face_asset_id'] = Variable(faceAssetId.value); + } + if (thumbnailPath.present) { + map['thumbnail_path'] = Variable(thumbnailPath.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (isHidden.present) { + map['is_hidden'] = Variable(isHidden.value); + } + if (color.present) { + map['color'] = Variable(color.value); + } + if (birthDate.present) { + map['birth_date'] = Variable(birthDate.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PersonEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('thumbnailPath: $thumbnailPath, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } +} + +class DatabaseAtV3 extends GeneratedDatabase { + DatabaseAtV3(QueryExecutor e) : super(e); + late final UserEntity userEntity = UserEntity(this); + late final RemoteAssetEntity remoteAssetEntity = RemoteAssetEntity(this); + late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); + late final StackEntity stackEntity = StackEntity(this); + late final Index idxLocalAssetChecksum = Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + late final Index uQRemoteAssetOwnerChecksum = Index( + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', + ); + late final Index idxRemoteAssetChecksum = Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); + late final UserMetadataEntity userMetadataEntity = UserMetadataEntity(this); + late final PartnerEntity partnerEntity = PartnerEntity(this); + late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); + late final LocalAlbumAssetEntity localAlbumAssetEntity = + LocalAlbumAssetEntity(this); + late final RemoteExifEntity remoteExifEntity = RemoteExifEntity(this); + late final RemoteAlbumEntity remoteAlbumEntity = RemoteAlbumEntity(this); + late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = + RemoteAlbumAssetEntity(this); + late final RemoteAlbumUserEntity remoteAlbumUserEntity = + RemoteAlbumUserEntity(this); + late final MemoryEntity memoryEntity = MemoryEntity(this); + late final MemoryAssetEntity memoryAssetEntity = MemoryAssetEntity(this); + late final PersonEntity personEntity = PersonEntity(this); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [ + userEntity, + remoteAssetEntity, + localAssetEntity, + stackEntity, + idxLocalAssetChecksum, + uQRemoteAssetOwnerChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + localAlbumEntity, + localAlbumAssetEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + ]; + @override + int get schemaVersion => 3; + @override + DriftDatabaseOptions get options => + const DriftDatabaseOptions(storeDateTimeAsText: true); +} diff --git a/mobile/test/drift/main/generated/schema_v4.dart b/mobile/test/drift/main/generated/schema_v4.dart new file mode 100644 index 0000000000..9eabea714f --- /dev/null +++ b/mobile/test/drift/main/generated/schema_v4.dart @@ -0,0 +1,6441 @@ +// dart format width=80 +// GENERATED CODE, DO NOT EDIT BY HAND. +// ignore_for_file: type=lint +import 'package:drift/drift.dart'; + +class UserEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isAdmin = GeneratedColumn( + 'is_admin', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn email = GeneratedColumn( + 'email', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn profileImagePath = GeneratedColumn( + 'profile_image_path', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn quotaSizeInBytes = GeneratedColumn( + 'quota_size_in_bytes', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn quotaUsageInBytes = GeneratedColumn( + 'quota_usage_in_bytes', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [ + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_entity'; + @override + Set get $primaryKey => {id}; + @override + UserEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + isAdmin: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_admin'], + )!, + email: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}email'], + )!, + profileImagePath: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}profile_image_path'], + ), + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + quotaSizeInBytes: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}quota_size_in_bytes'], + ), + quotaUsageInBytes: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}quota_usage_in_bytes'], + )!, + ); + } + + @override + UserEntity createAlias(String alias) { + return UserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserEntityData extends DataClass implements Insertable { + final String id; + final String name; + final bool isAdmin; + final String email; + final String? profileImagePath; + final DateTime updatedAt; + final int? quotaSizeInBytes; + final int quotaUsageInBytes; + const UserEntityData({ + required this.id, + required this.name, + required this.isAdmin, + required this.email, + this.profileImagePath, + required this.updatedAt, + this.quotaSizeInBytes, + required this.quotaUsageInBytes, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['is_admin'] = Variable(isAdmin); + map['email'] = Variable(email); + if (!nullToAbsent || profileImagePath != null) { + map['profile_image_path'] = Variable(profileImagePath); + } + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || quotaSizeInBytes != null) { + map['quota_size_in_bytes'] = Variable(quotaSizeInBytes); + } + map['quota_usage_in_bytes'] = Variable(quotaUsageInBytes); + return map; + } + + factory UserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + isAdmin: serializer.fromJson(json['isAdmin']), + email: serializer.fromJson(json['email']), + profileImagePath: serializer.fromJson(json['profileImagePath']), + updatedAt: serializer.fromJson(json['updatedAt']), + quotaSizeInBytes: serializer.fromJson(json['quotaSizeInBytes']), + quotaUsageInBytes: serializer.fromJson(json['quotaUsageInBytes']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'isAdmin': serializer.toJson(isAdmin), + 'email': serializer.toJson(email), + 'profileImagePath': serializer.toJson(profileImagePath), + 'updatedAt': serializer.toJson(updatedAt), + 'quotaSizeInBytes': serializer.toJson(quotaSizeInBytes), + 'quotaUsageInBytes': serializer.toJson(quotaUsageInBytes), + }; + } + + UserEntityData copyWith({ + String? id, + String? name, + bool? isAdmin, + String? email, + Value profileImagePath = const Value.absent(), + DateTime? updatedAt, + Value quotaSizeInBytes = const Value.absent(), + int? quotaUsageInBytes, + }) => UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + profileImagePath: profileImagePath.present + ? profileImagePath.value + : this.profileImagePath, + updatedAt: updatedAt ?? this.updatedAt, + quotaSizeInBytes: quotaSizeInBytes.present + ? quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + ); + UserEntityData copyWithCompanion(UserEntityCompanion data) { + return UserEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + isAdmin: data.isAdmin.present ? data.isAdmin.value : this.isAdmin, + email: data.email.present ? data.email.value : this.email, + profileImagePath: data.profileImagePath.present + ? data.profileImagePath.value + : this.profileImagePath, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + quotaSizeInBytes: data.quotaSizeInBytes.present + ? data.quotaSizeInBytes.value + : this.quotaSizeInBytes, + quotaUsageInBytes: data.quotaUsageInBytes.present + ? data.quotaUsageInBytes.value + : this.quotaUsageInBytes, + ); + } + + @override + String toString() { + return (StringBuffer('UserEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isAdmin: $isAdmin, ') + ..write('email: $email, ') + ..write('profileImagePath: $profileImagePath, ') + ..write('updatedAt: $updatedAt, ') + ..write('quotaSizeInBytes: $quotaSizeInBytes, ') + ..write('quotaUsageInBytes: $quotaUsageInBytes') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + isAdmin, + email, + profileImagePath, + updatedAt, + quotaSizeInBytes, + quotaUsageInBytes, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserEntityData && + other.id == this.id && + other.name == this.name && + other.isAdmin == this.isAdmin && + other.email == this.email && + other.profileImagePath == this.profileImagePath && + other.updatedAt == this.updatedAt && + other.quotaSizeInBytes == this.quotaSizeInBytes && + other.quotaUsageInBytes == this.quotaUsageInBytes); +} + +class UserEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value isAdmin; + final Value email; + final Value profileImagePath; + final Value updatedAt; + final Value quotaSizeInBytes; + final Value quotaUsageInBytes; + const UserEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.isAdmin = const Value.absent(), + this.email = const Value.absent(), + this.profileImagePath = const Value.absent(), + this.updatedAt = const Value.absent(), + this.quotaSizeInBytes = const Value.absent(), + this.quotaUsageInBytes = const Value.absent(), + }); + UserEntityCompanion.insert({ + required String id, + required String name, + this.isAdmin = const Value.absent(), + required String email, + this.profileImagePath = const Value.absent(), + this.updatedAt = const Value.absent(), + this.quotaSizeInBytes = const Value.absent(), + this.quotaUsageInBytes = const Value.absent(), + }) : id = Value(id), + name = Value(name), + email = Value(email); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? isAdmin, + Expression? email, + Expression? profileImagePath, + Expression? updatedAt, + Expression? quotaSizeInBytes, + Expression? quotaUsageInBytes, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (isAdmin != null) 'is_admin': isAdmin, + if (email != null) 'email': email, + if (profileImagePath != null) 'profile_image_path': profileImagePath, + if (updatedAt != null) 'updated_at': updatedAt, + if (quotaSizeInBytes != null) 'quota_size_in_bytes': quotaSizeInBytes, + if (quotaUsageInBytes != null) 'quota_usage_in_bytes': quotaUsageInBytes, + }); + } + + UserEntityCompanion copyWith({ + Value? id, + Value? name, + Value? isAdmin, + Value? email, + Value? profileImagePath, + Value? updatedAt, + Value? quotaSizeInBytes, + Value? quotaUsageInBytes, + }) { + return UserEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + profileImagePath: profileImagePath ?? this.profileImagePath, + updatedAt: updatedAt ?? this.updatedAt, + quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes, + quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (isAdmin.present) { + map['is_admin'] = Variable(isAdmin.value); + } + if (email.present) { + map['email'] = Variable(email.value); + } + if (profileImagePath.present) { + map['profile_image_path'] = Variable(profileImagePath.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (quotaSizeInBytes.present) { + map['quota_size_in_bytes'] = Variable(quotaSizeInBytes.value); + } + if (quotaUsageInBytes.present) { + map['quota_usage_in_bytes'] = Variable(quotaUsageInBytes.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isAdmin: $isAdmin, ') + ..write('email: $email, ') + ..write('profileImagePath: $profileImagePath, ') + ..write('updatedAt: $updatedAt, ') + ..write('quotaSizeInBytes: $quotaSizeInBytes, ') + ..write('quotaUsageInBytes: $quotaUsageInBytes') + ..write(')')) + .toString(); + } +} + +class RemoteAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn localDateTime = + GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn thumbHash = GeneratedColumn( + 'thumb_hash', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn livePhotoVideoId = GeneratedColumn( + 'live_photo_video_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn visibility = GeneratedColumn( + 'visibility', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn stackId = GeneratedColumn( + 'stack_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAssetEntityData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + localDateTime: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}local_date_time'], + ), + thumbHash: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumb_hash'], + ), + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + livePhotoVideoId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}live_photo_video_id'], + ), + visibility: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}visibility'], + )!, + stackId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}stack_id'], + ), + ); + } + + @override + RemoteAssetEntity createAlias(String alias) { + return RemoteAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String checksum; + final bool isFavorite; + final String ownerId; + final DateTime? localDateTime; + final String? thumbHash; + final DateTime? deletedAt; + final String? livePhotoVideoId; + final int visibility; + final String? stackId; + const RemoteAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + required this.checksum, + required this.isFavorite, + required this.ownerId, + this.localDateTime, + this.thumbHash, + this.deletedAt, + this.livePhotoVideoId, + required this.visibility, + this.stackId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + map['checksum'] = Variable(checksum); + map['is_favorite'] = Variable(isFavorite); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || localDateTime != null) { + map['local_date_time'] = Variable(localDateTime); + } + if (!nullToAbsent || thumbHash != null) { + map['thumb_hash'] = Variable(thumbHash); + } + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + if (!nullToAbsent || livePhotoVideoId != null) { + map['live_photo_video_id'] = Variable(livePhotoVideoId); + } + map['visibility'] = Variable(visibility); + if (!nullToAbsent || stackId != null) { + map['stack_id'] = Variable(stackId); + } + return map; + } + + factory RemoteAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + ownerId: serializer.fromJson(json['ownerId']), + localDateTime: serializer.fromJson(json['localDateTime']), + thumbHash: serializer.fromJson(json['thumbHash']), + deletedAt: serializer.fromJson(json['deletedAt']), + livePhotoVideoId: serializer.fromJson(json['livePhotoVideoId']), + visibility: serializer.fromJson(json['visibility']), + stackId: serializer.fromJson(json['stackId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'ownerId': serializer.toJson(ownerId), + 'localDateTime': serializer.toJson(localDateTime), + 'thumbHash': serializer.toJson(thumbHash), + 'deletedAt': serializer.toJson(deletedAt), + 'livePhotoVideoId': serializer.toJson(livePhotoVideoId), + 'visibility': serializer.toJson(visibility), + 'stackId': serializer.toJson(stackId), + }; + } + + RemoteAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + String? checksum, + bool? isFavorite, + String? ownerId, + Value localDateTime = const Value.absent(), + Value thumbHash = const Value.absent(), + Value deletedAt = const Value.absent(), + Value livePhotoVideoId = const Value.absent(), + int? visibility, + Value stackId = const Value.absent(), + }) => RemoteAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime.present + ? localDateTime.value + : this.localDateTime, + thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + livePhotoVideoId: livePhotoVideoId.present + ? livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId.present ? stackId.value : this.stackId, + ); + RemoteAssetEntityData copyWithCompanion(RemoteAssetEntityCompanion data) { + return RemoteAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + localDateTime: data.localDateTime.present + ? data.localDateTime.value + : this.localDateTime, + thumbHash: data.thumbHash.present ? data.thumbHash.value : this.thumbHash, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + livePhotoVideoId: data.livePhotoVideoId.present + ? data.livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: data.visibility.present + ? data.visibility.value + : this.visibility, + stackId: data.stackId.present ? data.stackId.value : this.stackId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.ownerId == this.ownerId && + other.localDateTime == this.localDateTime && + other.thumbHash == this.thumbHash && + other.deletedAt == this.deletedAt && + other.livePhotoVideoId == this.livePhotoVideoId && + other.visibility == this.visibility && + other.stackId == this.stackId); +} + +class RemoteAssetEntityCompanion + extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value ownerId; + final Value localDateTime; + final Value thumbHash; + final Value deletedAt; + final Value livePhotoVideoId; + final Value visibility; + final Value stackId; + const RemoteAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.ownerId = const Value.absent(), + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + this.visibility = const Value.absent(), + this.stackId = const Value.absent(), + }); + RemoteAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + required String checksum, + this.isFavorite = const Value.absent(), + required String ownerId, + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + required int visibility, + this.stackId = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id), + checksum = Value(checksum), + ownerId = Value(ownerId), + visibility = Value(visibility); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? ownerId, + Expression? localDateTime, + Expression? thumbHash, + Expression? deletedAt, + Expression? livePhotoVideoId, + Expression? visibility, + Expression? stackId, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (ownerId != null) 'owner_id': ownerId, + if (localDateTime != null) 'local_date_time': localDateTime, + if (thumbHash != null) 'thumb_hash': thumbHash, + if (deletedAt != null) 'deleted_at': deletedAt, + if (livePhotoVideoId != null) 'live_photo_video_id': livePhotoVideoId, + if (visibility != null) 'visibility': visibility, + if (stackId != null) 'stack_id': stackId, + }); + } + + RemoteAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? ownerId, + Value? localDateTime, + Value? thumbHash, + Value? deletedAt, + Value? livePhotoVideoId, + Value? visibility, + Value? stackId, + }) { + return RemoteAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime ?? this.localDateTime, + thumbHash: thumbHash ?? this.thumbHash, + deletedAt: deletedAt ?? this.deletedAt, + livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId ?? this.stackId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (localDateTime.present) { + map['local_date_time'] = Variable(localDateTime.value); + } + if (thumbHash.present) { + map['thumb_hash'] = Variable(thumbHash.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (livePhotoVideoId.present) { + map['live_photo_video_id'] = Variable(livePhotoVideoId.value); + } + if (visibility.present) { + map['visibility'] = Variable(visibility.value); + } + if (stackId.present) { + map['stack_id'] = Variable(stackId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId') + ..write(')')) + .toString(); + } +} + +class StackEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + StackEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn primaryAssetId = GeneratedColumn( + 'primary_asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + primaryAssetId, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'stack_entity'; + @override + Set get $primaryKey => {id}; + @override + StackEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StackEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + primaryAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}primary_asset_id'], + )!, + ); + } + + @override + StackEntity createAlias(String alias) { + return StackEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class StackEntityData extends DataClass implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String primaryAssetId; + const StackEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['primary_asset_id'] = Variable(primaryAssetId); + return map; + } + + factory StackEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StackEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + primaryAssetId: serializer.fromJson(json['primaryAssetId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'primaryAssetId': serializer.toJson(primaryAssetId), + }; + } + + StackEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId, + }) => StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + StackEntityData copyWithCompanion(StackEntityCompanion data) { + return StackEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + primaryAssetId: data.primaryAssetId.present + ? data.primaryAssetId.value + : this.primaryAssetId, + ); + } + + @override + String toString() { + return (StringBuffer('StackEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StackEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.primaryAssetId == this.primaryAssetId); +} + +class StackEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value primaryAssetId; + const StackEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.primaryAssetId = const Value.absent(), + }); + StackEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String primaryAssetId, + }) : id = Value(id), + ownerId = Value(ownerId), + primaryAssetId = Value(primaryAssetId); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? primaryAssetId, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (primaryAssetId != null) 'primary_asset_id': primaryAssetId, + }); + } + + StackEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? primaryAssetId, + }) { + return StackEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (primaryAssetId.present) { + map['primary_asset_id'] = Variable(primaryAssetId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StackEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } +} + +class LocalAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAssetEntityData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}orientation'], + )!, + ); + } + + @override + LocalAssetEntity createAlias(String alias) { + return LocalAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String? checksum; + final bool isFavorite; + final int orientation; + const LocalAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + this.checksum, + required this.isFavorite, + required this.orientation, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + if (!nullToAbsent || checksum != null) { + map['checksum'] = Variable(checksum); + } + map['is_favorite'] = Variable(isFavorite); + map['orientation'] = Variable(orientation); + return map; + } + + factory LocalAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + orientation: serializer.fromJson(json['orientation']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'orientation': serializer.toJson(orientation), + }; + } + + LocalAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + Value checksum = const Value.absent(), + bool? isFavorite, + int? orientation, + }) => LocalAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + LocalAssetEntityData copyWithCompanion(LocalAssetEntityCompanion data) { + return LocalAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.orientation == this.orientation); +} + +class LocalAssetEntityCompanion extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value orientation; + const LocalAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }); + LocalAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? orientation, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (orientation != null) 'orientation': orientation, + }); + } + + LocalAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? orientation, + }) { + return LocalAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } +} + +class LocalAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn backupSelection = GeneratedColumn( + 'backup_selection', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn isIosSharedAlbum = GeneratedColumn( + 'is_ios_shared_album', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn marker_ = GeneratedColumn( + 'marker', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); + @override + List get $columns => [ + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + backupSelection: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}backup_selection'], + )!, + isIosSharedAlbum: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_ios_shared_album'], + )!, + marker_: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), + ); + } + + @override + LocalAlbumEntity createAlias(String alias) { + return LocalAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final DateTime updatedAt; + final int backupSelection; + final bool isIosSharedAlbum; + final bool? marker_; + const LocalAlbumEntityData({ + required this.id, + required this.name, + required this.updatedAt, + required this.backupSelection, + required this.isIosSharedAlbum, + this.marker_, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['updated_at'] = Variable(updatedAt); + map['backup_selection'] = Variable(backupSelection); + map['is_ios_shared_album'] = Variable(isIosSharedAlbum); + if (!nullToAbsent || marker_ != null) { + map['marker'] = Variable(marker_); + } + return map; + } + + factory LocalAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + updatedAt: serializer.fromJson(json['updatedAt']), + backupSelection: serializer.fromJson(json['backupSelection']), + isIosSharedAlbum: serializer.fromJson(json['isIosSharedAlbum']), + marker_: serializer.fromJson(json['marker_']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'updatedAt': serializer.toJson(updatedAt), + 'backupSelection': serializer.toJson(backupSelection), + 'isIosSharedAlbum': serializer.toJson(isIosSharedAlbum), + 'marker_': serializer.toJson(marker_), + }; + } + + LocalAlbumEntityData copyWith({ + String? id, + String? name, + DateTime? updatedAt, + int? backupSelection, + bool? isIosSharedAlbum, + Value marker_ = const Value.absent(), + }) => LocalAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_.present ? marker_.value : this.marker_, + ); + LocalAlbumEntityData copyWithCompanion(LocalAlbumEntityCompanion data) { + return LocalAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + backupSelection: data.backupSelection.present + ? data.backupSelection.value + : this.backupSelection, + isIosSharedAlbum: data.isIosSharedAlbum.present + ? data.isIosSharedAlbum.value + : this.isIosSharedAlbum, + marker_: data.marker_.present ? data.marker_.value : this.marker_, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.updatedAt == this.updatedAt && + other.backupSelection == this.backupSelection && + other.isIosSharedAlbum == this.isIosSharedAlbum && + other.marker_ == this.marker_); +} + +class LocalAlbumEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value updatedAt; + final Value backupSelection; + final Value isIosSharedAlbum; + final Value marker_; + const LocalAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.updatedAt = const Value.absent(), + this.backupSelection = const Value.absent(), + this.isIosSharedAlbum = const Value.absent(), + this.marker_ = const Value.absent(), + }); + LocalAlbumEntityCompanion.insert({ + required String id, + required String name, + this.updatedAt = const Value.absent(), + required int backupSelection, + this.isIosSharedAlbum = const Value.absent(), + this.marker_ = const Value.absent(), + }) : id = Value(id), + name = Value(name), + backupSelection = Value(backupSelection); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? updatedAt, + Expression? backupSelection, + Expression? isIosSharedAlbum, + Expression? marker_, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (updatedAt != null) 'updated_at': updatedAt, + if (backupSelection != null) 'backup_selection': backupSelection, + if (isIosSharedAlbum != null) 'is_ios_shared_album': isIosSharedAlbum, + if (marker_ != null) 'marker': marker_, + }); + } + + LocalAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? updatedAt, + Value? backupSelection, + Value? isIosSharedAlbum, + Value? marker_, + }) { + return LocalAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_ ?? this.marker_, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (backupSelection.present) { + map['backup_selection'] = Variable(backupSelection.value); + } + if (isIosSharedAlbum.present) { + map['is_ios_shared_album'] = Variable(isIosSharedAlbum.value); + } + if (marker_.present) { + map['marker'] = Variable(marker_.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } +} + +class LocalAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + LocalAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + ); + } + + @override + LocalAlbumAssetEntity createAlias(String alias) { + return LocalAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const LocalAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory LocalAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + LocalAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + LocalAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + LocalAlbumAssetEntityData copyWithCompanion( + LocalAlbumAssetEntityCompanion data, + ) { + return LocalAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class LocalAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const LocalAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + LocalAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + LocalAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { + return LocalAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class UserMetadataEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserMetadataEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn key = GeneratedColumn( + 'key', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn value = GeneratedColumn( + 'value', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + ); + @override + List get $columns => [userId, key, value]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_metadata_entity'; + @override + Set get $primaryKey => {userId, key}; + @override + UserMetadataEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserMetadataEntityData( + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + key: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}key'], + )!, + value: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}value'], + )!, + ); + } + + @override + UserMetadataEntity createAlias(String alias) { + return UserMetadataEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserMetadataEntityData extends DataClass + implements Insertable { + final String userId; + final int key; + final Uint8List value; + const UserMetadataEntityData({ + required this.userId, + required this.key, + required this.value, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['user_id'] = Variable(userId); + map['key'] = Variable(key); + map['value'] = Variable(value); + return map; + } + + factory UserMetadataEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserMetadataEntityData( + userId: serializer.fromJson(json['userId']), + key: serializer.fromJson(json['key']), + value: serializer.fromJson(json['value']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'userId': serializer.toJson(userId), + 'key': serializer.toJson(key), + 'value': serializer.toJson(value), + }; + } + + UserMetadataEntityData copyWith({ + String? userId, + int? key, + Uint8List? value, + }) => UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { + return UserMetadataEntityData( + userId: data.userId.present ? data.userId.value : this.userId, + key: data.key.present ? data.key.value : this.key, + value: data.value.present ? data.value.value : this.value, + ); + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityData(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(userId, key, $driftBlobEquality.hash(value)); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserMetadataEntityData && + other.userId == this.userId && + other.key == this.key && + $driftBlobEquality.equals(other.value, this.value)); +} + +class UserMetadataEntityCompanion + extends UpdateCompanion { + final Value userId; + final Value key; + final Value value; + const UserMetadataEntityCompanion({ + this.userId = const Value.absent(), + this.key = const Value.absent(), + this.value = const Value.absent(), + }); + UserMetadataEntityCompanion.insert({ + required String userId, + required int key, + required Uint8List value, + }) : userId = Value(userId), + key = Value(key), + value = Value(value); + static Insertable custom({ + Expression? userId, + Expression? key, + Expression? value, + }) { + return RawValuesInsertable({ + if (userId != null) 'user_id': userId, + if (key != null) 'key': key, + if (value != null) 'value': value, + }); + } + + UserMetadataEntityCompanion copyWith({ + Value? userId, + Value? key, + Value? value, + }) { + return UserMetadataEntityCompanion( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (key.present) { + map['key'] = Variable(key.value); + } + if (value.present) { + map['value'] = Variable(value.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityCompanion(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } +} + +class PartnerEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PartnerEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn sharedById = GeneratedColumn( + 'shared_by_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn sharedWithId = GeneratedColumn( + 'shared_with_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn inTimeline = GeneratedColumn( + 'in_timeline', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [sharedById, sharedWithId, inTimeline]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'partner_entity'; + @override + Set get $primaryKey => {sharedById, sharedWithId}; + @override + PartnerEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PartnerEntityData( + sharedById: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_by_id'], + )!, + sharedWithId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_with_id'], + )!, + inTimeline: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}in_timeline'], + )!, + ); + } + + @override + PartnerEntity createAlias(String alias) { + return PartnerEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PartnerEntityData extends DataClass + implements Insertable { + final String sharedById; + final String sharedWithId; + final bool inTimeline; + const PartnerEntityData({ + required this.sharedById, + required this.sharedWithId, + required this.inTimeline, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['shared_by_id'] = Variable(sharedById); + map['shared_with_id'] = Variable(sharedWithId); + map['in_timeline'] = Variable(inTimeline); + return map; + } + + factory PartnerEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PartnerEntityData( + sharedById: serializer.fromJson(json['sharedById']), + sharedWithId: serializer.fromJson(json['sharedWithId']), + inTimeline: serializer.fromJson(json['inTimeline']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'sharedById': serializer.toJson(sharedById), + 'sharedWithId': serializer.toJson(sharedWithId), + 'inTimeline': serializer.toJson(inTimeline), + }; + } + + PartnerEntityData copyWith({ + String? sharedById, + String? sharedWithId, + bool? inTimeline, + }) => PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { + return PartnerEntityData( + sharedById: data.sharedById.present + ? data.sharedById.value + : this.sharedById, + sharedWithId: data.sharedWithId.present + ? data.sharedWithId.value + : this.sharedWithId, + inTimeline: data.inTimeline.present + ? data.inTimeline.value + : this.inTimeline, + ); + } + + @override + String toString() { + return (StringBuffer('PartnerEntityData(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(sharedById, sharedWithId, inTimeline); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PartnerEntityData && + other.sharedById == this.sharedById && + other.sharedWithId == this.sharedWithId && + other.inTimeline == this.inTimeline); +} + +class PartnerEntityCompanion extends UpdateCompanion { + final Value sharedById; + final Value sharedWithId; + final Value inTimeline; + const PartnerEntityCompanion({ + this.sharedById = const Value.absent(), + this.sharedWithId = const Value.absent(), + this.inTimeline = const Value.absent(), + }); + PartnerEntityCompanion.insert({ + required String sharedById, + required String sharedWithId, + this.inTimeline = const Value.absent(), + }) : sharedById = Value(sharedById), + sharedWithId = Value(sharedWithId); + static Insertable custom({ + Expression? sharedById, + Expression? sharedWithId, + Expression? inTimeline, + }) { + return RawValuesInsertable({ + if (sharedById != null) 'shared_by_id': sharedById, + if (sharedWithId != null) 'shared_with_id': sharedWithId, + if (inTimeline != null) 'in_timeline': inTimeline, + }); + } + + PartnerEntityCompanion copyWith({ + Value? sharedById, + Value? sharedWithId, + Value? inTimeline, + }) { + return PartnerEntityCompanion( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (sharedById.present) { + map['shared_by_id'] = Variable(sharedById.value); + } + if (sharedWithId.present) { + map['shared_with_id'] = Variable(sharedWithId.value); + } + if (inTimeline.present) { + map['in_timeline'] = Variable(inTimeline.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PartnerEntityCompanion(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } +} + +class RemoteExifEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteExifEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn city = GeneratedColumn( + 'city', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn state = GeneratedColumn( + 'state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn country = GeneratedColumn( + 'country', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn dateTimeOriginal = + GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn exposureTime = GeneratedColumn( + 'exposure_time', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn fNumber = GeneratedColumn( + 'f_number', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn fileSize = GeneratedColumn( + 'file_size', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn focalLength = GeneratedColumn( + 'focal_length', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn latitude = GeneratedColumn( + 'latitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn longitude = GeneratedColumn( + 'longitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn iso = GeneratedColumn( + 'iso', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn make = GeneratedColumn( + 'make', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn model = GeneratedColumn( + 'model', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn lens = GeneratedColumn( + 'lens', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn timeZone = GeneratedColumn( + 'time_zone', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn rating = GeneratedColumn( + 'rating', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn projectionType = GeneratedColumn( + 'projection_type', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_exif_entity'; + @override + Set get $primaryKey => {assetId}; + @override + RemoteExifEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteExifEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + city: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}city'], + ), + state: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}state'], + ), + country: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}country'], + ), + dateTimeOriginal: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}date_time_original'], + ), + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + exposureTime: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}exposure_time'], + ), + fNumber: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}f_number'], + ), + fileSize: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}file_size'], + ), + focalLength: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}focal_length'], + ), + latitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}latitude'], + ), + longitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}longitude'], + ), + iso: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}iso'], + ), + make: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}make'], + ), + model: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}model'], + ), + lens: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}lens'], + ), + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}orientation'], + ), + timeZone: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}time_zone'], + ), + rating: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}rating'], + ), + projectionType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}projection_type'], + ), + ); + } + + @override + RemoteExifEntity createAlias(String alias) { + return RemoteExifEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteExifEntityData extends DataClass + implements Insertable { + final String assetId; + final String? city; + final String? state; + final String? country; + final DateTime? dateTimeOriginal; + final String? description; + final int? height; + final int? width; + final String? exposureTime; + final double? fNumber; + final int? fileSize; + final double? focalLength; + final double? latitude; + final double? longitude; + final int? iso; + final String? make; + final String? model; + final String? lens; + final String? orientation; + final String? timeZone; + final int? rating; + final String? projectionType; + const RemoteExifEntityData({ + required this.assetId, + this.city, + this.state, + this.country, + this.dateTimeOriginal, + this.description, + this.height, + this.width, + this.exposureTime, + this.fNumber, + this.fileSize, + this.focalLength, + this.latitude, + this.longitude, + this.iso, + this.make, + this.model, + this.lens, + this.orientation, + this.timeZone, + this.rating, + this.projectionType, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || city != null) { + map['city'] = Variable(city); + } + if (!nullToAbsent || state != null) { + map['state'] = Variable(state); + } + if (!nullToAbsent || country != null) { + map['country'] = Variable(country); + } + if (!nullToAbsent || dateTimeOriginal != null) { + map['date_time_original'] = Variable(dateTimeOriginal); + } + if (!nullToAbsent || description != null) { + map['description'] = Variable(description); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || exposureTime != null) { + map['exposure_time'] = Variable(exposureTime); + } + if (!nullToAbsent || fNumber != null) { + map['f_number'] = Variable(fNumber); + } + if (!nullToAbsent || fileSize != null) { + map['file_size'] = Variable(fileSize); + } + if (!nullToAbsent || focalLength != null) { + map['focal_length'] = Variable(focalLength); + } + if (!nullToAbsent || latitude != null) { + map['latitude'] = Variable(latitude); + } + if (!nullToAbsent || longitude != null) { + map['longitude'] = Variable(longitude); + } + if (!nullToAbsent || iso != null) { + map['iso'] = Variable(iso); + } + if (!nullToAbsent || make != null) { + map['make'] = Variable(make); + } + if (!nullToAbsent || model != null) { + map['model'] = Variable(model); + } + if (!nullToAbsent || lens != null) { + map['lens'] = Variable(lens); + } + if (!nullToAbsent || orientation != null) { + map['orientation'] = Variable(orientation); + } + if (!nullToAbsent || timeZone != null) { + map['time_zone'] = Variable(timeZone); + } + if (!nullToAbsent || rating != null) { + map['rating'] = Variable(rating); + } + if (!nullToAbsent || projectionType != null) { + map['projection_type'] = Variable(projectionType); + } + return map; + } + + factory RemoteExifEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteExifEntityData( + assetId: serializer.fromJson(json['assetId']), + city: serializer.fromJson(json['city']), + state: serializer.fromJson(json['state']), + country: serializer.fromJson(json['country']), + dateTimeOriginal: serializer.fromJson( + json['dateTimeOriginal'], + ), + description: serializer.fromJson(json['description']), + height: serializer.fromJson(json['height']), + width: serializer.fromJson(json['width']), + exposureTime: serializer.fromJson(json['exposureTime']), + fNumber: serializer.fromJson(json['fNumber']), + fileSize: serializer.fromJson(json['fileSize']), + focalLength: serializer.fromJson(json['focalLength']), + latitude: serializer.fromJson(json['latitude']), + longitude: serializer.fromJson(json['longitude']), + iso: serializer.fromJson(json['iso']), + make: serializer.fromJson(json['make']), + model: serializer.fromJson(json['model']), + lens: serializer.fromJson(json['lens']), + orientation: serializer.fromJson(json['orientation']), + timeZone: serializer.fromJson(json['timeZone']), + rating: serializer.fromJson(json['rating']), + projectionType: serializer.fromJson(json['projectionType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'city': serializer.toJson(city), + 'state': serializer.toJson(state), + 'country': serializer.toJson(country), + 'dateTimeOriginal': serializer.toJson(dateTimeOriginal), + 'description': serializer.toJson(description), + 'height': serializer.toJson(height), + 'width': serializer.toJson(width), + 'exposureTime': serializer.toJson(exposureTime), + 'fNumber': serializer.toJson(fNumber), + 'fileSize': serializer.toJson(fileSize), + 'focalLength': serializer.toJson(focalLength), + 'latitude': serializer.toJson(latitude), + 'longitude': serializer.toJson(longitude), + 'iso': serializer.toJson(iso), + 'make': serializer.toJson(make), + 'model': serializer.toJson(model), + 'lens': serializer.toJson(lens), + 'orientation': serializer.toJson(orientation), + 'timeZone': serializer.toJson(timeZone), + 'rating': serializer.toJson(rating), + 'projectionType': serializer.toJson(projectionType), + }; + } + + RemoteExifEntityData copyWith({ + String? assetId, + Value city = const Value.absent(), + Value state = const Value.absent(), + Value country = const Value.absent(), + Value dateTimeOriginal = const Value.absent(), + Value description = const Value.absent(), + Value height = const Value.absent(), + Value width = const Value.absent(), + Value exposureTime = const Value.absent(), + Value fNumber = const Value.absent(), + Value fileSize = const Value.absent(), + Value focalLength = const Value.absent(), + Value latitude = const Value.absent(), + Value longitude = const Value.absent(), + Value iso = const Value.absent(), + Value make = const Value.absent(), + Value model = const Value.absent(), + Value lens = const Value.absent(), + Value orientation = const Value.absent(), + Value timeZone = const Value.absent(), + Value rating = const Value.absent(), + Value projectionType = const Value.absent(), + }) => RemoteExifEntityData( + assetId: assetId ?? this.assetId, + city: city.present ? city.value : this.city, + state: state.present ? state.value : this.state, + country: country.present ? country.value : this.country, + dateTimeOriginal: dateTimeOriginal.present + ? dateTimeOriginal.value + : this.dateTimeOriginal, + description: description.present ? description.value : this.description, + height: height.present ? height.value : this.height, + width: width.present ? width.value : this.width, + exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, + fNumber: fNumber.present ? fNumber.value : this.fNumber, + fileSize: fileSize.present ? fileSize.value : this.fileSize, + focalLength: focalLength.present ? focalLength.value : this.focalLength, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + iso: iso.present ? iso.value : this.iso, + make: make.present ? make.value : this.make, + model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, + orientation: orientation.present ? orientation.value : this.orientation, + timeZone: timeZone.present ? timeZone.value : this.timeZone, + rating: rating.present ? rating.value : this.rating, + projectionType: projectionType.present + ? projectionType.value + : this.projectionType, + ); + RemoteExifEntityData copyWithCompanion(RemoteExifEntityCompanion data) { + return RemoteExifEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + city: data.city.present ? data.city.value : this.city, + state: data.state.present ? data.state.value : this.state, + country: data.country.present ? data.country.value : this.country, + dateTimeOriginal: data.dateTimeOriginal.present + ? data.dateTimeOriginal.value + : this.dateTimeOriginal, + description: data.description.present + ? data.description.value + : this.description, + height: data.height.present ? data.height.value : this.height, + width: data.width.present ? data.width.value : this.width, + exposureTime: data.exposureTime.present + ? data.exposureTime.value + : this.exposureTime, + fNumber: data.fNumber.present ? data.fNumber.value : this.fNumber, + fileSize: data.fileSize.present ? data.fileSize.value : this.fileSize, + focalLength: data.focalLength.present + ? data.focalLength.value + : this.focalLength, + latitude: data.latitude.present ? data.latitude.value : this.latitude, + longitude: data.longitude.present ? data.longitude.value : this.longitude, + iso: data.iso.present ? data.iso.value : this.iso, + make: data.make.present ? data.make.value : this.make, + model: data.model.present ? data.model.value : this.model, + lens: data.lens.present ? data.lens.value : this.lens, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, + timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, + rating: data.rating.present ? data.rating.value : this.rating, + projectionType: data.projectionType.present + ? data.projectionType.value + : this.projectionType, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityData(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hashAll([ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteExifEntityData && + other.assetId == this.assetId && + other.city == this.city && + other.state == this.state && + other.country == this.country && + other.dateTimeOriginal == this.dateTimeOriginal && + other.description == this.description && + other.height == this.height && + other.width == this.width && + other.exposureTime == this.exposureTime && + other.fNumber == this.fNumber && + other.fileSize == this.fileSize && + other.focalLength == this.focalLength && + other.latitude == this.latitude && + other.longitude == this.longitude && + other.iso == this.iso && + other.make == this.make && + other.model == this.model && + other.lens == this.lens && + other.orientation == this.orientation && + other.timeZone == this.timeZone && + other.rating == this.rating && + other.projectionType == this.projectionType); +} + +class RemoteExifEntityCompanion extends UpdateCompanion { + final Value assetId; + final Value city; + final Value state; + final Value country; + final Value dateTimeOriginal; + final Value description; + final Value height; + final Value width; + final Value exposureTime; + final Value fNumber; + final Value fileSize; + final Value focalLength; + final Value latitude; + final Value longitude; + final Value iso; + final Value make; + final Value model; + final Value lens; + final Value orientation; + final Value timeZone; + final Value rating; + final Value projectionType; + const RemoteExifEntityCompanion({ + this.assetId = const Value.absent(), + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }); + RemoteExifEntityCompanion.insert({ + required String assetId, + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }) : assetId = Value(assetId); + static Insertable custom({ + Expression? assetId, + Expression? city, + Expression? state, + Expression? country, + Expression? dateTimeOriginal, + Expression? description, + Expression? height, + Expression? width, + Expression? exposureTime, + Expression? fNumber, + Expression? fileSize, + Expression? focalLength, + Expression? latitude, + Expression? longitude, + Expression? iso, + Expression? make, + Expression? model, + Expression? lens, + Expression? orientation, + Expression? timeZone, + Expression? rating, + Expression? projectionType, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (city != null) 'city': city, + if (state != null) 'state': state, + if (country != null) 'country': country, + if (dateTimeOriginal != null) 'date_time_original': dateTimeOriginal, + if (description != null) 'description': description, + if (height != null) 'height': height, + if (width != null) 'width': width, + if (exposureTime != null) 'exposure_time': exposureTime, + if (fNumber != null) 'f_number': fNumber, + if (fileSize != null) 'file_size': fileSize, + if (focalLength != null) 'focal_length': focalLength, + if (latitude != null) 'latitude': latitude, + if (longitude != null) 'longitude': longitude, + if (iso != null) 'iso': iso, + if (make != null) 'make': make, + if (model != null) 'model': model, + if (lens != null) 'lens': lens, + if (orientation != null) 'orientation': orientation, + if (timeZone != null) 'time_zone': timeZone, + if (rating != null) 'rating': rating, + if (projectionType != null) 'projection_type': projectionType, + }); + } + + RemoteExifEntityCompanion copyWith({ + Value? assetId, + Value? city, + Value? state, + Value? country, + Value? dateTimeOriginal, + Value? description, + Value? height, + Value? width, + Value? exposureTime, + Value? fNumber, + Value? fileSize, + Value? focalLength, + Value? latitude, + Value? longitude, + Value? iso, + Value? make, + Value? model, + Value? lens, + Value? orientation, + Value? timeZone, + Value? rating, + Value? projectionType, + }) { + return RemoteExifEntityCompanion( + assetId: assetId ?? this.assetId, + city: city ?? this.city, + state: state ?? this.state, + country: country ?? this.country, + dateTimeOriginal: dateTimeOriginal ?? this.dateTimeOriginal, + description: description ?? this.description, + height: height ?? this.height, + width: width ?? this.width, + exposureTime: exposureTime ?? this.exposureTime, + fNumber: fNumber ?? this.fNumber, + fileSize: fileSize ?? this.fileSize, + focalLength: focalLength ?? this.focalLength, + latitude: latitude ?? this.latitude, + longitude: longitude ?? this.longitude, + iso: iso ?? this.iso, + make: make ?? this.make, + model: model ?? this.model, + lens: lens ?? this.lens, + orientation: orientation ?? this.orientation, + timeZone: timeZone ?? this.timeZone, + rating: rating ?? this.rating, + projectionType: projectionType ?? this.projectionType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (city.present) { + map['city'] = Variable(city.value); + } + if (state.present) { + map['state'] = Variable(state.value); + } + if (country.present) { + map['country'] = Variable(country.value); + } + if (dateTimeOriginal.present) { + map['date_time_original'] = Variable(dateTimeOriginal.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (exposureTime.present) { + map['exposure_time'] = Variable(exposureTime.value); + } + if (fNumber.present) { + map['f_number'] = Variable(fNumber.value); + } + if (fileSize.present) { + map['file_size'] = Variable(fileSize.value); + } + if (focalLength.present) { + map['focal_length'] = Variable(focalLength.value); + } + if (latitude.present) { + map['latitude'] = Variable(latitude.value); + } + if (longitude.present) { + map['longitude'] = Variable(longitude.value); + } + if (iso.present) { + map['iso'] = Variable(iso.value); + } + if (make.present) { + map['make'] = Variable(make.value); + } + if (model.present) { + map['model'] = Variable(model.value); + } + if (lens.present) { + map['lens'] = Variable(lens.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + if (timeZone.present) { + map['time_zone'] = Variable(timeZone.value); + } + if (rating.present) { + map['rating'] = Variable(rating.value); + } + if (projectionType.present) { + map['projection_type'] = Variable(projectionType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const CustomExpression('\'\''), + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn thumbnailAssetId = GeneratedColumn( + 'thumbnail_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn isActivityEnabled = GeneratedColumn( + 'is_activity_enabled', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const CustomExpression('1'), + ); + late final GeneratedColumn order = GeneratedColumn( + 'order', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + thumbnailAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_asset_id'], + ), + isActivityEnabled: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_activity_enabled'], + )!, + order: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}order'], + )!, + ); + } + + @override + RemoteAlbumEntity createAlias(String alias) { + return RemoteAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final String description; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String? thumbnailAssetId; + final bool isActivityEnabled; + final int order; + const RemoteAlbumEntityData({ + required this.id, + required this.name, + required this.description, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + this.thumbnailAssetId, + required this.isActivityEnabled, + required this.order, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['description'] = Variable(description); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || thumbnailAssetId != null) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId); + } + map['is_activity_enabled'] = Variable(isActivityEnabled); + map['order'] = Variable(order); + return map; + } + + factory RemoteAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + description: serializer.fromJson(json['description']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + thumbnailAssetId: serializer.fromJson(json['thumbnailAssetId']), + isActivityEnabled: serializer.fromJson(json['isActivityEnabled']), + order: serializer.fromJson(json['order']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'description': serializer.toJson(description), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'thumbnailAssetId': serializer.toJson(thumbnailAssetId), + 'isActivityEnabled': serializer.toJson(isActivityEnabled), + 'order': serializer.toJson(order), + }; + } + + RemoteAlbumEntityData copyWith({ + String? id, + String? name, + String? description, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + Value thumbnailAssetId = const Value.absent(), + bool? isActivityEnabled, + int? order, + }) => RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId.present + ? thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + RemoteAlbumEntityData copyWithCompanion(RemoteAlbumEntityCompanion data) { + return RemoteAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + description: data.description.present + ? data.description.value + : this.description, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + thumbnailAssetId: data.thumbnailAssetId.present + ? data.thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: data.isActivityEnabled.present + ? data.isActivityEnabled.value + : this.isActivityEnabled, + order: data.order.present ? data.order.value : this.order, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.description == this.description && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.thumbnailAssetId == this.thumbnailAssetId && + other.isActivityEnabled == this.isActivityEnabled && + other.order == this.order); +} + +class RemoteAlbumEntityCompanion + extends UpdateCompanion { + final Value id; + final Value name; + final Value description; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value thumbnailAssetId; + final Value isActivityEnabled; + final Value order; + const RemoteAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + this.order = const Value.absent(), + }); + RemoteAlbumEntityCompanion.insert({ + required String id, + required String name, + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + required int order, + }) : id = Value(id), + name = Value(name), + ownerId = Value(ownerId), + order = Value(order); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? description, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? thumbnailAssetId, + Expression? isActivityEnabled, + Expression? order, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (description != null) 'description': description, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (thumbnailAssetId != null) 'thumbnail_asset_id': thumbnailAssetId, + if (isActivityEnabled != null) 'is_activity_enabled': isActivityEnabled, + if (order != null) 'order': order, + }); + } + + RemoteAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? description, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? thumbnailAssetId, + Value? isActivityEnabled, + Value? order, + }) { + return RemoteAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId ?? this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (thumbnailAssetId.present) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId.value); + } + if (isActivityEnabled.present) { + map['is_activity_enabled'] = Variable(isActivityEnabled.value); + } + if (order.present) { + map['order'] = Variable(order.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + RemoteAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + ); + } + + @override + RemoteAlbumAssetEntity createAlias(String alias) { + return RemoteAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const RemoteAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory RemoteAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + RemoteAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + RemoteAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + RemoteAlbumAssetEntityData copyWithCompanion( + RemoteAlbumAssetEntityCompanion data, + ) { + return RemoteAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class RemoteAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const RemoteAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + RemoteAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + RemoteAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { + return RemoteAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumUserEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumUserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn role = GeneratedColumn( + 'role', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [albumId, userId, role]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_user_entity'; + @override + Set get $primaryKey => {albumId, userId}; + @override + RemoteAlbumUserEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumUserEntityData( + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + role: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}role'], + )!, + ); + } + + @override + RemoteAlbumUserEntity createAlias(String alias) { + return RemoteAlbumUserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumUserEntityData extends DataClass + implements Insertable { + final String albumId; + final String userId; + final int role; + const RemoteAlbumUserEntityData({ + required this.albumId, + required this.userId, + required this.role, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['album_id'] = Variable(albumId); + map['user_id'] = Variable(userId); + map['role'] = Variable(role); + return map; + } + + factory RemoteAlbumUserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumUserEntityData( + albumId: serializer.fromJson(json['albumId']), + userId: serializer.fromJson(json['userId']), + role: serializer.fromJson(json['role']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'albumId': serializer.toJson(albumId), + 'userId': serializer.toJson(userId), + 'role': serializer.toJson(role), + }; + } + + RemoteAlbumUserEntityData copyWith({ + String? albumId, + String? userId, + int? role, + }) => RemoteAlbumUserEntityData( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + RemoteAlbumUserEntityData copyWithCompanion( + RemoteAlbumUserEntityCompanion data, + ) { + return RemoteAlbumUserEntityData( + albumId: data.albumId.present ? data.albumId.value : this.albumId, + userId: data.userId.present ? data.userId.value : this.userId, + role: data.role.present ? data.role.value : this.role, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityData(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(albumId, userId, role); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumUserEntityData && + other.albumId == this.albumId && + other.userId == this.userId && + other.role == this.role); +} + +class RemoteAlbumUserEntityCompanion + extends UpdateCompanion { + final Value albumId; + final Value userId; + final Value role; + const RemoteAlbumUserEntityCompanion({ + this.albumId = const Value.absent(), + this.userId = const Value.absent(), + this.role = const Value.absent(), + }); + RemoteAlbumUserEntityCompanion.insert({ + required String albumId, + required String userId, + required int role, + }) : albumId = Value(albumId), + userId = Value(userId), + role = Value(role); + static Insertable custom({ + Expression? albumId, + Expression? userId, + Expression? role, + }) { + return RawValuesInsertable({ + if (albumId != null) 'album_id': albumId, + if (userId != null) 'user_id': userId, + if (role != null) 'role': role, + }); + } + + RemoteAlbumUserEntityCompanion copyWith({ + Value? albumId, + Value? userId, + Value? role, + }) { + return RemoteAlbumUserEntityCompanion( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (role.present) { + map['role'] = Variable(role.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityCompanion(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } +} + +class MemoryEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn data = GeneratedColumn( + 'data', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isSaved = GeneratedColumn( + 'is_saved', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn memoryAt = GeneratedColumn( + 'memory_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: true, + ); + late final GeneratedColumn seenAt = GeneratedColumn( + 'seen_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn showAt = GeneratedColumn( + 'show_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn hideAt = GeneratedColumn( + 'hide_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_entity'; + @override + Set get $primaryKey => {id}; + @override + MemoryEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + data: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}data'], + )!, + isSaved: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_saved'], + )!, + memoryAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}memory_at'], + )!, + seenAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}seen_at'], + ), + showAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}show_at'], + ), + hideAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}hide_at'], + ), + ); + } + + @override + MemoryEntity createAlias(String alias) { + return MemoryEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final DateTime? deletedAt; + final String ownerId; + final int type; + final String data; + final bool isSaved; + final DateTime memoryAt; + final DateTime? seenAt; + final DateTime? showAt; + final DateTime? hideAt; + const MemoryEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + map['owner_id'] = Variable(ownerId); + map['type'] = Variable(type); + map['data'] = Variable(data); + map['is_saved'] = Variable(isSaved); + map['memory_at'] = Variable(memoryAt); + if (!nullToAbsent || seenAt != null) { + map['seen_at'] = Variable(seenAt); + } + if (!nullToAbsent || showAt != null) { + map['show_at'] = Variable(showAt); + } + if (!nullToAbsent || hideAt != null) { + map['hide_at'] = Variable(hideAt); + } + return map; + } + + factory MemoryEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + deletedAt: serializer.fromJson(json['deletedAt']), + ownerId: serializer.fromJson(json['ownerId']), + type: serializer.fromJson(json['type']), + data: serializer.fromJson(json['data']), + isSaved: serializer.fromJson(json['isSaved']), + memoryAt: serializer.fromJson(json['memoryAt']), + seenAt: serializer.fromJson(json['seenAt']), + showAt: serializer.fromJson(json['showAt']), + hideAt: serializer.fromJson(json['hideAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'deletedAt': serializer.toJson(deletedAt), + 'ownerId': serializer.toJson(ownerId), + 'type': serializer.toJson(type), + 'data': serializer.toJson(data), + 'isSaved': serializer.toJson(isSaved), + 'memoryAt': serializer.toJson(memoryAt), + 'seenAt': serializer.toJson(seenAt), + 'showAt': serializer.toJson(showAt), + 'hideAt': serializer.toJson(hideAt), + }; + } + + MemoryEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + Value deletedAt = const Value.absent(), + String? ownerId, + int? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + Value seenAt = const Value.absent(), + Value showAt = const Value.absent(), + Value hideAt = const Value.absent(), + }) => MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); + MemoryEntityData copyWithCompanion(MemoryEntityCompanion data) { + return MemoryEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + type: data.type.present ? data.type.value : this.type, + data: data.data.present ? data.data.value : this.data, + isSaved: data.isSaved.present ? data.isSaved.value : this.isSaved, + memoryAt: data.memoryAt.present ? data.memoryAt.value : this.memoryAt, + seenAt: data.seenAt.present ? data.seenAt.value : this.seenAt, + showAt: data.showAt.present ? data.showAt.value : this.showAt, + hideAt: data.hideAt.present ? data.hideAt.value : this.hideAt, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.deletedAt == this.deletedAt && + other.ownerId == this.ownerId && + other.type == this.type && + other.data == this.data && + other.isSaved == this.isSaved && + other.memoryAt == this.memoryAt && + other.seenAt == this.seenAt && + other.showAt == this.showAt && + other.hideAt == this.hideAt); +} + +class MemoryEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value deletedAt; + final Value ownerId; + final Value type; + final Value data; + final Value isSaved; + final Value memoryAt; + final Value seenAt; + final Value showAt; + final Value hideAt; + const MemoryEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.type = const Value.absent(), + this.data = const Value.absent(), + this.isSaved = const Value.absent(), + this.memoryAt = const Value.absent(), + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }); + MemoryEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + required String ownerId, + required int type, + required String data, + this.isSaved = const Value.absent(), + required DateTime memoryAt, + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + type = Value(type), + data = Value(data), + memoryAt = Value(memoryAt); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? deletedAt, + Expression? ownerId, + Expression? type, + Expression? data, + Expression? isSaved, + Expression? memoryAt, + Expression? seenAt, + Expression? showAt, + Expression? hideAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (deletedAt != null) 'deleted_at': deletedAt, + if (ownerId != null) 'owner_id': ownerId, + if (type != null) 'type': type, + if (data != null) 'data': data, + if (isSaved != null) 'is_saved': isSaved, + if (memoryAt != null) 'memory_at': memoryAt, + if (seenAt != null) 'seen_at': seenAt, + if (showAt != null) 'show_at': showAt, + if (hideAt != null) 'hide_at': hideAt, + }); + } + + MemoryEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? deletedAt, + Value? ownerId, + Value? type, + Value? data, + Value? isSaved, + Value? memoryAt, + Value? seenAt, + Value? showAt, + Value? hideAt, + }) { + return MemoryEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt ?? this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt ?? this.seenAt, + showAt: showAt ?? this.showAt, + hideAt: hideAt ?? this.hideAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (data.present) { + map['data'] = Variable(data.value); + } + if (isSaved.present) { + map['is_saved'] = Variable(isSaved.value); + } + if (memoryAt.present) { + map['memory_at'] = Variable(memoryAt.value); + } + if (seenAt.present) { + map['seen_at'] = Variable(seenAt.value); + } + if (showAt.present) { + map['show_at'] = Variable(showAt.value); + } + if (hideAt.present) { + map['hide_at'] = Variable(hideAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } +} + +class MemoryAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn memoryId = GeneratedColumn( + 'memory_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, memoryId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_asset_entity'; + @override + Set get $primaryKey => {assetId, memoryId}; + @override + MemoryAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + memoryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}memory_id'], + )!, + ); + } + + @override + MemoryAssetEntity createAlias(String alias) { + return MemoryAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String memoryId; + const MemoryAssetEntityData({required this.assetId, required this.memoryId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['memory_id'] = Variable(memoryId); + return map; + } + + factory MemoryAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + memoryId: serializer.fromJson(json['memoryId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'memoryId': serializer.toJson(memoryId), + }; + } + + MemoryAssetEntityData copyWith({String? assetId, String? memoryId}) => + MemoryAssetEntityData( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + MemoryAssetEntityData copyWithCompanion(MemoryAssetEntityCompanion data) { + return MemoryAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + memoryId: data.memoryId.present ? data.memoryId.value : this.memoryId, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, memoryId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryAssetEntityData && + other.assetId == this.assetId && + other.memoryId == this.memoryId); +} + +class MemoryAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value memoryId; + const MemoryAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.memoryId = const Value.absent(), + }); + MemoryAssetEntityCompanion.insert({ + required String assetId, + required String memoryId, + }) : assetId = Value(assetId), + memoryId = Value(memoryId); + static Insertable custom({ + Expression? assetId, + Expression? memoryId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (memoryId != null) 'memory_id': memoryId, + }); + } + + MemoryAssetEntityCompanion copyWith({ + Value? assetId, + Value? memoryId, + }) { + return MemoryAssetEntityCompanion( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (memoryId.present) { + map['memory_id'] = Variable(memoryId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } +} + +class PersonEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PersonEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn faceAssetId = GeneratedColumn( + 'face_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); + late final GeneratedColumn isHidden = GeneratedColumn( + 'is_hidden', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); + late final GeneratedColumn color = GeneratedColumn( + 'color', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn birthDate = GeneratedColumn( + 'birth_date', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'person_entity'; + @override + Set get $primaryKey => {id}; + @override + PersonEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PersonEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + faceAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}face_asset_id'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + isHidden: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_hidden'], + )!, + color: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}color'], + ), + birthDate: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}birth_date'], + ), + ); + } + + @override + PersonEntity createAlias(String alias) { + return PersonEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PersonEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String name; + final String? faceAssetId; + final bool isFavorite; + final bool isHidden; + final String? color; + final DateTime? birthDate; + const PersonEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['name'] = Variable(name); + if (!nullToAbsent || faceAssetId != null) { + map['face_asset_id'] = Variable(faceAssetId); + } + map['is_favorite'] = Variable(isFavorite); + map['is_hidden'] = Variable(isHidden); + if (!nullToAbsent || color != null) { + map['color'] = Variable(color); + } + if (!nullToAbsent || birthDate != null) { + map['birth_date'] = Variable(birthDate); + } + return map; + } + + factory PersonEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PersonEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + name: serializer.fromJson(json['name']), + faceAssetId: serializer.fromJson(json['faceAssetId']), + isFavorite: serializer.fromJson(json['isFavorite']), + isHidden: serializer.fromJson(json['isHidden']), + color: serializer.fromJson(json['color']), + birthDate: serializer.fromJson(json['birthDate']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'name': serializer.toJson(name), + 'faceAssetId': serializer.toJson(faceAssetId), + 'isFavorite': serializer.toJson(isFavorite), + 'isHidden': serializer.toJson(isHidden), + 'color': serializer.toJson(color), + 'birthDate': serializer.toJson(birthDate), + }; + } + + PersonEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + Value faceAssetId = const Value.absent(), + bool? isFavorite, + bool? isHidden, + Value color = const Value.absent(), + Value birthDate = const Value.absent(), + }) => PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); + PersonEntityData copyWithCompanion(PersonEntityCompanion data) { + return PersonEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + name: data.name.present ? data.name.value : this.name, + faceAssetId: data.faceAssetId.present + ? data.faceAssetId.value + : this.faceAssetId, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, + color: data.color.present ? data.color.value : this.color, + birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, + ); + } + + @override + String toString() { + return (StringBuffer('PersonEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PersonEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.name == this.name && + other.faceAssetId == this.faceAssetId && + other.isFavorite == this.isFavorite && + other.isHidden == this.isHidden && + other.color == this.color && + other.birthDate == this.birthDate); +} + +class PersonEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value name; + final Value faceAssetId; + final Value isFavorite; + final Value isHidden; + final Value color; + final Value birthDate; + const PersonEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.name = const Value.absent(), + this.faceAssetId = const Value.absent(), + this.isFavorite = const Value.absent(), + this.isHidden = const Value.absent(), + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }); + PersonEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String name, + this.faceAssetId = const Value.absent(), + required bool isFavorite, + required bool isHidden, + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + name = Value(name), + isFavorite = Value(isFavorite), + isHidden = Value(isHidden); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? name, + Expression? faceAssetId, + Expression? isFavorite, + Expression? isHidden, + Expression? color, + Expression? birthDate, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (name != null) 'name': name, + if (faceAssetId != null) 'face_asset_id': faceAssetId, + if (isFavorite != null) 'is_favorite': isFavorite, + if (isHidden != null) 'is_hidden': isHidden, + if (color != null) 'color': color, + if (birthDate != null) 'birth_date': birthDate, + }); + } + + PersonEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? name, + Value? faceAssetId, + Value? isFavorite, + Value? isHidden, + Value? color, + Value? birthDate, + }) { + return PersonEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId ?? this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color ?? this.color, + birthDate: birthDate ?? this.birthDate, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (faceAssetId.present) { + map['face_asset_id'] = Variable(faceAssetId.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (isHidden.present) { + map['is_hidden'] = Variable(isHidden.value); + } + if (color.present) { + map['color'] = Variable(color.value); + } + if (birthDate.present) { + map['birth_date'] = Variable(birthDate.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PersonEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } +} + +class AssetFaceEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + AssetFaceEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn personId = GeneratedColumn( + 'person_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES person_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn imageWidth = GeneratedColumn( + 'image_width', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn imageHeight = GeneratedColumn( + 'image_height', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX1 = GeneratedColumn( + 'bounding_box_x1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY1 = GeneratedColumn( + 'bounding_box_y1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX2 = GeneratedColumn( + 'bounding_box_x2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY2 = GeneratedColumn( + 'bounding_box_y2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn sourceType = GeneratedColumn( + 'source_type', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'asset_face_entity'; + @override + Set get $primaryKey => {id}; + @override + AssetFaceEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return AssetFaceEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + personId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}person_id'], + ), + imageWidth: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_width'], + )!, + imageHeight: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_height'], + )!, + boundingBoxX1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x1'], + )!, + boundingBoxY1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y1'], + )!, + boundingBoxX2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x2'], + )!, + boundingBoxY2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y2'], + )!, + sourceType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}source_type'], + )!, + ); + } + + @override + AssetFaceEntity createAlias(String alias) { + return AssetFaceEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class AssetFaceEntityData extends DataClass + implements Insertable { + final String id; + final String assetId; + final String? personId; + final int imageWidth; + final int imageHeight; + final int boundingBoxX1; + final int boundingBoxY1; + final int boundingBoxX2; + final int boundingBoxY2; + final String sourceType; + const AssetFaceEntityData({ + required this.id, + required this.assetId, + this.personId, + required this.imageWidth, + required this.imageHeight, + required this.boundingBoxX1, + required this.boundingBoxY1, + required this.boundingBoxX2, + required this.boundingBoxY2, + required this.sourceType, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || personId != null) { + map['person_id'] = Variable(personId); + } + map['image_width'] = Variable(imageWidth); + map['image_height'] = Variable(imageHeight); + map['bounding_box_x1'] = Variable(boundingBoxX1); + map['bounding_box_y1'] = Variable(boundingBoxY1); + map['bounding_box_x2'] = Variable(boundingBoxX2); + map['bounding_box_y2'] = Variable(boundingBoxY2); + map['source_type'] = Variable(sourceType); + return map; + } + + factory AssetFaceEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return AssetFaceEntityData( + id: serializer.fromJson(json['id']), + assetId: serializer.fromJson(json['assetId']), + personId: serializer.fromJson(json['personId']), + imageWidth: serializer.fromJson(json['imageWidth']), + imageHeight: serializer.fromJson(json['imageHeight']), + boundingBoxX1: serializer.fromJson(json['boundingBoxX1']), + boundingBoxY1: serializer.fromJson(json['boundingBoxY1']), + boundingBoxX2: serializer.fromJson(json['boundingBoxX2']), + boundingBoxY2: serializer.fromJson(json['boundingBoxY2']), + sourceType: serializer.fromJson(json['sourceType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'assetId': serializer.toJson(assetId), + 'personId': serializer.toJson(personId), + 'imageWidth': serializer.toJson(imageWidth), + 'imageHeight': serializer.toJson(imageHeight), + 'boundingBoxX1': serializer.toJson(boundingBoxX1), + 'boundingBoxY1': serializer.toJson(boundingBoxY1), + 'boundingBoxX2': serializer.toJson(boundingBoxX2), + 'boundingBoxY2': serializer.toJson(boundingBoxY2), + 'sourceType': serializer.toJson(sourceType), + }; + } + + AssetFaceEntityData copyWith({ + String? id, + String? assetId, + Value personId = const Value.absent(), + int? imageWidth, + int? imageHeight, + int? boundingBoxX1, + int? boundingBoxY1, + int? boundingBoxX2, + int? boundingBoxY2, + String? sourceType, + }) => AssetFaceEntityData( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId.present ? personId.value : this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + AssetFaceEntityData copyWithCompanion(AssetFaceEntityCompanion data) { + return AssetFaceEntityData( + id: data.id.present ? data.id.value : this.id, + assetId: data.assetId.present ? data.assetId.value : this.assetId, + personId: data.personId.present ? data.personId.value : this.personId, + imageWidth: data.imageWidth.present + ? data.imageWidth.value + : this.imageWidth, + imageHeight: data.imageHeight.present + ? data.imageHeight.value + : this.imageHeight, + boundingBoxX1: data.boundingBoxX1.present + ? data.boundingBoxX1.value + : this.boundingBoxX1, + boundingBoxY1: data.boundingBoxY1.present + ? data.boundingBoxY1.value + : this.boundingBoxY1, + boundingBoxX2: data.boundingBoxX2.present + ? data.boundingBoxX2.value + : this.boundingBoxX2, + boundingBoxY2: data.boundingBoxY2.present + ? data.boundingBoxY2.value + : this.boundingBoxY2, + sourceType: data.sourceType.present + ? data.sourceType.value + : this.sourceType, + ); + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityData(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is AssetFaceEntityData && + other.id == this.id && + other.assetId == this.assetId && + other.personId == this.personId && + other.imageWidth == this.imageWidth && + other.imageHeight == this.imageHeight && + other.boundingBoxX1 == this.boundingBoxX1 && + other.boundingBoxY1 == this.boundingBoxY1 && + other.boundingBoxX2 == this.boundingBoxX2 && + other.boundingBoxY2 == this.boundingBoxY2 && + other.sourceType == this.sourceType); +} + +class AssetFaceEntityCompanion extends UpdateCompanion { + final Value id; + final Value assetId; + final Value personId; + final Value imageWidth; + final Value imageHeight; + final Value boundingBoxX1; + final Value boundingBoxY1; + final Value boundingBoxX2; + final Value boundingBoxY2; + final Value sourceType; + const AssetFaceEntityCompanion({ + this.id = const Value.absent(), + this.assetId = const Value.absent(), + this.personId = const Value.absent(), + this.imageWidth = const Value.absent(), + this.imageHeight = const Value.absent(), + this.boundingBoxX1 = const Value.absent(), + this.boundingBoxY1 = const Value.absent(), + this.boundingBoxX2 = const Value.absent(), + this.boundingBoxY2 = const Value.absent(), + this.sourceType = const Value.absent(), + }); + AssetFaceEntityCompanion.insert({ + required String id, + required String assetId, + this.personId = const Value.absent(), + required int imageWidth, + required int imageHeight, + required int boundingBoxX1, + required int boundingBoxY1, + required int boundingBoxX2, + required int boundingBoxY2, + required String sourceType, + }) : id = Value(id), + assetId = Value(assetId), + imageWidth = Value(imageWidth), + imageHeight = Value(imageHeight), + boundingBoxX1 = Value(boundingBoxX1), + boundingBoxY1 = Value(boundingBoxY1), + boundingBoxX2 = Value(boundingBoxX2), + boundingBoxY2 = Value(boundingBoxY2), + sourceType = Value(sourceType); + static Insertable custom({ + Expression? id, + Expression? assetId, + Expression? personId, + Expression? imageWidth, + Expression? imageHeight, + Expression? boundingBoxX1, + Expression? boundingBoxY1, + Expression? boundingBoxX2, + Expression? boundingBoxY2, + Expression? sourceType, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (assetId != null) 'asset_id': assetId, + if (personId != null) 'person_id': personId, + if (imageWidth != null) 'image_width': imageWidth, + if (imageHeight != null) 'image_height': imageHeight, + if (boundingBoxX1 != null) 'bounding_box_x1': boundingBoxX1, + if (boundingBoxY1 != null) 'bounding_box_y1': boundingBoxY1, + if (boundingBoxX2 != null) 'bounding_box_x2': boundingBoxX2, + if (boundingBoxY2 != null) 'bounding_box_y2': boundingBoxY2, + if (sourceType != null) 'source_type': sourceType, + }); + } + + AssetFaceEntityCompanion copyWith({ + Value? id, + Value? assetId, + Value? personId, + Value? imageWidth, + Value? imageHeight, + Value? boundingBoxX1, + Value? boundingBoxY1, + Value? boundingBoxX2, + Value? boundingBoxY2, + Value? sourceType, + }) { + return AssetFaceEntityCompanion( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId ?? this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (personId.present) { + map['person_id'] = Variable(personId.value); + } + if (imageWidth.present) { + map['image_width'] = Variable(imageWidth.value); + } + if (imageHeight.present) { + map['image_height'] = Variable(imageHeight.value); + } + if (boundingBoxX1.present) { + map['bounding_box_x1'] = Variable(boundingBoxX1.value); + } + if (boundingBoxY1.present) { + map['bounding_box_y1'] = Variable(boundingBoxY1.value); + } + if (boundingBoxX2.present) { + map['bounding_box_x2'] = Variable(boundingBoxX2.value); + } + if (boundingBoxY2.present) { + map['bounding_box_y2'] = Variable(boundingBoxY2.value); + } + if (sourceType.present) { + map['source_type'] = Variable(sourceType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityCompanion(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } +} + +class DatabaseAtV4 extends GeneratedDatabase { + DatabaseAtV4(QueryExecutor e) : super(e); + late final UserEntity userEntity = UserEntity(this); + late final RemoteAssetEntity remoteAssetEntity = RemoteAssetEntity(this); + late final StackEntity stackEntity = StackEntity(this); + late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); + late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); + late final LocalAlbumAssetEntity localAlbumAssetEntity = + LocalAlbumAssetEntity(this); + late final Index idxLocalAssetChecksum = Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + late final Index uQRemoteAssetOwnerChecksum = Index( + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', + ); + late final Index idxRemoteAssetChecksum = Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); + late final UserMetadataEntity userMetadataEntity = UserMetadataEntity(this); + late final PartnerEntity partnerEntity = PartnerEntity(this); + late final RemoteExifEntity remoteExifEntity = RemoteExifEntity(this); + late final RemoteAlbumEntity remoteAlbumEntity = RemoteAlbumEntity(this); + late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = + RemoteAlbumAssetEntity(this); + late final RemoteAlbumUserEntity remoteAlbumUserEntity = + RemoteAlbumUserEntity(this); + late final MemoryEntity memoryEntity = MemoryEntity(this); + late final MemoryAssetEntity memoryAssetEntity = MemoryAssetEntity(this); + late final PersonEntity personEntity = PersonEntity(this); + late final AssetFaceEntity assetFaceEntity = AssetFaceEntity(this); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [ + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAssetChecksum, + uQRemoteAssetOwnerChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + ]; + @override + int get schemaVersion => 4; + @override + DriftDatabaseOptions get options => + const DriftDatabaseOptions(storeDateTimeAsText: true); +} diff --git a/mobile/test/drift/main/generated/schema_v5.dart b/mobile/test/drift/main/generated/schema_v5.dart new file mode 100644 index 0000000000..5c94ff26cb --- /dev/null +++ b/mobile/test/drift/main/generated/schema_v5.dart @@ -0,0 +1,6402 @@ +// dart format width=80 +// GENERATED CODE, DO NOT EDIT BY HAND. +// ignore_for_file: type=lint +import 'package:drift/drift.dart'; + +class UserEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isAdmin = GeneratedColumn( + 'is_admin', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn email = GeneratedColumn( + 'email', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn hasProfileImage = GeneratedColumn( + 'has_profile_image', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("has_profile_image" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn profileChangedAt = + GeneratedColumn( + 'profile_changed_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + @override + List get $columns => [ + id, + name, + isAdmin, + email, + hasProfileImage, + profileChangedAt, + updatedAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_entity'; + @override + Set get $primaryKey => {id}; + @override + UserEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + isAdmin: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_admin'], + )!, + email: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}email'], + )!, + hasProfileImage: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}has_profile_image'], + )!, + profileChangedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}profile_changed_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ); + } + + @override + UserEntity createAlias(String alias) { + return UserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserEntityData extends DataClass implements Insertable { + final String id; + final String name; + final bool isAdmin; + final String email; + final bool hasProfileImage; + final DateTime profileChangedAt; + final DateTime updatedAt; + const UserEntityData({ + required this.id, + required this.name, + required this.isAdmin, + required this.email, + required this.hasProfileImage, + required this.profileChangedAt, + required this.updatedAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['is_admin'] = Variable(isAdmin); + map['email'] = Variable(email); + map['has_profile_image'] = Variable(hasProfileImage); + map['profile_changed_at'] = Variable(profileChangedAt); + map['updated_at'] = Variable(updatedAt); + return map; + } + + factory UserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + isAdmin: serializer.fromJson(json['isAdmin']), + email: serializer.fromJson(json['email']), + hasProfileImage: serializer.fromJson(json['hasProfileImage']), + profileChangedAt: serializer.fromJson(json['profileChangedAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'isAdmin': serializer.toJson(isAdmin), + 'email': serializer.toJson(email), + 'hasProfileImage': serializer.toJson(hasProfileImage), + 'profileChangedAt': serializer.toJson(profileChangedAt), + 'updatedAt': serializer.toJson(updatedAt), + }; + } + + UserEntityData copyWith({ + String? id, + String? name, + bool? isAdmin, + String? email, + bool? hasProfileImage, + DateTime? profileChangedAt, + DateTime? updatedAt, + }) => UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + updatedAt: updatedAt ?? this.updatedAt, + ); + UserEntityData copyWithCompanion(UserEntityCompanion data) { + return UserEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + isAdmin: data.isAdmin.present ? data.isAdmin.value : this.isAdmin, + email: data.email.present ? data.email.value : this.email, + hasProfileImage: data.hasProfileImage.present + ? data.hasProfileImage.value + : this.hasProfileImage, + profileChangedAt: data.profileChangedAt.present + ? data.profileChangedAt.value + : this.profileChangedAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ); + } + + @override + String toString() { + return (StringBuffer('UserEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isAdmin: $isAdmin, ') + ..write('email: $email, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('updatedAt: $updatedAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + isAdmin, + email, + hasProfileImage, + profileChangedAt, + updatedAt, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserEntityData && + other.id == this.id && + other.name == this.name && + other.isAdmin == this.isAdmin && + other.email == this.email && + other.hasProfileImage == this.hasProfileImage && + other.profileChangedAt == this.profileChangedAt && + other.updatedAt == this.updatedAt); +} + +class UserEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value isAdmin; + final Value email; + final Value hasProfileImage; + final Value profileChangedAt; + final Value updatedAt; + const UserEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.isAdmin = const Value.absent(), + this.email = const Value.absent(), + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + this.updatedAt = const Value.absent(), + }); + UserEntityCompanion.insert({ + required String id, + required String name, + this.isAdmin = const Value.absent(), + required String email, + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + this.updatedAt = const Value.absent(), + }) : id = Value(id), + name = Value(name), + email = Value(email); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? isAdmin, + Expression? email, + Expression? hasProfileImage, + Expression? profileChangedAt, + Expression? updatedAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (isAdmin != null) 'is_admin': isAdmin, + if (email != null) 'email': email, + if (hasProfileImage != null) 'has_profile_image': hasProfileImage, + if (profileChangedAt != null) 'profile_changed_at': profileChangedAt, + if (updatedAt != null) 'updated_at': updatedAt, + }); + } + + UserEntityCompanion copyWith({ + Value? id, + Value? name, + Value? isAdmin, + Value? email, + Value? hasProfileImage, + Value? profileChangedAt, + Value? updatedAt, + }) { + return UserEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + updatedAt: updatedAt ?? this.updatedAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (isAdmin.present) { + map['is_admin'] = Variable(isAdmin.value); + } + if (email.present) { + map['email'] = Variable(email.value); + } + if (hasProfileImage.present) { + map['has_profile_image'] = Variable(hasProfileImage.value); + } + if (profileChangedAt.present) { + map['profile_changed_at'] = Variable(profileChangedAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isAdmin: $isAdmin, ') + ..write('email: $email, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('updatedAt: $updatedAt') + ..write(')')) + .toString(); + } +} + +class RemoteAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn localDateTime = + GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn thumbHash = GeneratedColumn( + 'thumb_hash', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn livePhotoVideoId = GeneratedColumn( + 'live_photo_video_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn visibility = GeneratedColumn( + 'visibility', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn stackId = GeneratedColumn( + 'stack_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAssetEntityData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + localDateTime: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}local_date_time'], + ), + thumbHash: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumb_hash'], + ), + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + livePhotoVideoId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}live_photo_video_id'], + ), + visibility: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}visibility'], + )!, + stackId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}stack_id'], + ), + ); + } + + @override + RemoteAssetEntity createAlias(String alias) { + return RemoteAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String checksum; + final bool isFavorite; + final String ownerId; + final DateTime? localDateTime; + final String? thumbHash; + final DateTime? deletedAt; + final String? livePhotoVideoId; + final int visibility; + final String? stackId; + const RemoteAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + required this.checksum, + required this.isFavorite, + required this.ownerId, + this.localDateTime, + this.thumbHash, + this.deletedAt, + this.livePhotoVideoId, + required this.visibility, + this.stackId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + map['checksum'] = Variable(checksum); + map['is_favorite'] = Variable(isFavorite); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || localDateTime != null) { + map['local_date_time'] = Variable(localDateTime); + } + if (!nullToAbsent || thumbHash != null) { + map['thumb_hash'] = Variable(thumbHash); + } + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + if (!nullToAbsent || livePhotoVideoId != null) { + map['live_photo_video_id'] = Variable(livePhotoVideoId); + } + map['visibility'] = Variable(visibility); + if (!nullToAbsent || stackId != null) { + map['stack_id'] = Variable(stackId); + } + return map; + } + + factory RemoteAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + ownerId: serializer.fromJson(json['ownerId']), + localDateTime: serializer.fromJson(json['localDateTime']), + thumbHash: serializer.fromJson(json['thumbHash']), + deletedAt: serializer.fromJson(json['deletedAt']), + livePhotoVideoId: serializer.fromJson(json['livePhotoVideoId']), + visibility: serializer.fromJson(json['visibility']), + stackId: serializer.fromJson(json['stackId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'ownerId': serializer.toJson(ownerId), + 'localDateTime': serializer.toJson(localDateTime), + 'thumbHash': serializer.toJson(thumbHash), + 'deletedAt': serializer.toJson(deletedAt), + 'livePhotoVideoId': serializer.toJson(livePhotoVideoId), + 'visibility': serializer.toJson(visibility), + 'stackId': serializer.toJson(stackId), + }; + } + + RemoteAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + String? checksum, + bool? isFavorite, + String? ownerId, + Value localDateTime = const Value.absent(), + Value thumbHash = const Value.absent(), + Value deletedAt = const Value.absent(), + Value livePhotoVideoId = const Value.absent(), + int? visibility, + Value stackId = const Value.absent(), + }) => RemoteAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime.present + ? localDateTime.value + : this.localDateTime, + thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + livePhotoVideoId: livePhotoVideoId.present + ? livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId.present ? stackId.value : this.stackId, + ); + RemoteAssetEntityData copyWithCompanion(RemoteAssetEntityCompanion data) { + return RemoteAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + localDateTime: data.localDateTime.present + ? data.localDateTime.value + : this.localDateTime, + thumbHash: data.thumbHash.present ? data.thumbHash.value : this.thumbHash, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + livePhotoVideoId: data.livePhotoVideoId.present + ? data.livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: data.visibility.present + ? data.visibility.value + : this.visibility, + stackId: data.stackId.present ? data.stackId.value : this.stackId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.ownerId == this.ownerId && + other.localDateTime == this.localDateTime && + other.thumbHash == this.thumbHash && + other.deletedAt == this.deletedAt && + other.livePhotoVideoId == this.livePhotoVideoId && + other.visibility == this.visibility && + other.stackId == this.stackId); +} + +class RemoteAssetEntityCompanion + extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value ownerId; + final Value localDateTime; + final Value thumbHash; + final Value deletedAt; + final Value livePhotoVideoId; + final Value visibility; + final Value stackId; + const RemoteAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.ownerId = const Value.absent(), + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + this.visibility = const Value.absent(), + this.stackId = const Value.absent(), + }); + RemoteAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + required String checksum, + this.isFavorite = const Value.absent(), + required String ownerId, + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + required int visibility, + this.stackId = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id), + checksum = Value(checksum), + ownerId = Value(ownerId), + visibility = Value(visibility); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? ownerId, + Expression? localDateTime, + Expression? thumbHash, + Expression? deletedAt, + Expression? livePhotoVideoId, + Expression? visibility, + Expression? stackId, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (ownerId != null) 'owner_id': ownerId, + if (localDateTime != null) 'local_date_time': localDateTime, + if (thumbHash != null) 'thumb_hash': thumbHash, + if (deletedAt != null) 'deleted_at': deletedAt, + if (livePhotoVideoId != null) 'live_photo_video_id': livePhotoVideoId, + if (visibility != null) 'visibility': visibility, + if (stackId != null) 'stack_id': stackId, + }); + } + + RemoteAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? ownerId, + Value? localDateTime, + Value? thumbHash, + Value? deletedAt, + Value? livePhotoVideoId, + Value? visibility, + Value? stackId, + }) { + return RemoteAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime ?? this.localDateTime, + thumbHash: thumbHash ?? this.thumbHash, + deletedAt: deletedAt ?? this.deletedAt, + livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId ?? this.stackId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (localDateTime.present) { + map['local_date_time'] = Variable(localDateTime.value); + } + if (thumbHash.present) { + map['thumb_hash'] = Variable(thumbHash.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (livePhotoVideoId.present) { + map['live_photo_video_id'] = Variable(livePhotoVideoId.value); + } + if (visibility.present) { + map['visibility'] = Variable(visibility.value); + } + if (stackId.present) { + map['stack_id'] = Variable(stackId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId') + ..write(')')) + .toString(); + } +} + +class StackEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + StackEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn primaryAssetId = GeneratedColumn( + 'primary_asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + primaryAssetId, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'stack_entity'; + @override + Set get $primaryKey => {id}; + @override + StackEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StackEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + primaryAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}primary_asset_id'], + )!, + ); + } + + @override + StackEntity createAlias(String alias) { + return StackEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class StackEntityData extends DataClass implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String primaryAssetId; + const StackEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['primary_asset_id'] = Variable(primaryAssetId); + return map; + } + + factory StackEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StackEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + primaryAssetId: serializer.fromJson(json['primaryAssetId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'primaryAssetId': serializer.toJson(primaryAssetId), + }; + } + + StackEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId, + }) => StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + StackEntityData copyWithCompanion(StackEntityCompanion data) { + return StackEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + primaryAssetId: data.primaryAssetId.present + ? data.primaryAssetId.value + : this.primaryAssetId, + ); + } + + @override + String toString() { + return (StringBuffer('StackEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StackEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.primaryAssetId == this.primaryAssetId); +} + +class StackEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value primaryAssetId; + const StackEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.primaryAssetId = const Value.absent(), + }); + StackEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String primaryAssetId, + }) : id = Value(id), + ownerId = Value(ownerId), + primaryAssetId = Value(primaryAssetId); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? primaryAssetId, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (primaryAssetId != null) 'primary_asset_id': primaryAssetId, + }); + } + + StackEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? primaryAssetId, + }) { + return StackEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (primaryAssetId.present) { + map['primary_asset_id'] = Variable(primaryAssetId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StackEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } +} + +class LocalAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAssetEntityData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}orientation'], + )!, + ); + } + + @override + LocalAssetEntity createAlias(String alias) { + return LocalAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String? checksum; + final bool isFavorite; + final int orientation; + const LocalAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + this.checksum, + required this.isFavorite, + required this.orientation, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + if (!nullToAbsent || checksum != null) { + map['checksum'] = Variable(checksum); + } + map['is_favorite'] = Variable(isFavorite); + map['orientation'] = Variable(orientation); + return map; + } + + factory LocalAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + orientation: serializer.fromJson(json['orientation']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'orientation': serializer.toJson(orientation), + }; + } + + LocalAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + Value checksum = const Value.absent(), + bool? isFavorite, + int? orientation, + }) => LocalAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + LocalAssetEntityData copyWithCompanion(LocalAssetEntityCompanion data) { + return LocalAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.orientation == this.orientation); +} + +class LocalAssetEntityCompanion extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value orientation; + const LocalAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }); + LocalAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? orientation, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (orientation != null) 'orientation': orientation, + }); + } + + LocalAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? orientation, + }) { + return LocalAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } +} + +class LocalAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn backupSelection = GeneratedColumn( + 'backup_selection', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn isIosSharedAlbum = GeneratedColumn( + 'is_ios_shared_album', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn marker_ = GeneratedColumn( + 'marker', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); + @override + List get $columns => [ + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + backupSelection: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}backup_selection'], + )!, + isIosSharedAlbum: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_ios_shared_album'], + )!, + marker_: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), + ); + } + + @override + LocalAlbumEntity createAlias(String alias) { + return LocalAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final DateTime updatedAt; + final int backupSelection; + final bool isIosSharedAlbum; + final bool? marker_; + const LocalAlbumEntityData({ + required this.id, + required this.name, + required this.updatedAt, + required this.backupSelection, + required this.isIosSharedAlbum, + this.marker_, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['updated_at'] = Variable(updatedAt); + map['backup_selection'] = Variable(backupSelection); + map['is_ios_shared_album'] = Variable(isIosSharedAlbum); + if (!nullToAbsent || marker_ != null) { + map['marker'] = Variable(marker_); + } + return map; + } + + factory LocalAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + updatedAt: serializer.fromJson(json['updatedAt']), + backupSelection: serializer.fromJson(json['backupSelection']), + isIosSharedAlbum: serializer.fromJson(json['isIosSharedAlbum']), + marker_: serializer.fromJson(json['marker_']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'updatedAt': serializer.toJson(updatedAt), + 'backupSelection': serializer.toJson(backupSelection), + 'isIosSharedAlbum': serializer.toJson(isIosSharedAlbum), + 'marker_': serializer.toJson(marker_), + }; + } + + LocalAlbumEntityData copyWith({ + String? id, + String? name, + DateTime? updatedAt, + int? backupSelection, + bool? isIosSharedAlbum, + Value marker_ = const Value.absent(), + }) => LocalAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_.present ? marker_.value : this.marker_, + ); + LocalAlbumEntityData copyWithCompanion(LocalAlbumEntityCompanion data) { + return LocalAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + backupSelection: data.backupSelection.present + ? data.backupSelection.value + : this.backupSelection, + isIosSharedAlbum: data.isIosSharedAlbum.present + ? data.isIosSharedAlbum.value + : this.isIosSharedAlbum, + marker_: data.marker_.present ? data.marker_.value : this.marker_, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.updatedAt == this.updatedAt && + other.backupSelection == this.backupSelection && + other.isIosSharedAlbum == this.isIosSharedAlbum && + other.marker_ == this.marker_); +} + +class LocalAlbumEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value updatedAt; + final Value backupSelection; + final Value isIosSharedAlbum; + final Value marker_; + const LocalAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.updatedAt = const Value.absent(), + this.backupSelection = const Value.absent(), + this.isIosSharedAlbum = const Value.absent(), + this.marker_ = const Value.absent(), + }); + LocalAlbumEntityCompanion.insert({ + required String id, + required String name, + this.updatedAt = const Value.absent(), + required int backupSelection, + this.isIosSharedAlbum = const Value.absent(), + this.marker_ = const Value.absent(), + }) : id = Value(id), + name = Value(name), + backupSelection = Value(backupSelection); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? updatedAt, + Expression? backupSelection, + Expression? isIosSharedAlbum, + Expression? marker_, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (updatedAt != null) 'updated_at': updatedAt, + if (backupSelection != null) 'backup_selection': backupSelection, + if (isIosSharedAlbum != null) 'is_ios_shared_album': isIosSharedAlbum, + if (marker_ != null) 'marker': marker_, + }); + } + + LocalAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? updatedAt, + Value? backupSelection, + Value? isIosSharedAlbum, + Value? marker_, + }) { + return LocalAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_ ?? this.marker_, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (backupSelection.present) { + map['backup_selection'] = Variable(backupSelection.value); + } + if (isIosSharedAlbum.present) { + map['is_ios_shared_album'] = Variable(isIosSharedAlbum.value); + } + if (marker_.present) { + map['marker'] = Variable(marker_.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } +} + +class LocalAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + LocalAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + ); + } + + @override + LocalAlbumAssetEntity createAlias(String alias) { + return LocalAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const LocalAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory LocalAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + LocalAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + LocalAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + LocalAlbumAssetEntityData copyWithCompanion( + LocalAlbumAssetEntityCompanion data, + ) { + return LocalAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class LocalAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const LocalAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + LocalAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + LocalAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { + return LocalAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class UserMetadataEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserMetadataEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn key = GeneratedColumn( + 'key', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn value = GeneratedColumn( + 'value', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + ); + @override + List get $columns => [userId, key, value]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_metadata_entity'; + @override + Set get $primaryKey => {userId, key}; + @override + UserMetadataEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserMetadataEntityData( + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + key: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}key'], + )!, + value: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}value'], + )!, + ); + } + + @override + UserMetadataEntity createAlias(String alias) { + return UserMetadataEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserMetadataEntityData extends DataClass + implements Insertable { + final String userId; + final int key; + final Uint8List value; + const UserMetadataEntityData({ + required this.userId, + required this.key, + required this.value, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['user_id'] = Variable(userId); + map['key'] = Variable(key); + map['value'] = Variable(value); + return map; + } + + factory UserMetadataEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserMetadataEntityData( + userId: serializer.fromJson(json['userId']), + key: serializer.fromJson(json['key']), + value: serializer.fromJson(json['value']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'userId': serializer.toJson(userId), + 'key': serializer.toJson(key), + 'value': serializer.toJson(value), + }; + } + + UserMetadataEntityData copyWith({ + String? userId, + int? key, + Uint8List? value, + }) => UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { + return UserMetadataEntityData( + userId: data.userId.present ? data.userId.value : this.userId, + key: data.key.present ? data.key.value : this.key, + value: data.value.present ? data.value.value : this.value, + ); + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityData(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(userId, key, $driftBlobEquality.hash(value)); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserMetadataEntityData && + other.userId == this.userId && + other.key == this.key && + $driftBlobEquality.equals(other.value, this.value)); +} + +class UserMetadataEntityCompanion + extends UpdateCompanion { + final Value userId; + final Value key; + final Value value; + const UserMetadataEntityCompanion({ + this.userId = const Value.absent(), + this.key = const Value.absent(), + this.value = const Value.absent(), + }); + UserMetadataEntityCompanion.insert({ + required String userId, + required int key, + required Uint8List value, + }) : userId = Value(userId), + key = Value(key), + value = Value(value); + static Insertable custom({ + Expression? userId, + Expression? key, + Expression? value, + }) { + return RawValuesInsertable({ + if (userId != null) 'user_id': userId, + if (key != null) 'key': key, + if (value != null) 'value': value, + }); + } + + UserMetadataEntityCompanion copyWith({ + Value? userId, + Value? key, + Value? value, + }) { + return UserMetadataEntityCompanion( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (key.present) { + map['key'] = Variable(key.value); + } + if (value.present) { + map['value'] = Variable(value.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityCompanion(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } +} + +class PartnerEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PartnerEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn sharedById = GeneratedColumn( + 'shared_by_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn sharedWithId = GeneratedColumn( + 'shared_with_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn inTimeline = GeneratedColumn( + 'in_timeline', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [sharedById, sharedWithId, inTimeline]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'partner_entity'; + @override + Set get $primaryKey => {sharedById, sharedWithId}; + @override + PartnerEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PartnerEntityData( + sharedById: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_by_id'], + )!, + sharedWithId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_with_id'], + )!, + inTimeline: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}in_timeline'], + )!, + ); + } + + @override + PartnerEntity createAlias(String alias) { + return PartnerEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PartnerEntityData extends DataClass + implements Insertable { + final String sharedById; + final String sharedWithId; + final bool inTimeline; + const PartnerEntityData({ + required this.sharedById, + required this.sharedWithId, + required this.inTimeline, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['shared_by_id'] = Variable(sharedById); + map['shared_with_id'] = Variable(sharedWithId); + map['in_timeline'] = Variable(inTimeline); + return map; + } + + factory PartnerEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PartnerEntityData( + sharedById: serializer.fromJson(json['sharedById']), + sharedWithId: serializer.fromJson(json['sharedWithId']), + inTimeline: serializer.fromJson(json['inTimeline']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'sharedById': serializer.toJson(sharedById), + 'sharedWithId': serializer.toJson(sharedWithId), + 'inTimeline': serializer.toJson(inTimeline), + }; + } + + PartnerEntityData copyWith({ + String? sharedById, + String? sharedWithId, + bool? inTimeline, + }) => PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { + return PartnerEntityData( + sharedById: data.sharedById.present + ? data.sharedById.value + : this.sharedById, + sharedWithId: data.sharedWithId.present + ? data.sharedWithId.value + : this.sharedWithId, + inTimeline: data.inTimeline.present + ? data.inTimeline.value + : this.inTimeline, + ); + } + + @override + String toString() { + return (StringBuffer('PartnerEntityData(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(sharedById, sharedWithId, inTimeline); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PartnerEntityData && + other.sharedById == this.sharedById && + other.sharedWithId == this.sharedWithId && + other.inTimeline == this.inTimeline); +} + +class PartnerEntityCompanion extends UpdateCompanion { + final Value sharedById; + final Value sharedWithId; + final Value inTimeline; + const PartnerEntityCompanion({ + this.sharedById = const Value.absent(), + this.sharedWithId = const Value.absent(), + this.inTimeline = const Value.absent(), + }); + PartnerEntityCompanion.insert({ + required String sharedById, + required String sharedWithId, + this.inTimeline = const Value.absent(), + }) : sharedById = Value(sharedById), + sharedWithId = Value(sharedWithId); + static Insertable custom({ + Expression? sharedById, + Expression? sharedWithId, + Expression? inTimeline, + }) { + return RawValuesInsertable({ + if (sharedById != null) 'shared_by_id': sharedById, + if (sharedWithId != null) 'shared_with_id': sharedWithId, + if (inTimeline != null) 'in_timeline': inTimeline, + }); + } + + PartnerEntityCompanion copyWith({ + Value? sharedById, + Value? sharedWithId, + Value? inTimeline, + }) { + return PartnerEntityCompanion( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (sharedById.present) { + map['shared_by_id'] = Variable(sharedById.value); + } + if (sharedWithId.present) { + map['shared_with_id'] = Variable(sharedWithId.value); + } + if (inTimeline.present) { + map['in_timeline'] = Variable(inTimeline.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PartnerEntityCompanion(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } +} + +class RemoteExifEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteExifEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn city = GeneratedColumn( + 'city', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn state = GeneratedColumn( + 'state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn country = GeneratedColumn( + 'country', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn dateTimeOriginal = + GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn exposureTime = GeneratedColumn( + 'exposure_time', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn fNumber = GeneratedColumn( + 'f_number', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn fileSize = GeneratedColumn( + 'file_size', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn focalLength = GeneratedColumn( + 'focal_length', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn latitude = GeneratedColumn( + 'latitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn longitude = GeneratedColumn( + 'longitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn iso = GeneratedColumn( + 'iso', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn make = GeneratedColumn( + 'make', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn model = GeneratedColumn( + 'model', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn lens = GeneratedColumn( + 'lens', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn timeZone = GeneratedColumn( + 'time_zone', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn rating = GeneratedColumn( + 'rating', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn projectionType = GeneratedColumn( + 'projection_type', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_exif_entity'; + @override + Set get $primaryKey => {assetId}; + @override + RemoteExifEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteExifEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + city: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}city'], + ), + state: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}state'], + ), + country: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}country'], + ), + dateTimeOriginal: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}date_time_original'], + ), + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + exposureTime: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}exposure_time'], + ), + fNumber: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}f_number'], + ), + fileSize: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}file_size'], + ), + focalLength: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}focal_length'], + ), + latitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}latitude'], + ), + longitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}longitude'], + ), + iso: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}iso'], + ), + make: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}make'], + ), + model: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}model'], + ), + lens: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}lens'], + ), + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}orientation'], + ), + timeZone: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}time_zone'], + ), + rating: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}rating'], + ), + projectionType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}projection_type'], + ), + ); + } + + @override + RemoteExifEntity createAlias(String alias) { + return RemoteExifEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteExifEntityData extends DataClass + implements Insertable { + final String assetId; + final String? city; + final String? state; + final String? country; + final DateTime? dateTimeOriginal; + final String? description; + final int? height; + final int? width; + final String? exposureTime; + final double? fNumber; + final int? fileSize; + final double? focalLength; + final double? latitude; + final double? longitude; + final int? iso; + final String? make; + final String? model; + final String? lens; + final String? orientation; + final String? timeZone; + final int? rating; + final String? projectionType; + const RemoteExifEntityData({ + required this.assetId, + this.city, + this.state, + this.country, + this.dateTimeOriginal, + this.description, + this.height, + this.width, + this.exposureTime, + this.fNumber, + this.fileSize, + this.focalLength, + this.latitude, + this.longitude, + this.iso, + this.make, + this.model, + this.lens, + this.orientation, + this.timeZone, + this.rating, + this.projectionType, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || city != null) { + map['city'] = Variable(city); + } + if (!nullToAbsent || state != null) { + map['state'] = Variable(state); + } + if (!nullToAbsent || country != null) { + map['country'] = Variable(country); + } + if (!nullToAbsent || dateTimeOriginal != null) { + map['date_time_original'] = Variable(dateTimeOriginal); + } + if (!nullToAbsent || description != null) { + map['description'] = Variable(description); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || exposureTime != null) { + map['exposure_time'] = Variable(exposureTime); + } + if (!nullToAbsent || fNumber != null) { + map['f_number'] = Variable(fNumber); + } + if (!nullToAbsent || fileSize != null) { + map['file_size'] = Variable(fileSize); + } + if (!nullToAbsent || focalLength != null) { + map['focal_length'] = Variable(focalLength); + } + if (!nullToAbsent || latitude != null) { + map['latitude'] = Variable(latitude); + } + if (!nullToAbsent || longitude != null) { + map['longitude'] = Variable(longitude); + } + if (!nullToAbsent || iso != null) { + map['iso'] = Variable(iso); + } + if (!nullToAbsent || make != null) { + map['make'] = Variable(make); + } + if (!nullToAbsent || model != null) { + map['model'] = Variable(model); + } + if (!nullToAbsent || lens != null) { + map['lens'] = Variable(lens); + } + if (!nullToAbsent || orientation != null) { + map['orientation'] = Variable(orientation); + } + if (!nullToAbsent || timeZone != null) { + map['time_zone'] = Variable(timeZone); + } + if (!nullToAbsent || rating != null) { + map['rating'] = Variable(rating); + } + if (!nullToAbsent || projectionType != null) { + map['projection_type'] = Variable(projectionType); + } + return map; + } + + factory RemoteExifEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteExifEntityData( + assetId: serializer.fromJson(json['assetId']), + city: serializer.fromJson(json['city']), + state: serializer.fromJson(json['state']), + country: serializer.fromJson(json['country']), + dateTimeOriginal: serializer.fromJson( + json['dateTimeOriginal'], + ), + description: serializer.fromJson(json['description']), + height: serializer.fromJson(json['height']), + width: serializer.fromJson(json['width']), + exposureTime: serializer.fromJson(json['exposureTime']), + fNumber: serializer.fromJson(json['fNumber']), + fileSize: serializer.fromJson(json['fileSize']), + focalLength: serializer.fromJson(json['focalLength']), + latitude: serializer.fromJson(json['latitude']), + longitude: serializer.fromJson(json['longitude']), + iso: serializer.fromJson(json['iso']), + make: serializer.fromJson(json['make']), + model: serializer.fromJson(json['model']), + lens: serializer.fromJson(json['lens']), + orientation: serializer.fromJson(json['orientation']), + timeZone: serializer.fromJson(json['timeZone']), + rating: serializer.fromJson(json['rating']), + projectionType: serializer.fromJson(json['projectionType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'city': serializer.toJson(city), + 'state': serializer.toJson(state), + 'country': serializer.toJson(country), + 'dateTimeOriginal': serializer.toJson(dateTimeOriginal), + 'description': serializer.toJson(description), + 'height': serializer.toJson(height), + 'width': serializer.toJson(width), + 'exposureTime': serializer.toJson(exposureTime), + 'fNumber': serializer.toJson(fNumber), + 'fileSize': serializer.toJson(fileSize), + 'focalLength': serializer.toJson(focalLength), + 'latitude': serializer.toJson(latitude), + 'longitude': serializer.toJson(longitude), + 'iso': serializer.toJson(iso), + 'make': serializer.toJson(make), + 'model': serializer.toJson(model), + 'lens': serializer.toJson(lens), + 'orientation': serializer.toJson(orientation), + 'timeZone': serializer.toJson(timeZone), + 'rating': serializer.toJson(rating), + 'projectionType': serializer.toJson(projectionType), + }; + } + + RemoteExifEntityData copyWith({ + String? assetId, + Value city = const Value.absent(), + Value state = const Value.absent(), + Value country = const Value.absent(), + Value dateTimeOriginal = const Value.absent(), + Value description = const Value.absent(), + Value height = const Value.absent(), + Value width = const Value.absent(), + Value exposureTime = const Value.absent(), + Value fNumber = const Value.absent(), + Value fileSize = const Value.absent(), + Value focalLength = const Value.absent(), + Value latitude = const Value.absent(), + Value longitude = const Value.absent(), + Value iso = const Value.absent(), + Value make = const Value.absent(), + Value model = const Value.absent(), + Value lens = const Value.absent(), + Value orientation = const Value.absent(), + Value timeZone = const Value.absent(), + Value rating = const Value.absent(), + Value projectionType = const Value.absent(), + }) => RemoteExifEntityData( + assetId: assetId ?? this.assetId, + city: city.present ? city.value : this.city, + state: state.present ? state.value : this.state, + country: country.present ? country.value : this.country, + dateTimeOriginal: dateTimeOriginal.present + ? dateTimeOriginal.value + : this.dateTimeOriginal, + description: description.present ? description.value : this.description, + height: height.present ? height.value : this.height, + width: width.present ? width.value : this.width, + exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, + fNumber: fNumber.present ? fNumber.value : this.fNumber, + fileSize: fileSize.present ? fileSize.value : this.fileSize, + focalLength: focalLength.present ? focalLength.value : this.focalLength, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + iso: iso.present ? iso.value : this.iso, + make: make.present ? make.value : this.make, + model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, + orientation: orientation.present ? orientation.value : this.orientation, + timeZone: timeZone.present ? timeZone.value : this.timeZone, + rating: rating.present ? rating.value : this.rating, + projectionType: projectionType.present + ? projectionType.value + : this.projectionType, + ); + RemoteExifEntityData copyWithCompanion(RemoteExifEntityCompanion data) { + return RemoteExifEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + city: data.city.present ? data.city.value : this.city, + state: data.state.present ? data.state.value : this.state, + country: data.country.present ? data.country.value : this.country, + dateTimeOriginal: data.dateTimeOriginal.present + ? data.dateTimeOriginal.value + : this.dateTimeOriginal, + description: data.description.present + ? data.description.value + : this.description, + height: data.height.present ? data.height.value : this.height, + width: data.width.present ? data.width.value : this.width, + exposureTime: data.exposureTime.present + ? data.exposureTime.value + : this.exposureTime, + fNumber: data.fNumber.present ? data.fNumber.value : this.fNumber, + fileSize: data.fileSize.present ? data.fileSize.value : this.fileSize, + focalLength: data.focalLength.present + ? data.focalLength.value + : this.focalLength, + latitude: data.latitude.present ? data.latitude.value : this.latitude, + longitude: data.longitude.present ? data.longitude.value : this.longitude, + iso: data.iso.present ? data.iso.value : this.iso, + make: data.make.present ? data.make.value : this.make, + model: data.model.present ? data.model.value : this.model, + lens: data.lens.present ? data.lens.value : this.lens, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, + timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, + rating: data.rating.present ? data.rating.value : this.rating, + projectionType: data.projectionType.present + ? data.projectionType.value + : this.projectionType, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityData(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hashAll([ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteExifEntityData && + other.assetId == this.assetId && + other.city == this.city && + other.state == this.state && + other.country == this.country && + other.dateTimeOriginal == this.dateTimeOriginal && + other.description == this.description && + other.height == this.height && + other.width == this.width && + other.exposureTime == this.exposureTime && + other.fNumber == this.fNumber && + other.fileSize == this.fileSize && + other.focalLength == this.focalLength && + other.latitude == this.latitude && + other.longitude == this.longitude && + other.iso == this.iso && + other.make == this.make && + other.model == this.model && + other.lens == this.lens && + other.orientation == this.orientation && + other.timeZone == this.timeZone && + other.rating == this.rating && + other.projectionType == this.projectionType); +} + +class RemoteExifEntityCompanion extends UpdateCompanion { + final Value assetId; + final Value city; + final Value state; + final Value country; + final Value dateTimeOriginal; + final Value description; + final Value height; + final Value width; + final Value exposureTime; + final Value fNumber; + final Value fileSize; + final Value focalLength; + final Value latitude; + final Value longitude; + final Value iso; + final Value make; + final Value model; + final Value lens; + final Value orientation; + final Value timeZone; + final Value rating; + final Value projectionType; + const RemoteExifEntityCompanion({ + this.assetId = const Value.absent(), + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }); + RemoteExifEntityCompanion.insert({ + required String assetId, + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }) : assetId = Value(assetId); + static Insertable custom({ + Expression? assetId, + Expression? city, + Expression? state, + Expression? country, + Expression? dateTimeOriginal, + Expression? description, + Expression? height, + Expression? width, + Expression? exposureTime, + Expression? fNumber, + Expression? fileSize, + Expression? focalLength, + Expression? latitude, + Expression? longitude, + Expression? iso, + Expression? make, + Expression? model, + Expression? lens, + Expression? orientation, + Expression? timeZone, + Expression? rating, + Expression? projectionType, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (city != null) 'city': city, + if (state != null) 'state': state, + if (country != null) 'country': country, + if (dateTimeOriginal != null) 'date_time_original': dateTimeOriginal, + if (description != null) 'description': description, + if (height != null) 'height': height, + if (width != null) 'width': width, + if (exposureTime != null) 'exposure_time': exposureTime, + if (fNumber != null) 'f_number': fNumber, + if (fileSize != null) 'file_size': fileSize, + if (focalLength != null) 'focal_length': focalLength, + if (latitude != null) 'latitude': latitude, + if (longitude != null) 'longitude': longitude, + if (iso != null) 'iso': iso, + if (make != null) 'make': make, + if (model != null) 'model': model, + if (lens != null) 'lens': lens, + if (orientation != null) 'orientation': orientation, + if (timeZone != null) 'time_zone': timeZone, + if (rating != null) 'rating': rating, + if (projectionType != null) 'projection_type': projectionType, + }); + } + + RemoteExifEntityCompanion copyWith({ + Value? assetId, + Value? city, + Value? state, + Value? country, + Value? dateTimeOriginal, + Value? description, + Value? height, + Value? width, + Value? exposureTime, + Value? fNumber, + Value? fileSize, + Value? focalLength, + Value? latitude, + Value? longitude, + Value? iso, + Value? make, + Value? model, + Value? lens, + Value? orientation, + Value? timeZone, + Value? rating, + Value? projectionType, + }) { + return RemoteExifEntityCompanion( + assetId: assetId ?? this.assetId, + city: city ?? this.city, + state: state ?? this.state, + country: country ?? this.country, + dateTimeOriginal: dateTimeOriginal ?? this.dateTimeOriginal, + description: description ?? this.description, + height: height ?? this.height, + width: width ?? this.width, + exposureTime: exposureTime ?? this.exposureTime, + fNumber: fNumber ?? this.fNumber, + fileSize: fileSize ?? this.fileSize, + focalLength: focalLength ?? this.focalLength, + latitude: latitude ?? this.latitude, + longitude: longitude ?? this.longitude, + iso: iso ?? this.iso, + make: make ?? this.make, + model: model ?? this.model, + lens: lens ?? this.lens, + orientation: orientation ?? this.orientation, + timeZone: timeZone ?? this.timeZone, + rating: rating ?? this.rating, + projectionType: projectionType ?? this.projectionType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (city.present) { + map['city'] = Variable(city.value); + } + if (state.present) { + map['state'] = Variable(state.value); + } + if (country.present) { + map['country'] = Variable(country.value); + } + if (dateTimeOriginal.present) { + map['date_time_original'] = Variable(dateTimeOriginal.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (exposureTime.present) { + map['exposure_time'] = Variable(exposureTime.value); + } + if (fNumber.present) { + map['f_number'] = Variable(fNumber.value); + } + if (fileSize.present) { + map['file_size'] = Variable(fileSize.value); + } + if (focalLength.present) { + map['focal_length'] = Variable(focalLength.value); + } + if (latitude.present) { + map['latitude'] = Variable(latitude.value); + } + if (longitude.present) { + map['longitude'] = Variable(longitude.value); + } + if (iso.present) { + map['iso'] = Variable(iso.value); + } + if (make.present) { + map['make'] = Variable(make.value); + } + if (model.present) { + map['model'] = Variable(model.value); + } + if (lens.present) { + map['lens'] = Variable(lens.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + if (timeZone.present) { + map['time_zone'] = Variable(timeZone.value); + } + if (rating.present) { + map['rating'] = Variable(rating.value); + } + if (projectionType.present) { + map['projection_type'] = Variable(projectionType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const CustomExpression('\'\''), + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn thumbnailAssetId = GeneratedColumn( + 'thumbnail_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn isActivityEnabled = GeneratedColumn( + 'is_activity_enabled', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const CustomExpression('1'), + ); + late final GeneratedColumn order = GeneratedColumn( + 'order', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + thumbnailAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_asset_id'], + ), + isActivityEnabled: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_activity_enabled'], + )!, + order: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}order'], + )!, + ); + } + + @override + RemoteAlbumEntity createAlias(String alias) { + return RemoteAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final String description; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String? thumbnailAssetId; + final bool isActivityEnabled; + final int order; + const RemoteAlbumEntityData({ + required this.id, + required this.name, + required this.description, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + this.thumbnailAssetId, + required this.isActivityEnabled, + required this.order, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['description'] = Variable(description); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || thumbnailAssetId != null) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId); + } + map['is_activity_enabled'] = Variable(isActivityEnabled); + map['order'] = Variable(order); + return map; + } + + factory RemoteAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + description: serializer.fromJson(json['description']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + thumbnailAssetId: serializer.fromJson(json['thumbnailAssetId']), + isActivityEnabled: serializer.fromJson(json['isActivityEnabled']), + order: serializer.fromJson(json['order']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'description': serializer.toJson(description), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'thumbnailAssetId': serializer.toJson(thumbnailAssetId), + 'isActivityEnabled': serializer.toJson(isActivityEnabled), + 'order': serializer.toJson(order), + }; + } + + RemoteAlbumEntityData copyWith({ + String? id, + String? name, + String? description, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + Value thumbnailAssetId = const Value.absent(), + bool? isActivityEnabled, + int? order, + }) => RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId.present + ? thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + RemoteAlbumEntityData copyWithCompanion(RemoteAlbumEntityCompanion data) { + return RemoteAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + description: data.description.present + ? data.description.value + : this.description, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + thumbnailAssetId: data.thumbnailAssetId.present + ? data.thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: data.isActivityEnabled.present + ? data.isActivityEnabled.value + : this.isActivityEnabled, + order: data.order.present ? data.order.value : this.order, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.description == this.description && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.thumbnailAssetId == this.thumbnailAssetId && + other.isActivityEnabled == this.isActivityEnabled && + other.order == this.order); +} + +class RemoteAlbumEntityCompanion + extends UpdateCompanion { + final Value id; + final Value name; + final Value description; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value thumbnailAssetId; + final Value isActivityEnabled; + final Value order; + const RemoteAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + this.order = const Value.absent(), + }); + RemoteAlbumEntityCompanion.insert({ + required String id, + required String name, + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + required int order, + }) : id = Value(id), + name = Value(name), + ownerId = Value(ownerId), + order = Value(order); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? description, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? thumbnailAssetId, + Expression? isActivityEnabled, + Expression? order, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (description != null) 'description': description, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (thumbnailAssetId != null) 'thumbnail_asset_id': thumbnailAssetId, + if (isActivityEnabled != null) 'is_activity_enabled': isActivityEnabled, + if (order != null) 'order': order, + }); + } + + RemoteAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? description, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? thumbnailAssetId, + Value? isActivityEnabled, + Value? order, + }) { + return RemoteAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId ?? this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (thumbnailAssetId.present) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId.value); + } + if (isActivityEnabled.present) { + map['is_activity_enabled'] = Variable(isActivityEnabled.value); + } + if (order.present) { + map['order'] = Variable(order.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + RemoteAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + ); + } + + @override + RemoteAlbumAssetEntity createAlias(String alias) { + return RemoteAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const RemoteAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory RemoteAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + RemoteAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + RemoteAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + RemoteAlbumAssetEntityData copyWithCompanion( + RemoteAlbumAssetEntityCompanion data, + ) { + return RemoteAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class RemoteAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const RemoteAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + RemoteAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + RemoteAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { + return RemoteAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumUserEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumUserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn role = GeneratedColumn( + 'role', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [albumId, userId, role]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_user_entity'; + @override + Set get $primaryKey => {albumId, userId}; + @override + RemoteAlbumUserEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumUserEntityData( + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + role: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}role'], + )!, + ); + } + + @override + RemoteAlbumUserEntity createAlias(String alias) { + return RemoteAlbumUserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumUserEntityData extends DataClass + implements Insertable { + final String albumId; + final String userId; + final int role; + const RemoteAlbumUserEntityData({ + required this.albumId, + required this.userId, + required this.role, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['album_id'] = Variable(albumId); + map['user_id'] = Variable(userId); + map['role'] = Variable(role); + return map; + } + + factory RemoteAlbumUserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumUserEntityData( + albumId: serializer.fromJson(json['albumId']), + userId: serializer.fromJson(json['userId']), + role: serializer.fromJson(json['role']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'albumId': serializer.toJson(albumId), + 'userId': serializer.toJson(userId), + 'role': serializer.toJson(role), + }; + } + + RemoteAlbumUserEntityData copyWith({ + String? albumId, + String? userId, + int? role, + }) => RemoteAlbumUserEntityData( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + RemoteAlbumUserEntityData copyWithCompanion( + RemoteAlbumUserEntityCompanion data, + ) { + return RemoteAlbumUserEntityData( + albumId: data.albumId.present ? data.albumId.value : this.albumId, + userId: data.userId.present ? data.userId.value : this.userId, + role: data.role.present ? data.role.value : this.role, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityData(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(albumId, userId, role); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumUserEntityData && + other.albumId == this.albumId && + other.userId == this.userId && + other.role == this.role); +} + +class RemoteAlbumUserEntityCompanion + extends UpdateCompanion { + final Value albumId; + final Value userId; + final Value role; + const RemoteAlbumUserEntityCompanion({ + this.albumId = const Value.absent(), + this.userId = const Value.absent(), + this.role = const Value.absent(), + }); + RemoteAlbumUserEntityCompanion.insert({ + required String albumId, + required String userId, + required int role, + }) : albumId = Value(albumId), + userId = Value(userId), + role = Value(role); + static Insertable custom({ + Expression? albumId, + Expression? userId, + Expression? role, + }) { + return RawValuesInsertable({ + if (albumId != null) 'album_id': albumId, + if (userId != null) 'user_id': userId, + if (role != null) 'role': role, + }); + } + + RemoteAlbumUserEntityCompanion copyWith({ + Value? albumId, + Value? userId, + Value? role, + }) { + return RemoteAlbumUserEntityCompanion( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (role.present) { + map['role'] = Variable(role.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityCompanion(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } +} + +class MemoryEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn data = GeneratedColumn( + 'data', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isSaved = GeneratedColumn( + 'is_saved', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn memoryAt = GeneratedColumn( + 'memory_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: true, + ); + late final GeneratedColumn seenAt = GeneratedColumn( + 'seen_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn showAt = GeneratedColumn( + 'show_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn hideAt = GeneratedColumn( + 'hide_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_entity'; + @override + Set get $primaryKey => {id}; + @override + MemoryEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + data: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}data'], + )!, + isSaved: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_saved'], + )!, + memoryAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}memory_at'], + )!, + seenAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}seen_at'], + ), + showAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}show_at'], + ), + hideAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}hide_at'], + ), + ); + } + + @override + MemoryEntity createAlias(String alias) { + return MemoryEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final DateTime? deletedAt; + final String ownerId; + final int type; + final String data; + final bool isSaved; + final DateTime memoryAt; + final DateTime? seenAt; + final DateTime? showAt; + final DateTime? hideAt; + const MemoryEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + map['owner_id'] = Variable(ownerId); + map['type'] = Variable(type); + map['data'] = Variable(data); + map['is_saved'] = Variable(isSaved); + map['memory_at'] = Variable(memoryAt); + if (!nullToAbsent || seenAt != null) { + map['seen_at'] = Variable(seenAt); + } + if (!nullToAbsent || showAt != null) { + map['show_at'] = Variable(showAt); + } + if (!nullToAbsent || hideAt != null) { + map['hide_at'] = Variable(hideAt); + } + return map; + } + + factory MemoryEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + deletedAt: serializer.fromJson(json['deletedAt']), + ownerId: serializer.fromJson(json['ownerId']), + type: serializer.fromJson(json['type']), + data: serializer.fromJson(json['data']), + isSaved: serializer.fromJson(json['isSaved']), + memoryAt: serializer.fromJson(json['memoryAt']), + seenAt: serializer.fromJson(json['seenAt']), + showAt: serializer.fromJson(json['showAt']), + hideAt: serializer.fromJson(json['hideAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'deletedAt': serializer.toJson(deletedAt), + 'ownerId': serializer.toJson(ownerId), + 'type': serializer.toJson(type), + 'data': serializer.toJson(data), + 'isSaved': serializer.toJson(isSaved), + 'memoryAt': serializer.toJson(memoryAt), + 'seenAt': serializer.toJson(seenAt), + 'showAt': serializer.toJson(showAt), + 'hideAt': serializer.toJson(hideAt), + }; + } + + MemoryEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + Value deletedAt = const Value.absent(), + String? ownerId, + int? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + Value seenAt = const Value.absent(), + Value showAt = const Value.absent(), + Value hideAt = const Value.absent(), + }) => MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); + MemoryEntityData copyWithCompanion(MemoryEntityCompanion data) { + return MemoryEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + type: data.type.present ? data.type.value : this.type, + data: data.data.present ? data.data.value : this.data, + isSaved: data.isSaved.present ? data.isSaved.value : this.isSaved, + memoryAt: data.memoryAt.present ? data.memoryAt.value : this.memoryAt, + seenAt: data.seenAt.present ? data.seenAt.value : this.seenAt, + showAt: data.showAt.present ? data.showAt.value : this.showAt, + hideAt: data.hideAt.present ? data.hideAt.value : this.hideAt, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.deletedAt == this.deletedAt && + other.ownerId == this.ownerId && + other.type == this.type && + other.data == this.data && + other.isSaved == this.isSaved && + other.memoryAt == this.memoryAt && + other.seenAt == this.seenAt && + other.showAt == this.showAt && + other.hideAt == this.hideAt); +} + +class MemoryEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value deletedAt; + final Value ownerId; + final Value type; + final Value data; + final Value isSaved; + final Value memoryAt; + final Value seenAt; + final Value showAt; + final Value hideAt; + const MemoryEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.type = const Value.absent(), + this.data = const Value.absent(), + this.isSaved = const Value.absent(), + this.memoryAt = const Value.absent(), + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }); + MemoryEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + required String ownerId, + required int type, + required String data, + this.isSaved = const Value.absent(), + required DateTime memoryAt, + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + type = Value(type), + data = Value(data), + memoryAt = Value(memoryAt); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? deletedAt, + Expression? ownerId, + Expression? type, + Expression? data, + Expression? isSaved, + Expression? memoryAt, + Expression? seenAt, + Expression? showAt, + Expression? hideAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (deletedAt != null) 'deleted_at': deletedAt, + if (ownerId != null) 'owner_id': ownerId, + if (type != null) 'type': type, + if (data != null) 'data': data, + if (isSaved != null) 'is_saved': isSaved, + if (memoryAt != null) 'memory_at': memoryAt, + if (seenAt != null) 'seen_at': seenAt, + if (showAt != null) 'show_at': showAt, + if (hideAt != null) 'hide_at': hideAt, + }); + } + + MemoryEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? deletedAt, + Value? ownerId, + Value? type, + Value? data, + Value? isSaved, + Value? memoryAt, + Value? seenAt, + Value? showAt, + Value? hideAt, + }) { + return MemoryEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt ?? this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt ?? this.seenAt, + showAt: showAt ?? this.showAt, + hideAt: hideAt ?? this.hideAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (data.present) { + map['data'] = Variable(data.value); + } + if (isSaved.present) { + map['is_saved'] = Variable(isSaved.value); + } + if (memoryAt.present) { + map['memory_at'] = Variable(memoryAt.value); + } + if (seenAt.present) { + map['seen_at'] = Variable(seenAt.value); + } + if (showAt.present) { + map['show_at'] = Variable(showAt.value); + } + if (hideAt.present) { + map['hide_at'] = Variable(hideAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } +} + +class MemoryAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn memoryId = GeneratedColumn( + 'memory_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, memoryId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_asset_entity'; + @override + Set get $primaryKey => {assetId, memoryId}; + @override + MemoryAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + memoryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}memory_id'], + )!, + ); + } + + @override + MemoryAssetEntity createAlias(String alias) { + return MemoryAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String memoryId; + const MemoryAssetEntityData({required this.assetId, required this.memoryId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['memory_id'] = Variable(memoryId); + return map; + } + + factory MemoryAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + memoryId: serializer.fromJson(json['memoryId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'memoryId': serializer.toJson(memoryId), + }; + } + + MemoryAssetEntityData copyWith({String? assetId, String? memoryId}) => + MemoryAssetEntityData( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + MemoryAssetEntityData copyWithCompanion(MemoryAssetEntityCompanion data) { + return MemoryAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + memoryId: data.memoryId.present ? data.memoryId.value : this.memoryId, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, memoryId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryAssetEntityData && + other.assetId == this.assetId && + other.memoryId == this.memoryId); +} + +class MemoryAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value memoryId; + const MemoryAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.memoryId = const Value.absent(), + }); + MemoryAssetEntityCompanion.insert({ + required String assetId, + required String memoryId, + }) : assetId = Value(assetId), + memoryId = Value(memoryId); + static Insertable custom({ + Expression? assetId, + Expression? memoryId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (memoryId != null) 'memory_id': memoryId, + }); + } + + MemoryAssetEntityCompanion copyWith({ + Value? assetId, + Value? memoryId, + }) { + return MemoryAssetEntityCompanion( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (memoryId.present) { + map['memory_id'] = Variable(memoryId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } +} + +class PersonEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PersonEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn faceAssetId = GeneratedColumn( + 'face_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); + late final GeneratedColumn isHidden = GeneratedColumn( + 'is_hidden', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); + late final GeneratedColumn color = GeneratedColumn( + 'color', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn birthDate = GeneratedColumn( + 'birth_date', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'person_entity'; + @override + Set get $primaryKey => {id}; + @override + PersonEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PersonEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + faceAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}face_asset_id'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + isHidden: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_hidden'], + )!, + color: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}color'], + ), + birthDate: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}birth_date'], + ), + ); + } + + @override + PersonEntity createAlias(String alias) { + return PersonEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PersonEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String name; + final String? faceAssetId; + final bool isFavorite; + final bool isHidden; + final String? color; + final DateTime? birthDate; + const PersonEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['name'] = Variable(name); + if (!nullToAbsent || faceAssetId != null) { + map['face_asset_id'] = Variable(faceAssetId); + } + map['is_favorite'] = Variable(isFavorite); + map['is_hidden'] = Variable(isHidden); + if (!nullToAbsent || color != null) { + map['color'] = Variable(color); + } + if (!nullToAbsent || birthDate != null) { + map['birth_date'] = Variable(birthDate); + } + return map; + } + + factory PersonEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PersonEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + name: serializer.fromJson(json['name']), + faceAssetId: serializer.fromJson(json['faceAssetId']), + isFavorite: serializer.fromJson(json['isFavorite']), + isHidden: serializer.fromJson(json['isHidden']), + color: serializer.fromJson(json['color']), + birthDate: serializer.fromJson(json['birthDate']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'name': serializer.toJson(name), + 'faceAssetId': serializer.toJson(faceAssetId), + 'isFavorite': serializer.toJson(isFavorite), + 'isHidden': serializer.toJson(isHidden), + 'color': serializer.toJson(color), + 'birthDate': serializer.toJson(birthDate), + }; + } + + PersonEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + Value faceAssetId = const Value.absent(), + bool? isFavorite, + bool? isHidden, + Value color = const Value.absent(), + Value birthDate = const Value.absent(), + }) => PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); + PersonEntityData copyWithCompanion(PersonEntityCompanion data) { + return PersonEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + name: data.name.present ? data.name.value : this.name, + faceAssetId: data.faceAssetId.present + ? data.faceAssetId.value + : this.faceAssetId, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, + color: data.color.present ? data.color.value : this.color, + birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, + ); + } + + @override + String toString() { + return (StringBuffer('PersonEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PersonEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.name == this.name && + other.faceAssetId == this.faceAssetId && + other.isFavorite == this.isFavorite && + other.isHidden == this.isHidden && + other.color == this.color && + other.birthDate == this.birthDate); +} + +class PersonEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value name; + final Value faceAssetId; + final Value isFavorite; + final Value isHidden; + final Value color; + final Value birthDate; + const PersonEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.name = const Value.absent(), + this.faceAssetId = const Value.absent(), + this.isFavorite = const Value.absent(), + this.isHidden = const Value.absent(), + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }); + PersonEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String name, + this.faceAssetId = const Value.absent(), + required bool isFavorite, + required bool isHidden, + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + name = Value(name), + isFavorite = Value(isFavorite), + isHidden = Value(isHidden); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? name, + Expression? faceAssetId, + Expression? isFavorite, + Expression? isHidden, + Expression? color, + Expression? birthDate, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (name != null) 'name': name, + if (faceAssetId != null) 'face_asset_id': faceAssetId, + if (isFavorite != null) 'is_favorite': isFavorite, + if (isHidden != null) 'is_hidden': isHidden, + if (color != null) 'color': color, + if (birthDate != null) 'birth_date': birthDate, + }); + } + + PersonEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? name, + Value? faceAssetId, + Value? isFavorite, + Value? isHidden, + Value? color, + Value? birthDate, + }) { + return PersonEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId ?? this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color ?? this.color, + birthDate: birthDate ?? this.birthDate, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (faceAssetId.present) { + map['face_asset_id'] = Variable(faceAssetId.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (isHidden.present) { + map['is_hidden'] = Variable(isHidden.value); + } + if (color.present) { + map['color'] = Variable(color.value); + } + if (birthDate.present) { + map['birth_date'] = Variable(birthDate.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PersonEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } +} + +class AssetFaceEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + AssetFaceEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn personId = GeneratedColumn( + 'person_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES person_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn imageWidth = GeneratedColumn( + 'image_width', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn imageHeight = GeneratedColumn( + 'image_height', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX1 = GeneratedColumn( + 'bounding_box_x1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY1 = GeneratedColumn( + 'bounding_box_y1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX2 = GeneratedColumn( + 'bounding_box_x2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY2 = GeneratedColumn( + 'bounding_box_y2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn sourceType = GeneratedColumn( + 'source_type', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'asset_face_entity'; + @override + Set get $primaryKey => {id}; + @override + AssetFaceEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return AssetFaceEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + personId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}person_id'], + ), + imageWidth: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_width'], + )!, + imageHeight: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_height'], + )!, + boundingBoxX1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x1'], + )!, + boundingBoxY1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y1'], + )!, + boundingBoxX2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x2'], + )!, + boundingBoxY2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y2'], + )!, + sourceType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}source_type'], + )!, + ); + } + + @override + AssetFaceEntity createAlias(String alias) { + return AssetFaceEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class AssetFaceEntityData extends DataClass + implements Insertable { + final String id; + final String assetId; + final String? personId; + final int imageWidth; + final int imageHeight; + final int boundingBoxX1; + final int boundingBoxY1; + final int boundingBoxX2; + final int boundingBoxY2; + final String sourceType; + const AssetFaceEntityData({ + required this.id, + required this.assetId, + this.personId, + required this.imageWidth, + required this.imageHeight, + required this.boundingBoxX1, + required this.boundingBoxY1, + required this.boundingBoxX2, + required this.boundingBoxY2, + required this.sourceType, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || personId != null) { + map['person_id'] = Variable(personId); + } + map['image_width'] = Variable(imageWidth); + map['image_height'] = Variable(imageHeight); + map['bounding_box_x1'] = Variable(boundingBoxX1); + map['bounding_box_y1'] = Variable(boundingBoxY1); + map['bounding_box_x2'] = Variable(boundingBoxX2); + map['bounding_box_y2'] = Variable(boundingBoxY2); + map['source_type'] = Variable(sourceType); + return map; + } + + factory AssetFaceEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return AssetFaceEntityData( + id: serializer.fromJson(json['id']), + assetId: serializer.fromJson(json['assetId']), + personId: serializer.fromJson(json['personId']), + imageWidth: serializer.fromJson(json['imageWidth']), + imageHeight: serializer.fromJson(json['imageHeight']), + boundingBoxX1: serializer.fromJson(json['boundingBoxX1']), + boundingBoxY1: serializer.fromJson(json['boundingBoxY1']), + boundingBoxX2: serializer.fromJson(json['boundingBoxX2']), + boundingBoxY2: serializer.fromJson(json['boundingBoxY2']), + sourceType: serializer.fromJson(json['sourceType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'assetId': serializer.toJson(assetId), + 'personId': serializer.toJson(personId), + 'imageWidth': serializer.toJson(imageWidth), + 'imageHeight': serializer.toJson(imageHeight), + 'boundingBoxX1': serializer.toJson(boundingBoxX1), + 'boundingBoxY1': serializer.toJson(boundingBoxY1), + 'boundingBoxX2': serializer.toJson(boundingBoxX2), + 'boundingBoxY2': serializer.toJson(boundingBoxY2), + 'sourceType': serializer.toJson(sourceType), + }; + } + + AssetFaceEntityData copyWith({ + String? id, + String? assetId, + Value personId = const Value.absent(), + int? imageWidth, + int? imageHeight, + int? boundingBoxX1, + int? boundingBoxY1, + int? boundingBoxX2, + int? boundingBoxY2, + String? sourceType, + }) => AssetFaceEntityData( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId.present ? personId.value : this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + AssetFaceEntityData copyWithCompanion(AssetFaceEntityCompanion data) { + return AssetFaceEntityData( + id: data.id.present ? data.id.value : this.id, + assetId: data.assetId.present ? data.assetId.value : this.assetId, + personId: data.personId.present ? data.personId.value : this.personId, + imageWidth: data.imageWidth.present + ? data.imageWidth.value + : this.imageWidth, + imageHeight: data.imageHeight.present + ? data.imageHeight.value + : this.imageHeight, + boundingBoxX1: data.boundingBoxX1.present + ? data.boundingBoxX1.value + : this.boundingBoxX1, + boundingBoxY1: data.boundingBoxY1.present + ? data.boundingBoxY1.value + : this.boundingBoxY1, + boundingBoxX2: data.boundingBoxX2.present + ? data.boundingBoxX2.value + : this.boundingBoxX2, + boundingBoxY2: data.boundingBoxY2.present + ? data.boundingBoxY2.value + : this.boundingBoxY2, + sourceType: data.sourceType.present + ? data.sourceType.value + : this.sourceType, + ); + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityData(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is AssetFaceEntityData && + other.id == this.id && + other.assetId == this.assetId && + other.personId == this.personId && + other.imageWidth == this.imageWidth && + other.imageHeight == this.imageHeight && + other.boundingBoxX1 == this.boundingBoxX1 && + other.boundingBoxY1 == this.boundingBoxY1 && + other.boundingBoxX2 == this.boundingBoxX2 && + other.boundingBoxY2 == this.boundingBoxY2 && + other.sourceType == this.sourceType); +} + +class AssetFaceEntityCompanion extends UpdateCompanion { + final Value id; + final Value assetId; + final Value personId; + final Value imageWidth; + final Value imageHeight; + final Value boundingBoxX1; + final Value boundingBoxY1; + final Value boundingBoxX2; + final Value boundingBoxY2; + final Value sourceType; + const AssetFaceEntityCompanion({ + this.id = const Value.absent(), + this.assetId = const Value.absent(), + this.personId = const Value.absent(), + this.imageWidth = const Value.absent(), + this.imageHeight = const Value.absent(), + this.boundingBoxX1 = const Value.absent(), + this.boundingBoxY1 = const Value.absent(), + this.boundingBoxX2 = const Value.absent(), + this.boundingBoxY2 = const Value.absent(), + this.sourceType = const Value.absent(), + }); + AssetFaceEntityCompanion.insert({ + required String id, + required String assetId, + this.personId = const Value.absent(), + required int imageWidth, + required int imageHeight, + required int boundingBoxX1, + required int boundingBoxY1, + required int boundingBoxX2, + required int boundingBoxY2, + required String sourceType, + }) : id = Value(id), + assetId = Value(assetId), + imageWidth = Value(imageWidth), + imageHeight = Value(imageHeight), + boundingBoxX1 = Value(boundingBoxX1), + boundingBoxY1 = Value(boundingBoxY1), + boundingBoxX2 = Value(boundingBoxX2), + boundingBoxY2 = Value(boundingBoxY2), + sourceType = Value(sourceType); + static Insertable custom({ + Expression? id, + Expression? assetId, + Expression? personId, + Expression? imageWidth, + Expression? imageHeight, + Expression? boundingBoxX1, + Expression? boundingBoxY1, + Expression? boundingBoxX2, + Expression? boundingBoxY2, + Expression? sourceType, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (assetId != null) 'asset_id': assetId, + if (personId != null) 'person_id': personId, + if (imageWidth != null) 'image_width': imageWidth, + if (imageHeight != null) 'image_height': imageHeight, + if (boundingBoxX1 != null) 'bounding_box_x1': boundingBoxX1, + if (boundingBoxY1 != null) 'bounding_box_y1': boundingBoxY1, + if (boundingBoxX2 != null) 'bounding_box_x2': boundingBoxX2, + if (boundingBoxY2 != null) 'bounding_box_y2': boundingBoxY2, + if (sourceType != null) 'source_type': sourceType, + }); + } + + AssetFaceEntityCompanion copyWith({ + Value? id, + Value? assetId, + Value? personId, + Value? imageWidth, + Value? imageHeight, + Value? boundingBoxX1, + Value? boundingBoxY1, + Value? boundingBoxX2, + Value? boundingBoxY2, + Value? sourceType, + }) { + return AssetFaceEntityCompanion( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId ?? this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (personId.present) { + map['person_id'] = Variable(personId.value); + } + if (imageWidth.present) { + map['image_width'] = Variable(imageWidth.value); + } + if (imageHeight.present) { + map['image_height'] = Variable(imageHeight.value); + } + if (boundingBoxX1.present) { + map['bounding_box_x1'] = Variable(boundingBoxX1.value); + } + if (boundingBoxY1.present) { + map['bounding_box_y1'] = Variable(boundingBoxY1.value); + } + if (boundingBoxX2.present) { + map['bounding_box_x2'] = Variable(boundingBoxX2.value); + } + if (boundingBoxY2.present) { + map['bounding_box_y2'] = Variable(boundingBoxY2.value); + } + if (sourceType.present) { + map['source_type'] = Variable(sourceType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityCompanion(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } +} + +class DatabaseAtV5 extends GeneratedDatabase { + DatabaseAtV5(QueryExecutor e) : super(e); + late final UserEntity userEntity = UserEntity(this); + late final RemoteAssetEntity remoteAssetEntity = RemoteAssetEntity(this); + late final StackEntity stackEntity = StackEntity(this); + late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); + late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); + late final LocalAlbumAssetEntity localAlbumAssetEntity = + LocalAlbumAssetEntity(this); + late final Index idxLocalAssetChecksum = Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + late final Index uQRemoteAssetOwnerChecksum = Index( + 'UQ_remote_asset_owner_checksum', + 'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)', + ); + late final Index idxRemoteAssetChecksum = Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); + late final UserMetadataEntity userMetadataEntity = UserMetadataEntity(this); + late final PartnerEntity partnerEntity = PartnerEntity(this); + late final RemoteExifEntity remoteExifEntity = RemoteExifEntity(this); + late final RemoteAlbumEntity remoteAlbumEntity = RemoteAlbumEntity(this); + late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = + RemoteAlbumAssetEntity(this); + late final RemoteAlbumUserEntity remoteAlbumUserEntity = + RemoteAlbumUserEntity(this); + late final MemoryEntity memoryEntity = MemoryEntity(this); + late final MemoryAssetEntity memoryAssetEntity = MemoryAssetEntity(this); + late final PersonEntity personEntity = PersonEntity(this); + late final AssetFaceEntity assetFaceEntity = AssetFaceEntity(this); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [ + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAssetChecksum, + uQRemoteAssetOwnerChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + ]; + @override + int get schemaVersion => 5; + @override + DriftDatabaseOptions get options => + const DriftDatabaseOptions(storeDateTimeAsText: true); +} diff --git a/mobile/test/drift/main/generated/schema_v6.dart b/mobile/test/drift/main/generated/schema_v6.dart new file mode 100644 index 0000000000..97b91caa07 --- /dev/null +++ b/mobile/test/drift/main/generated/schema_v6.dart @@ -0,0 +1,6448 @@ +// dart format width=80 +// GENERATED CODE, DO NOT EDIT BY HAND. +// ignore_for_file: type=lint +import 'package:drift/drift.dart'; + +class UserEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isAdmin = GeneratedColumn( + 'is_admin', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_admin" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn email = GeneratedColumn( + 'email', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn hasProfileImage = GeneratedColumn( + 'has_profile_image', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("has_profile_image" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn profileChangedAt = + GeneratedColumn( + 'profile_changed_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + @override + List get $columns => [ + id, + name, + isAdmin, + email, + hasProfileImage, + profileChangedAt, + updatedAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_entity'; + @override + Set get $primaryKey => {id}; + @override + UserEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + isAdmin: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_admin'], + )!, + email: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}email'], + )!, + hasProfileImage: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}has_profile_image'], + )!, + profileChangedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}profile_changed_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ); + } + + @override + UserEntity createAlias(String alias) { + return UserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserEntityData extends DataClass implements Insertable { + final String id; + final String name; + final bool isAdmin; + final String email; + final bool hasProfileImage; + final DateTime profileChangedAt; + final DateTime updatedAt; + const UserEntityData({ + required this.id, + required this.name, + required this.isAdmin, + required this.email, + required this.hasProfileImage, + required this.profileChangedAt, + required this.updatedAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['is_admin'] = Variable(isAdmin); + map['email'] = Variable(email); + map['has_profile_image'] = Variable(hasProfileImage); + map['profile_changed_at'] = Variable(profileChangedAt); + map['updated_at'] = Variable(updatedAt); + return map; + } + + factory UserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + isAdmin: serializer.fromJson(json['isAdmin']), + email: serializer.fromJson(json['email']), + hasProfileImage: serializer.fromJson(json['hasProfileImage']), + profileChangedAt: serializer.fromJson(json['profileChangedAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'isAdmin': serializer.toJson(isAdmin), + 'email': serializer.toJson(email), + 'hasProfileImage': serializer.toJson(hasProfileImage), + 'profileChangedAt': serializer.toJson(profileChangedAt), + 'updatedAt': serializer.toJson(updatedAt), + }; + } + + UserEntityData copyWith({ + String? id, + String? name, + bool? isAdmin, + String? email, + bool? hasProfileImage, + DateTime? profileChangedAt, + DateTime? updatedAt, + }) => UserEntityData( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + updatedAt: updatedAt ?? this.updatedAt, + ); + UserEntityData copyWithCompanion(UserEntityCompanion data) { + return UserEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + isAdmin: data.isAdmin.present ? data.isAdmin.value : this.isAdmin, + email: data.email.present ? data.email.value : this.email, + hasProfileImage: data.hasProfileImage.present + ? data.hasProfileImage.value + : this.hasProfileImage, + profileChangedAt: data.profileChangedAt.present + ? data.profileChangedAt.value + : this.profileChangedAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ); + } + + @override + String toString() { + return (StringBuffer('UserEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isAdmin: $isAdmin, ') + ..write('email: $email, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('updatedAt: $updatedAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + isAdmin, + email, + hasProfileImage, + profileChangedAt, + updatedAt, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserEntityData && + other.id == this.id && + other.name == this.name && + other.isAdmin == this.isAdmin && + other.email == this.email && + other.hasProfileImage == this.hasProfileImage && + other.profileChangedAt == this.profileChangedAt && + other.updatedAt == this.updatedAt); +} + +class UserEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value isAdmin; + final Value email; + final Value hasProfileImage; + final Value profileChangedAt; + final Value updatedAt; + const UserEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.isAdmin = const Value.absent(), + this.email = const Value.absent(), + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + this.updatedAt = const Value.absent(), + }); + UserEntityCompanion.insert({ + required String id, + required String name, + this.isAdmin = const Value.absent(), + required String email, + this.hasProfileImage = const Value.absent(), + this.profileChangedAt = const Value.absent(), + this.updatedAt = const Value.absent(), + }) : id = Value(id), + name = Value(name), + email = Value(email); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? isAdmin, + Expression? email, + Expression? hasProfileImage, + Expression? profileChangedAt, + Expression? updatedAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (isAdmin != null) 'is_admin': isAdmin, + if (email != null) 'email': email, + if (hasProfileImage != null) 'has_profile_image': hasProfileImage, + if (profileChangedAt != null) 'profile_changed_at': profileChangedAt, + if (updatedAt != null) 'updated_at': updatedAt, + }); + } + + UserEntityCompanion copyWith({ + Value? id, + Value? name, + Value? isAdmin, + Value? email, + Value? hasProfileImage, + Value? profileChangedAt, + Value? updatedAt, + }) { + return UserEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + isAdmin: isAdmin ?? this.isAdmin, + email: email ?? this.email, + hasProfileImage: hasProfileImage ?? this.hasProfileImage, + profileChangedAt: profileChangedAt ?? this.profileChangedAt, + updatedAt: updatedAt ?? this.updatedAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (isAdmin.present) { + map['is_admin'] = Variable(isAdmin.value); + } + if (email.present) { + map['email'] = Variable(email.value); + } + if (hasProfileImage.present) { + map['has_profile_image'] = Variable(hasProfileImage.value); + } + if (profileChangedAt.present) { + map['profile_changed_at'] = Variable(profileChangedAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('isAdmin: $isAdmin, ') + ..write('email: $email, ') + ..write('hasProfileImage: $hasProfileImage, ') + ..write('profileChangedAt: $profileChangedAt, ') + ..write('updatedAt: $updatedAt') + ..write(')')) + .toString(); + } +} + +class RemoteAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn localDateTime = + GeneratedColumn( + 'local_date_time', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn thumbHash = GeneratedColumn( + 'thumb_hash', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn livePhotoVideoId = GeneratedColumn( + 'live_photo_video_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn visibility = GeneratedColumn( + 'visibility', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn stackId = GeneratedColumn( + 'stack_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn libraryId = GeneratedColumn( + 'library_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + libraryId, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAssetEntityData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + )!, + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + localDateTime: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}local_date_time'], + ), + thumbHash: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumb_hash'], + ), + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + livePhotoVideoId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}live_photo_video_id'], + ), + visibility: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}visibility'], + )!, + stackId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}stack_id'], + ), + libraryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}library_id'], + ), + ); + } + + @override + RemoteAssetEntity createAlias(String alias) { + return RemoteAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String checksum; + final bool isFavorite; + final String ownerId; + final DateTime? localDateTime; + final String? thumbHash; + final DateTime? deletedAt; + final String? livePhotoVideoId; + final int visibility; + final String? stackId; + final String? libraryId; + const RemoteAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + required this.checksum, + required this.isFavorite, + required this.ownerId, + this.localDateTime, + this.thumbHash, + this.deletedAt, + this.livePhotoVideoId, + required this.visibility, + this.stackId, + this.libraryId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + map['checksum'] = Variable(checksum); + map['is_favorite'] = Variable(isFavorite); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || localDateTime != null) { + map['local_date_time'] = Variable(localDateTime); + } + if (!nullToAbsent || thumbHash != null) { + map['thumb_hash'] = Variable(thumbHash); + } + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + if (!nullToAbsent || livePhotoVideoId != null) { + map['live_photo_video_id'] = Variable(livePhotoVideoId); + } + map['visibility'] = Variable(visibility); + if (!nullToAbsent || stackId != null) { + map['stack_id'] = Variable(stackId); + } + if (!nullToAbsent || libraryId != null) { + map['library_id'] = Variable(libraryId); + } + return map; + } + + factory RemoteAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + ownerId: serializer.fromJson(json['ownerId']), + localDateTime: serializer.fromJson(json['localDateTime']), + thumbHash: serializer.fromJson(json['thumbHash']), + deletedAt: serializer.fromJson(json['deletedAt']), + livePhotoVideoId: serializer.fromJson(json['livePhotoVideoId']), + visibility: serializer.fromJson(json['visibility']), + stackId: serializer.fromJson(json['stackId']), + libraryId: serializer.fromJson(json['libraryId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'ownerId': serializer.toJson(ownerId), + 'localDateTime': serializer.toJson(localDateTime), + 'thumbHash': serializer.toJson(thumbHash), + 'deletedAt': serializer.toJson(deletedAt), + 'livePhotoVideoId': serializer.toJson(livePhotoVideoId), + 'visibility': serializer.toJson(visibility), + 'stackId': serializer.toJson(stackId), + 'libraryId': serializer.toJson(libraryId), + }; + } + + RemoteAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + String? checksum, + bool? isFavorite, + String? ownerId, + Value localDateTime = const Value.absent(), + Value thumbHash = const Value.absent(), + Value deletedAt = const Value.absent(), + Value livePhotoVideoId = const Value.absent(), + int? visibility, + Value stackId = const Value.absent(), + Value libraryId = const Value.absent(), + }) => RemoteAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime.present + ? localDateTime.value + : this.localDateTime, + thumbHash: thumbHash.present ? thumbHash.value : this.thumbHash, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + livePhotoVideoId: livePhotoVideoId.present + ? livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId.present ? stackId.value : this.stackId, + libraryId: libraryId.present ? libraryId.value : this.libraryId, + ); + RemoteAssetEntityData copyWithCompanion(RemoteAssetEntityCompanion data) { + return RemoteAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + localDateTime: data.localDateTime.present + ? data.localDateTime.value + : this.localDateTime, + thumbHash: data.thumbHash.present ? data.thumbHash.value : this.thumbHash, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + livePhotoVideoId: data.livePhotoVideoId.present + ? data.livePhotoVideoId.value + : this.livePhotoVideoId, + visibility: data.visibility.present + ? data.visibility.value + : this.visibility, + stackId: data.stackId.present ? data.stackId.value : this.stackId, + libraryId: data.libraryId.present ? data.libraryId.value : this.libraryId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId, ') + ..write('libraryId: $libraryId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + ownerId, + localDateTime, + thumbHash, + deletedAt, + livePhotoVideoId, + visibility, + stackId, + libraryId, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.ownerId == this.ownerId && + other.localDateTime == this.localDateTime && + other.thumbHash == this.thumbHash && + other.deletedAt == this.deletedAt && + other.livePhotoVideoId == this.livePhotoVideoId && + other.visibility == this.visibility && + other.stackId == this.stackId && + other.libraryId == this.libraryId); +} + +class RemoteAssetEntityCompanion + extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value ownerId; + final Value localDateTime; + final Value thumbHash; + final Value deletedAt; + final Value livePhotoVideoId; + final Value visibility; + final Value stackId; + final Value libraryId; + const RemoteAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.ownerId = const Value.absent(), + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + this.visibility = const Value.absent(), + this.stackId = const Value.absent(), + this.libraryId = const Value.absent(), + }); + RemoteAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + required String checksum, + this.isFavorite = const Value.absent(), + required String ownerId, + this.localDateTime = const Value.absent(), + this.thumbHash = const Value.absent(), + this.deletedAt = const Value.absent(), + this.livePhotoVideoId = const Value.absent(), + required int visibility, + this.stackId = const Value.absent(), + this.libraryId = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id), + checksum = Value(checksum), + ownerId = Value(ownerId), + visibility = Value(visibility); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? ownerId, + Expression? localDateTime, + Expression? thumbHash, + Expression? deletedAt, + Expression? livePhotoVideoId, + Expression? visibility, + Expression? stackId, + Expression? libraryId, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (ownerId != null) 'owner_id': ownerId, + if (localDateTime != null) 'local_date_time': localDateTime, + if (thumbHash != null) 'thumb_hash': thumbHash, + if (deletedAt != null) 'deleted_at': deletedAt, + if (livePhotoVideoId != null) 'live_photo_video_id': livePhotoVideoId, + if (visibility != null) 'visibility': visibility, + if (stackId != null) 'stack_id': stackId, + if (libraryId != null) 'library_id': libraryId, + }); + } + + RemoteAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? ownerId, + Value? localDateTime, + Value? thumbHash, + Value? deletedAt, + Value? livePhotoVideoId, + Value? visibility, + Value? stackId, + Value? libraryId, + }) { + return RemoteAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + ownerId: ownerId ?? this.ownerId, + localDateTime: localDateTime ?? this.localDateTime, + thumbHash: thumbHash ?? this.thumbHash, + deletedAt: deletedAt ?? this.deletedAt, + livePhotoVideoId: livePhotoVideoId ?? this.livePhotoVideoId, + visibility: visibility ?? this.visibility, + stackId: stackId ?? this.stackId, + libraryId: libraryId ?? this.libraryId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (localDateTime.present) { + map['local_date_time'] = Variable(localDateTime.value); + } + if (thumbHash.present) { + map['thumb_hash'] = Variable(thumbHash.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (livePhotoVideoId.present) { + map['live_photo_video_id'] = Variable(livePhotoVideoId.value); + } + if (visibility.present) { + map['visibility'] = Variable(visibility.value); + } + if (stackId.present) { + map['stack_id'] = Variable(stackId.value); + } + if (libraryId.present) { + map['library_id'] = Variable(libraryId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('ownerId: $ownerId, ') + ..write('localDateTime: $localDateTime, ') + ..write('thumbHash: $thumbHash, ') + ..write('deletedAt: $deletedAt, ') + ..write('livePhotoVideoId: $livePhotoVideoId, ') + ..write('visibility: $visibility, ') + ..write('stackId: $stackId, ') + ..write('libraryId: $libraryId') + ..write(')')) + .toString(); + } +} + +class StackEntity extends Table with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + StackEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn primaryAssetId = GeneratedColumn( + 'primary_asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + primaryAssetId, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'stack_entity'; + @override + Set get $primaryKey => {id}; + @override + StackEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StackEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + primaryAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}primary_asset_id'], + )!, + ); + } + + @override + StackEntity createAlias(String alias) { + return StackEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class StackEntityData extends DataClass implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String primaryAssetId; + const StackEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.primaryAssetId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['primary_asset_id'] = Variable(primaryAssetId); + return map; + } + + factory StackEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StackEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + primaryAssetId: serializer.fromJson(json['primaryAssetId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'primaryAssetId': serializer.toJson(primaryAssetId), + }; + } + + StackEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? primaryAssetId, + }) => StackEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + StackEntityData copyWithCompanion(StackEntityCompanion data) { + return StackEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + primaryAssetId: data.primaryAssetId.present + ? data.primaryAssetId.value + : this.primaryAssetId, + ); + } + + @override + String toString() { + return (StringBuffer('StackEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(id, createdAt, updatedAt, ownerId, primaryAssetId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StackEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.primaryAssetId == this.primaryAssetId); +} + +class StackEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value primaryAssetId; + const StackEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.primaryAssetId = const Value.absent(), + }); + StackEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String primaryAssetId, + }) : id = Value(id), + ownerId = Value(ownerId), + primaryAssetId = Value(primaryAssetId); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? primaryAssetId, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (primaryAssetId != null) 'primary_asset_id': primaryAssetId, + }); + } + + StackEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? primaryAssetId, + }) { + return StackEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + primaryAssetId: primaryAssetId ?? this.primaryAssetId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (primaryAssetId.present) { + map['primary_asset_id'] = Variable(primaryAssetId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StackEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('primaryAssetId: $primaryAssetId') + ..write(')')) + .toString(); + } +} + +class LocalAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn durationInSeconds = GeneratedColumn( + 'duration_in_seconds', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn checksum = GeneratedColumn( + 'checksum', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [ + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_asset_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAssetEntityData( + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + durationInSeconds: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}duration_in_seconds'], + ), + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + checksum: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}checksum'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}orientation'], + )!, + ); + } + + @override + LocalAssetEntity createAlias(String alias) { + return LocalAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAssetEntityData extends DataClass + implements Insertable { + final String name; + final int type; + final DateTime createdAt; + final DateTime updatedAt; + final int? width; + final int? height; + final int? durationInSeconds; + final String id; + final String? checksum; + final bool isFavorite; + final int orientation; + const LocalAssetEntityData({ + required this.name, + required this.type, + required this.createdAt, + required this.updatedAt, + this.width, + this.height, + this.durationInSeconds, + required this.id, + this.checksum, + required this.isFavorite, + required this.orientation, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['name'] = Variable(name); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || durationInSeconds != null) { + map['duration_in_seconds'] = Variable(durationInSeconds); + } + map['id'] = Variable(id); + if (!nullToAbsent || checksum != null) { + map['checksum'] = Variable(checksum); + } + map['is_favorite'] = Variable(isFavorite); + map['orientation'] = Variable(orientation); + return map; + } + + factory LocalAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAssetEntityData( + name: serializer.fromJson(json['name']), + type: serializer.fromJson(json['type']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + width: serializer.fromJson(json['width']), + height: serializer.fromJson(json['height']), + durationInSeconds: serializer.fromJson(json['durationInSeconds']), + id: serializer.fromJson(json['id']), + checksum: serializer.fromJson(json['checksum']), + isFavorite: serializer.fromJson(json['isFavorite']), + orientation: serializer.fromJson(json['orientation']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'name': serializer.toJson(name), + 'type': serializer.toJson(type), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'width': serializer.toJson(width), + 'height': serializer.toJson(height), + 'durationInSeconds': serializer.toJson(durationInSeconds), + 'id': serializer.toJson(id), + 'checksum': serializer.toJson(checksum), + 'isFavorite': serializer.toJson(isFavorite), + 'orientation': serializer.toJson(orientation), + }; + } + + LocalAssetEntityData copyWith({ + String? name, + int? type, + DateTime? createdAt, + DateTime? updatedAt, + Value width = const Value.absent(), + Value height = const Value.absent(), + Value durationInSeconds = const Value.absent(), + String? id, + Value checksum = const Value.absent(), + bool? isFavorite, + int? orientation, + }) => LocalAssetEntityData( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width.present ? width.value : this.width, + height: height.present ? height.value : this.height, + durationInSeconds: durationInSeconds.present + ? durationInSeconds.value + : this.durationInSeconds, + id: id ?? this.id, + checksum: checksum.present ? checksum.value : this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + LocalAssetEntityData copyWithCompanion(LocalAssetEntityCompanion data) { + return LocalAssetEntityData( + name: data.name.present ? data.name.value : this.name, + type: data.type.present ? data.type.value : this.type, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + width: data.width.present ? data.width.value : this.width, + height: data.height.present ? data.height.value : this.height, + durationInSeconds: data.durationInSeconds.present + ? data.durationInSeconds.value + : this.durationInSeconds, + id: data.id.present ? data.id.value : this.id, + checksum: data.checksum.present ? data.checksum.value : this.checksum, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityData(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + name, + type, + createdAt, + updatedAt, + width, + height, + durationInSeconds, + id, + checksum, + isFavorite, + orientation, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAssetEntityData && + other.name == this.name && + other.type == this.type && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.width == this.width && + other.height == this.height && + other.durationInSeconds == this.durationInSeconds && + other.id == this.id && + other.checksum == this.checksum && + other.isFavorite == this.isFavorite && + other.orientation == this.orientation); +} + +class LocalAssetEntityCompanion extends UpdateCompanion { + final Value name; + final Value type; + final Value createdAt; + final Value updatedAt; + final Value width; + final Value height; + final Value durationInSeconds; + final Value id; + final Value checksum; + final Value isFavorite; + final Value orientation; + const LocalAssetEntityCompanion({ + this.name = const Value.absent(), + this.type = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + this.id = const Value.absent(), + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }); + LocalAssetEntityCompanion.insert({ + required String name, + required int type, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.width = const Value.absent(), + this.height = const Value.absent(), + this.durationInSeconds = const Value.absent(), + required String id, + this.checksum = const Value.absent(), + this.isFavorite = const Value.absent(), + this.orientation = const Value.absent(), + }) : name = Value(name), + type = Value(type), + id = Value(id); + static Insertable custom({ + Expression? name, + Expression? type, + Expression? createdAt, + Expression? updatedAt, + Expression? width, + Expression? height, + Expression? durationInSeconds, + Expression? id, + Expression? checksum, + Expression? isFavorite, + Expression? orientation, + }) { + return RawValuesInsertable({ + if (name != null) 'name': name, + if (type != null) 'type': type, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (width != null) 'width': width, + if (height != null) 'height': height, + if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, + if (id != null) 'id': id, + if (checksum != null) 'checksum': checksum, + if (isFavorite != null) 'is_favorite': isFavorite, + if (orientation != null) 'orientation': orientation, + }); + } + + LocalAssetEntityCompanion copyWith({ + Value? name, + Value? type, + Value? createdAt, + Value? updatedAt, + Value? width, + Value? height, + Value? durationInSeconds, + Value? id, + Value? checksum, + Value? isFavorite, + Value? orientation, + }) { + return LocalAssetEntityCompanion( + name: name ?? this.name, + type: type ?? this.type, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + width: width ?? this.width, + height: height ?? this.height, + durationInSeconds: durationInSeconds ?? this.durationInSeconds, + id: id ?? this.id, + checksum: checksum ?? this.checksum, + isFavorite: isFavorite ?? this.isFavorite, + orientation: orientation ?? this.orientation, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (name.present) { + map['name'] = Variable(name.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (durationInSeconds.present) { + map['duration_in_seconds'] = Variable(durationInSeconds.value); + } + if (id.present) { + map['id'] = Variable(id.value); + } + if (checksum.present) { + map['checksum'] = Variable(checksum.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAssetEntityCompanion(') + ..write('name: $name, ') + ..write('type: $type, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('width: $width, ') + ..write('height: $height, ') + ..write('durationInSeconds: $durationInSeconds, ') + ..write('id: $id, ') + ..write('checksum: $checksum, ') + ..write('isFavorite: $isFavorite, ') + ..write('orientation: $orientation') + ..write(')')) + .toString(); + } +} + +class LocalAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn backupSelection = GeneratedColumn( + 'backup_selection', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn isIosSharedAlbum = GeneratedColumn( + 'is_ios_shared_album', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_ios_shared_album" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn marker_ = GeneratedColumn( + 'marker', + aliasedName, + true, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("marker" IN (0, 1))', + ), + ); + @override + List get $columns => [ + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_entity'; + @override + Set get $primaryKey => {id}; + @override + LocalAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + backupSelection: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}backup_selection'], + )!, + isIosSharedAlbum: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_ios_shared_album'], + )!, + marker_: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}marker'], + ), + ); + } + + @override + LocalAlbumEntity createAlias(String alias) { + return LocalAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final DateTime updatedAt; + final int backupSelection; + final bool isIosSharedAlbum; + final bool? marker_; + const LocalAlbumEntityData({ + required this.id, + required this.name, + required this.updatedAt, + required this.backupSelection, + required this.isIosSharedAlbum, + this.marker_, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['updated_at'] = Variable(updatedAt); + map['backup_selection'] = Variable(backupSelection); + map['is_ios_shared_album'] = Variable(isIosSharedAlbum); + if (!nullToAbsent || marker_ != null) { + map['marker'] = Variable(marker_); + } + return map; + } + + factory LocalAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + updatedAt: serializer.fromJson(json['updatedAt']), + backupSelection: serializer.fromJson(json['backupSelection']), + isIosSharedAlbum: serializer.fromJson(json['isIosSharedAlbum']), + marker_: serializer.fromJson(json['marker_']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'updatedAt': serializer.toJson(updatedAt), + 'backupSelection': serializer.toJson(backupSelection), + 'isIosSharedAlbum': serializer.toJson(isIosSharedAlbum), + 'marker_': serializer.toJson(marker_), + }; + } + + LocalAlbumEntityData copyWith({ + String? id, + String? name, + DateTime? updatedAt, + int? backupSelection, + bool? isIosSharedAlbum, + Value marker_ = const Value.absent(), + }) => LocalAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_.present ? marker_.value : this.marker_, + ); + LocalAlbumEntityData copyWithCompanion(LocalAlbumEntityCompanion data) { + return LocalAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + backupSelection: data.backupSelection.present + ? data.backupSelection.value + : this.backupSelection, + isIosSharedAlbum: data.isIosSharedAlbum.present + ? data.isIosSharedAlbum.value + : this.isIosSharedAlbum, + marker_: data.marker_.present ? data.marker_.value : this.marker_, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + updatedAt, + backupSelection, + isIosSharedAlbum, + marker_, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.updatedAt == this.updatedAt && + other.backupSelection == this.backupSelection && + other.isIosSharedAlbum == this.isIosSharedAlbum && + other.marker_ == this.marker_); +} + +class LocalAlbumEntityCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value updatedAt; + final Value backupSelection; + final Value isIosSharedAlbum; + final Value marker_; + const LocalAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.updatedAt = const Value.absent(), + this.backupSelection = const Value.absent(), + this.isIosSharedAlbum = const Value.absent(), + this.marker_ = const Value.absent(), + }); + LocalAlbumEntityCompanion.insert({ + required String id, + required String name, + this.updatedAt = const Value.absent(), + required int backupSelection, + this.isIosSharedAlbum = const Value.absent(), + this.marker_ = const Value.absent(), + }) : id = Value(id), + name = Value(name), + backupSelection = Value(backupSelection); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? updatedAt, + Expression? backupSelection, + Expression? isIosSharedAlbum, + Expression? marker_, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (updatedAt != null) 'updated_at': updatedAt, + if (backupSelection != null) 'backup_selection': backupSelection, + if (isIosSharedAlbum != null) 'is_ios_shared_album': isIosSharedAlbum, + if (marker_ != null) 'marker': marker_, + }); + } + + LocalAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? updatedAt, + Value? backupSelection, + Value? isIosSharedAlbum, + Value? marker_, + }) { + return LocalAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + updatedAt: updatedAt ?? this.updatedAt, + backupSelection: backupSelection ?? this.backupSelection, + isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum, + marker_: marker_ ?? this.marker_, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (backupSelection.present) { + map['backup_selection'] = Variable(backupSelection.value); + } + if (isIosSharedAlbum.present) { + map['is_ios_shared_album'] = Variable(isIosSharedAlbum.value); + } + if (marker_.present) { + map['marker'] = Variable(marker_.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('updatedAt: $updatedAt, ') + ..write('backupSelection: $backupSelection, ') + ..write('isIosSharedAlbum: $isIosSharedAlbum, ') + ..write('marker_: $marker_') + ..write(')')) + .toString(); + } +} + +class LocalAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + LocalAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES local_album_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'local_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + LocalAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return LocalAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + ); + } + + @override + LocalAlbumAssetEntity createAlias(String alias) { + return LocalAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class LocalAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const LocalAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory LocalAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return LocalAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + LocalAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + LocalAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + LocalAlbumAssetEntityData copyWithCompanion( + LocalAlbumAssetEntityCompanion data, + ) { + return LocalAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is LocalAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class LocalAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const LocalAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + LocalAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + LocalAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { + return LocalAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('LocalAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class UserMetadataEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + UserMetadataEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn key = GeneratedColumn( + 'key', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn value = GeneratedColumn( + 'value', + aliasedName, + false, + type: DriftSqlType.blob, + requiredDuringInsert: true, + ); + @override + List get $columns => [userId, key, value]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'user_metadata_entity'; + @override + Set get $primaryKey => {userId, key}; + @override + UserMetadataEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return UserMetadataEntityData( + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + key: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}key'], + )!, + value: attachedDatabase.typeMapping.read( + DriftSqlType.blob, + data['${effectivePrefix}value'], + )!, + ); + } + + @override + UserMetadataEntity createAlias(String alias) { + return UserMetadataEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class UserMetadataEntityData extends DataClass + implements Insertable { + final String userId; + final int key; + final Uint8List value; + const UserMetadataEntityData({ + required this.userId, + required this.key, + required this.value, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['user_id'] = Variable(userId); + map['key'] = Variable(key); + map['value'] = Variable(value); + return map; + } + + factory UserMetadataEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return UserMetadataEntityData( + userId: serializer.fromJson(json['userId']), + key: serializer.fromJson(json['key']), + value: serializer.fromJson(json['value']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'userId': serializer.toJson(userId), + 'key': serializer.toJson(key), + 'value': serializer.toJson(value), + }; + } + + UserMetadataEntityData copyWith({ + String? userId, + int? key, + Uint8List? value, + }) => UserMetadataEntityData( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + UserMetadataEntityData copyWithCompanion(UserMetadataEntityCompanion data) { + return UserMetadataEntityData( + userId: data.userId.present ? data.userId.value : this.userId, + key: data.key.present ? data.key.value : this.key, + value: data.value.present ? data.value.value : this.value, + ); + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityData(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(userId, key, $driftBlobEquality.hash(value)); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is UserMetadataEntityData && + other.userId == this.userId && + other.key == this.key && + $driftBlobEquality.equals(other.value, this.value)); +} + +class UserMetadataEntityCompanion + extends UpdateCompanion { + final Value userId; + final Value key; + final Value value; + const UserMetadataEntityCompanion({ + this.userId = const Value.absent(), + this.key = const Value.absent(), + this.value = const Value.absent(), + }); + UserMetadataEntityCompanion.insert({ + required String userId, + required int key, + required Uint8List value, + }) : userId = Value(userId), + key = Value(key), + value = Value(value); + static Insertable custom({ + Expression? userId, + Expression? key, + Expression? value, + }) { + return RawValuesInsertable({ + if (userId != null) 'user_id': userId, + if (key != null) 'key': key, + if (value != null) 'value': value, + }); + } + + UserMetadataEntityCompanion copyWith({ + Value? userId, + Value? key, + Value? value, + }) { + return UserMetadataEntityCompanion( + userId: userId ?? this.userId, + key: key ?? this.key, + value: value ?? this.value, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (key.present) { + map['key'] = Variable(key.value); + } + if (value.present) { + map['value'] = Variable(value.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('UserMetadataEntityCompanion(') + ..write('userId: $userId, ') + ..write('key: $key, ') + ..write('value: $value') + ..write(')')) + .toString(); + } +} + +class PartnerEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PartnerEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn sharedById = GeneratedColumn( + 'shared_by_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn sharedWithId = GeneratedColumn( + 'shared_with_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn inTimeline = GeneratedColumn( + 'in_timeline', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("in_timeline" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + @override + List get $columns => [sharedById, sharedWithId, inTimeline]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'partner_entity'; + @override + Set get $primaryKey => {sharedById, sharedWithId}; + @override + PartnerEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PartnerEntityData( + sharedById: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_by_id'], + )!, + sharedWithId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}shared_with_id'], + )!, + inTimeline: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}in_timeline'], + )!, + ); + } + + @override + PartnerEntity createAlias(String alias) { + return PartnerEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PartnerEntityData extends DataClass + implements Insertable { + final String sharedById; + final String sharedWithId; + final bool inTimeline; + const PartnerEntityData({ + required this.sharedById, + required this.sharedWithId, + required this.inTimeline, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['shared_by_id'] = Variable(sharedById); + map['shared_with_id'] = Variable(sharedWithId); + map['in_timeline'] = Variable(inTimeline); + return map; + } + + factory PartnerEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PartnerEntityData( + sharedById: serializer.fromJson(json['sharedById']), + sharedWithId: serializer.fromJson(json['sharedWithId']), + inTimeline: serializer.fromJson(json['inTimeline']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'sharedById': serializer.toJson(sharedById), + 'sharedWithId': serializer.toJson(sharedWithId), + 'inTimeline': serializer.toJson(inTimeline), + }; + } + + PartnerEntityData copyWith({ + String? sharedById, + String? sharedWithId, + bool? inTimeline, + }) => PartnerEntityData( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + PartnerEntityData copyWithCompanion(PartnerEntityCompanion data) { + return PartnerEntityData( + sharedById: data.sharedById.present + ? data.sharedById.value + : this.sharedById, + sharedWithId: data.sharedWithId.present + ? data.sharedWithId.value + : this.sharedWithId, + inTimeline: data.inTimeline.present + ? data.inTimeline.value + : this.inTimeline, + ); + } + + @override + String toString() { + return (StringBuffer('PartnerEntityData(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(sharedById, sharedWithId, inTimeline); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PartnerEntityData && + other.sharedById == this.sharedById && + other.sharedWithId == this.sharedWithId && + other.inTimeline == this.inTimeline); +} + +class PartnerEntityCompanion extends UpdateCompanion { + final Value sharedById; + final Value sharedWithId; + final Value inTimeline; + const PartnerEntityCompanion({ + this.sharedById = const Value.absent(), + this.sharedWithId = const Value.absent(), + this.inTimeline = const Value.absent(), + }); + PartnerEntityCompanion.insert({ + required String sharedById, + required String sharedWithId, + this.inTimeline = const Value.absent(), + }) : sharedById = Value(sharedById), + sharedWithId = Value(sharedWithId); + static Insertable custom({ + Expression? sharedById, + Expression? sharedWithId, + Expression? inTimeline, + }) { + return RawValuesInsertable({ + if (sharedById != null) 'shared_by_id': sharedById, + if (sharedWithId != null) 'shared_with_id': sharedWithId, + if (inTimeline != null) 'in_timeline': inTimeline, + }); + } + + PartnerEntityCompanion copyWith({ + Value? sharedById, + Value? sharedWithId, + Value? inTimeline, + }) { + return PartnerEntityCompanion( + sharedById: sharedById ?? this.sharedById, + sharedWithId: sharedWithId ?? this.sharedWithId, + inTimeline: inTimeline ?? this.inTimeline, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (sharedById.present) { + map['shared_by_id'] = Variable(sharedById.value); + } + if (sharedWithId.present) { + map['shared_with_id'] = Variable(sharedWithId.value); + } + if (inTimeline.present) { + map['in_timeline'] = Variable(inTimeline.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PartnerEntityCompanion(') + ..write('sharedById: $sharedById, ') + ..write('sharedWithId: $sharedWithId, ') + ..write('inTimeline: $inTimeline') + ..write(')')) + .toString(); + } +} + +class RemoteExifEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteExifEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn city = GeneratedColumn( + 'city', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn state = GeneratedColumn( + 'state', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn country = GeneratedColumn( + 'country', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn dateTimeOriginal = + GeneratedColumn( + 'date_time_original', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn height = GeneratedColumn( + 'height', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn width = GeneratedColumn( + 'width', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn exposureTime = GeneratedColumn( + 'exposure_time', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn fNumber = GeneratedColumn( + 'f_number', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn fileSize = GeneratedColumn( + 'file_size', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn focalLength = GeneratedColumn( + 'focal_length', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn latitude = GeneratedColumn( + 'latitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn longitude = GeneratedColumn( + 'longitude', + aliasedName, + true, + type: DriftSqlType.double, + requiredDuringInsert: false, + ); + late final GeneratedColumn iso = GeneratedColumn( + 'iso', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn make = GeneratedColumn( + 'make', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn model = GeneratedColumn( + 'model', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn lens = GeneratedColumn( + 'lens', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn orientation = GeneratedColumn( + 'orientation', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn timeZone = GeneratedColumn( + 'time_zone', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn rating = GeneratedColumn( + 'rating', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + late final GeneratedColumn projectionType = GeneratedColumn( + 'projection_type', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_exif_entity'; + @override + Set get $primaryKey => {assetId}; + @override + RemoteExifEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteExifEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + city: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}city'], + ), + state: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}state'], + ), + country: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}country'], + ), + dateTimeOriginal: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}date_time_original'], + ), + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + ), + height: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}height'], + ), + width: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}width'], + ), + exposureTime: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}exposure_time'], + ), + fNumber: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}f_number'], + ), + fileSize: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}file_size'], + ), + focalLength: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}focal_length'], + ), + latitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}latitude'], + ), + longitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}longitude'], + ), + iso: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}iso'], + ), + make: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}make'], + ), + model: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}model'], + ), + lens: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}lens'], + ), + orientation: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}orientation'], + ), + timeZone: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}time_zone'], + ), + rating: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}rating'], + ), + projectionType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}projection_type'], + ), + ); + } + + @override + RemoteExifEntity createAlias(String alias) { + return RemoteExifEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteExifEntityData extends DataClass + implements Insertable { + final String assetId; + final String? city; + final String? state; + final String? country; + final DateTime? dateTimeOriginal; + final String? description; + final int? height; + final int? width; + final String? exposureTime; + final double? fNumber; + final int? fileSize; + final double? focalLength; + final double? latitude; + final double? longitude; + final int? iso; + final String? make; + final String? model; + final String? lens; + final String? orientation; + final String? timeZone; + final int? rating; + final String? projectionType; + const RemoteExifEntityData({ + required this.assetId, + this.city, + this.state, + this.country, + this.dateTimeOriginal, + this.description, + this.height, + this.width, + this.exposureTime, + this.fNumber, + this.fileSize, + this.focalLength, + this.latitude, + this.longitude, + this.iso, + this.make, + this.model, + this.lens, + this.orientation, + this.timeZone, + this.rating, + this.projectionType, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || city != null) { + map['city'] = Variable(city); + } + if (!nullToAbsent || state != null) { + map['state'] = Variable(state); + } + if (!nullToAbsent || country != null) { + map['country'] = Variable(country); + } + if (!nullToAbsent || dateTimeOriginal != null) { + map['date_time_original'] = Variable(dateTimeOriginal); + } + if (!nullToAbsent || description != null) { + map['description'] = Variable(description); + } + if (!nullToAbsent || height != null) { + map['height'] = Variable(height); + } + if (!nullToAbsent || width != null) { + map['width'] = Variable(width); + } + if (!nullToAbsent || exposureTime != null) { + map['exposure_time'] = Variable(exposureTime); + } + if (!nullToAbsent || fNumber != null) { + map['f_number'] = Variable(fNumber); + } + if (!nullToAbsent || fileSize != null) { + map['file_size'] = Variable(fileSize); + } + if (!nullToAbsent || focalLength != null) { + map['focal_length'] = Variable(focalLength); + } + if (!nullToAbsent || latitude != null) { + map['latitude'] = Variable(latitude); + } + if (!nullToAbsent || longitude != null) { + map['longitude'] = Variable(longitude); + } + if (!nullToAbsent || iso != null) { + map['iso'] = Variable(iso); + } + if (!nullToAbsent || make != null) { + map['make'] = Variable(make); + } + if (!nullToAbsent || model != null) { + map['model'] = Variable(model); + } + if (!nullToAbsent || lens != null) { + map['lens'] = Variable(lens); + } + if (!nullToAbsent || orientation != null) { + map['orientation'] = Variable(orientation); + } + if (!nullToAbsent || timeZone != null) { + map['time_zone'] = Variable(timeZone); + } + if (!nullToAbsent || rating != null) { + map['rating'] = Variable(rating); + } + if (!nullToAbsent || projectionType != null) { + map['projection_type'] = Variable(projectionType); + } + return map; + } + + factory RemoteExifEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteExifEntityData( + assetId: serializer.fromJson(json['assetId']), + city: serializer.fromJson(json['city']), + state: serializer.fromJson(json['state']), + country: serializer.fromJson(json['country']), + dateTimeOriginal: serializer.fromJson( + json['dateTimeOriginal'], + ), + description: serializer.fromJson(json['description']), + height: serializer.fromJson(json['height']), + width: serializer.fromJson(json['width']), + exposureTime: serializer.fromJson(json['exposureTime']), + fNumber: serializer.fromJson(json['fNumber']), + fileSize: serializer.fromJson(json['fileSize']), + focalLength: serializer.fromJson(json['focalLength']), + latitude: serializer.fromJson(json['latitude']), + longitude: serializer.fromJson(json['longitude']), + iso: serializer.fromJson(json['iso']), + make: serializer.fromJson(json['make']), + model: serializer.fromJson(json['model']), + lens: serializer.fromJson(json['lens']), + orientation: serializer.fromJson(json['orientation']), + timeZone: serializer.fromJson(json['timeZone']), + rating: serializer.fromJson(json['rating']), + projectionType: serializer.fromJson(json['projectionType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'city': serializer.toJson(city), + 'state': serializer.toJson(state), + 'country': serializer.toJson(country), + 'dateTimeOriginal': serializer.toJson(dateTimeOriginal), + 'description': serializer.toJson(description), + 'height': serializer.toJson(height), + 'width': serializer.toJson(width), + 'exposureTime': serializer.toJson(exposureTime), + 'fNumber': serializer.toJson(fNumber), + 'fileSize': serializer.toJson(fileSize), + 'focalLength': serializer.toJson(focalLength), + 'latitude': serializer.toJson(latitude), + 'longitude': serializer.toJson(longitude), + 'iso': serializer.toJson(iso), + 'make': serializer.toJson(make), + 'model': serializer.toJson(model), + 'lens': serializer.toJson(lens), + 'orientation': serializer.toJson(orientation), + 'timeZone': serializer.toJson(timeZone), + 'rating': serializer.toJson(rating), + 'projectionType': serializer.toJson(projectionType), + }; + } + + RemoteExifEntityData copyWith({ + String? assetId, + Value city = const Value.absent(), + Value state = const Value.absent(), + Value country = const Value.absent(), + Value dateTimeOriginal = const Value.absent(), + Value description = const Value.absent(), + Value height = const Value.absent(), + Value width = const Value.absent(), + Value exposureTime = const Value.absent(), + Value fNumber = const Value.absent(), + Value fileSize = const Value.absent(), + Value focalLength = const Value.absent(), + Value latitude = const Value.absent(), + Value longitude = const Value.absent(), + Value iso = const Value.absent(), + Value make = const Value.absent(), + Value model = const Value.absent(), + Value lens = const Value.absent(), + Value orientation = const Value.absent(), + Value timeZone = const Value.absent(), + Value rating = const Value.absent(), + Value projectionType = const Value.absent(), + }) => RemoteExifEntityData( + assetId: assetId ?? this.assetId, + city: city.present ? city.value : this.city, + state: state.present ? state.value : this.state, + country: country.present ? country.value : this.country, + dateTimeOriginal: dateTimeOriginal.present + ? dateTimeOriginal.value + : this.dateTimeOriginal, + description: description.present ? description.value : this.description, + height: height.present ? height.value : this.height, + width: width.present ? width.value : this.width, + exposureTime: exposureTime.present ? exposureTime.value : this.exposureTime, + fNumber: fNumber.present ? fNumber.value : this.fNumber, + fileSize: fileSize.present ? fileSize.value : this.fileSize, + focalLength: focalLength.present ? focalLength.value : this.focalLength, + latitude: latitude.present ? latitude.value : this.latitude, + longitude: longitude.present ? longitude.value : this.longitude, + iso: iso.present ? iso.value : this.iso, + make: make.present ? make.value : this.make, + model: model.present ? model.value : this.model, + lens: lens.present ? lens.value : this.lens, + orientation: orientation.present ? orientation.value : this.orientation, + timeZone: timeZone.present ? timeZone.value : this.timeZone, + rating: rating.present ? rating.value : this.rating, + projectionType: projectionType.present + ? projectionType.value + : this.projectionType, + ); + RemoteExifEntityData copyWithCompanion(RemoteExifEntityCompanion data) { + return RemoteExifEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + city: data.city.present ? data.city.value : this.city, + state: data.state.present ? data.state.value : this.state, + country: data.country.present ? data.country.value : this.country, + dateTimeOriginal: data.dateTimeOriginal.present + ? data.dateTimeOriginal.value + : this.dateTimeOriginal, + description: data.description.present + ? data.description.value + : this.description, + height: data.height.present ? data.height.value : this.height, + width: data.width.present ? data.width.value : this.width, + exposureTime: data.exposureTime.present + ? data.exposureTime.value + : this.exposureTime, + fNumber: data.fNumber.present ? data.fNumber.value : this.fNumber, + fileSize: data.fileSize.present ? data.fileSize.value : this.fileSize, + focalLength: data.focalLength.present + ? data.focalLength.value + : this.focalLength, + latitude: data.latitude.present ? data.latitude.value : this.latitude, + longitude: data.longitude.present ? data.longitude.value : this.longitude, + iso: data.iso.present ? data.iso.value : this.iso, + make: data.make.present ? data.make.value : this.make, + model: data.model.present ? data.model.value : this.model, + lens: data.lens.present ? data.lens.value : this.lens, + orientation: data.orientation.present + ? data.orientation.value + : this.orientation, + timeZone: data.timeZone.present ? data.timeZone.value : this.timeZone, + rating: data.rating.present ? data.rating.value : this.rating, + projectionType: data.projectionType.present + ? data.projectionType.value + : this.projectionType, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityData(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hashAll([ + assetId, + city, + state, + country, + dateTimeOriginal, + description, + height, + width, + exposureTime, + fNumber, + fileSize, + focalLength, + latitude, + longitude, + iso, + make, + model, + lens, + orientation, + timeZone, + rating, + projectionType, + ]); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteExifEntityData && + other.assetId == this.assetId && + other.city == this.city && + other.state == this.state && + other.country == this.country && + other.dateTimeOriginal == this.dateTimeOriginal && + other.description == this.description && + other.height == this.height && + other.width == this.width && + other.exposureTime == this.exposureTime && + other.fNumber == this.fNumber && + other.fileSize == this.fileSize && + other.focalLength == this.focalLength && + other.latitude == this.latitude && + other.longitude == this.longitude && + other.iso == this.iso && + other.make == this.make && + other.model == this.model && + other.lens == this.lens && + other.orientation == this.orientation && + other.timeZone == this.timeZone && + other.rating == this.rating && + other.projectionType == this.projectionType); +} + +class RemoteExifEntityCompanion extends UpdateCompanion { + final Value assetId; + final Value city; + final Value state; + final Value country; + final Value dateTimeOriginal; + final Value description; + final Value height; + final Value width; + final Value exposureTime; + final Value fNumber; + final Value fileSize; + final Value focalLength; + final Value latitude; + final Value longitude; + final Value iso; + final Value make; + final Value model; + final Value lens; + final Value orientation; + final Value timeZone; + final Value rating; + final Value projectionType; + const RemoteExifEntityCompanion({ + this.assetId = const Value.absent(), + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }); + RemoteExifEntityCompanion.insert({ + required String assetId, + this.city = const Value.absent(), + this.state = const Value.absent(), + this.country = const Value.absent(), + this.dateTimeOriginal = const Value.absent(), + this.description = const Value.absent(), + this.height = const Value.absent(), + this.width = const Value.absent(), + this.exposureTime = const Value.absent(), + this.fNumber = const Value.absent(), + this.fileSize = const Value.absent(), + this.focalLength = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.iso = const Value.absent(), + this.make = const Value.absent(), + this.model = const Value.absent(), + this.lens = const Value.absent(), + this.orientation = const Value.absent(), + this.timeZone = const Value.absent(), + this.rating = const Value.absent(), + this.projectionType = const Value.absent(), + }) : assetId = Value(assetId); + static Insertable custom({ + Expression? assetId, + Expression? city, + Expression? state, + Expression? country, + Expression? dateTimeOriginal, + Expression? description, + Expression? height, + Expression? width, + Expression? exposureTime, + Expression? fNumber, + Expression? fileSize, + Expression? focalLength, + Expression? latitude, + Expression? longitude, + Expression? iso, + Expression? make, + Expression? model, + Expression? lens, + Expression? orientation, + Expression? timeZone, + Expression? rating, + Expression? projectionType, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (city != null) 'city': city, + if (state != null) 'state': state, + if (country != null) 'country': country, + if (dateTimeOriginal != null) 'date_time_original': dateTimeOriginal, + if (description != null) 'description': description, + if (height != null) 'height': height, + if (width != null) 'width': width, + if (exposureTime != null) 'exposure_time': exposureTime, + if (fNumber != null) 'f_number': fNumber, + if (fileSize != null) 'file_size': fileSize, + if (focalLength != null) 'focal_length': focalLength, + if (latitude != null) 'latitude': latitude, + if (longitude != null) 'longitude': longitude, + if (iso != null) 'iso': iso, + if (make != null) 'make': make, + if (model != null) 'model': model, + if (lens != null) 'lens': lens, + if (orientation != null) 'orientation': orientation, + if (timeZone != null) 'time_zone': timeZone, + if (rating != null) 'rating': rating, + if (projectionType != null) 'projection_type': projectionType, + }); + } + + RemoteExifEntityCompanion copyWith({ + Value? assetId, + Value? city, + Value? state, + Value? country, + Value? dateTimeOriginal, + Value? description, + Value? height, + Value? width, + Value? exposureTime, + Value? fNumber, + Value? fileSize, + Value? focalLength, + Value? latitude, + Value? longitude, + Value? iso, + Value? make, + Value? model, + Value? lens, + Value? orientation, + Value? timeZone, + Value? rating, + Value? projectionType, + }) { + return RemoteExifEntityCompanion( + assetId: assetId ?? this.assetId, + city: city ?? this.city, + state: state ?? this.state, + country: country ?? this.country, + dateTimeOriginal: dateTimeOriginal ?? this.dateTimeOriginal, + description: description ?? this.description, + height: height ?? this.height, + width: width ?? this.width, + exposureTime: exposureTime ?? this.exposureTime, + fNumber: fNumber ?? this.fNumber, + fileSize: fileSize ?? this.fileSize, + focalLength: focalLength ?? this.focalLength, + latitude: latitude ?? this.latitude, + longitude: longitude ?? this.longitude, + iso: iso ?? this.iso, + make: make ?? this.make, + model: model ?? this.model, + lens: lens ?? this.lens, + orientation: orientation ?? this.orientation, + timeZone: timeZone ?? this.timeZone, + rating: rating ?? this.rating, + projectionType: projectionType ?? this.projectionType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (city.present) { + map['city'] = Variable(city.value); + } + if (state.present) { + map['state'] = Variable(state.value); + } + if (country.present) { + map['country'] = Variable(country.value); + } + if (dateTimeOriginal.present) { + map['date_time_original'] = Variable(dateTimeOriginal.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (height.present) { + map['height'] = Variable(height.value); + } + if (width.present) { + map['width'] = Variable(width.value); + } + if (exposureTime.present) { + map['exposure_time'] = Variable(exposureTime.value); + } + if (fNumber.present) { + map['f_number'] = Variable(fNumber.value); + } + if (fileSize.present) { + map['file_size'] = Variable(fileSize.value); + } + if (focalLength.present) { + map['focal_length'] = Variable(focalLength.value); + } + if (latitude.present) { + map['latitude'] = Variable(latitude.value); + } + if (longitude.present) { + map['longitude'] = Variable(longitude.value); + } + if (iso.present) { + map['iso'] = Variable(iso.value); + } + if (make.present) { + map['make'] = Variable(make.value); + } + if (model.present) { + map['model'] = Variable(model.value); + } + if (lens.present) { + map['lens'] = Variable(lens.value); + } + if (orientation.present) { + map['orientation'] = Variable(orientation.value); + } + if (timeZone.present) { + map['time_zone'] = Variable(timeZone.value); + } + if (rating.present) { + map['rating'] = Variable(rating.value); + } + if (projectionType.present) { + map['projection_type'] = Variable(projectionType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteExifEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('city: $city, ') + ..write('state: $state, ') + ..write('country: $country, ') + ..write('dateTimeOriginal: $dateTimeOriginal, ') + ..write('description: $description, ') + ..write('height: $height, ') + ..write('width: $width, ') + ..write('exposureTime: $exposureTime, ') + ..write('fNumber: $fNumber, ') + ..write('fileSize: $fileSize, ') + ..write('focalLength: $focalLength, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('iso: $iso, ') + ..write('make: $make, ') + ..write('model: $model, ') + ..write('lens: $lens, ') + ..write('orientation: $orientation, ') + ..write('timeZone: $timeZone, ') + ..write('rating: $rating, ') + ..write('projectionType: $projectionType') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultValue: const CustomExpression('\'\''), + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn thumbnailAssetId = GeneratedColumn( + 'thumbnail_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn isActivityEnabled = GeneratedColumn( + 'is_activity_enabled', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_activity_enabled" IN (0, 1))', + ), + defaultValue: const CustomExpression('1'), + ); + late final GeneratedColumn order = GeneratedColumn( + 'order', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_entity'; + @override + Set get $primaryKey => {id}; + @override + RemoteAlbumEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + thumbnailAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}thumbnail_asset_id'], + ), + isActivityEnabled: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_activity_enabled'], + )!, + order: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}order'], + )!, + ); + } + + @override + RemoteAlbumEntity createAlias(String alias) { + return RemoteAlbumEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumEntityData extends DataClass + implements Insertable { + final String id; + final String name; + final String description; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String? thumbnailAssetId; + final bool isActivityEnabled; + final int order; + const RemoteAlbumEntityData({ + required this.id, + required this.name, + required this.description, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + this.thumbnailAssetId, + required this.isActivityEnabled, + required this.order, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['description'] = Variable(description); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + if (!nullToAbsent || thumbnailAssetId != null) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId); + } + map['is_activity_enabled'] = Variable(isActivityEnabled); + map['order'] = Variable(order); + return map; + } + + factory RemoteAlbumEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumEntityData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + description: serializer.fromJson(json['description']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + thumbnailAssetId: serializer.fromJson(json['thumbnailAssetId']), + isActivityEnabled: serializer.fromJson(json['isActivityEnabled']), + order: serializer.fromJson(json['order']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'description': serializer.toJson(description), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'thumbnailAssetId': serializer.toJson(thumbnailAssetId), + 'isActivityEnabled': serializer.toJson(isActivityEnabled), + 'order': serializer.toJson(order), + }; + } + + RemoteAlbumEntityData copyWith({ + String? id, + String? name, + String? description, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + Value thumbnailAssetId = const Value.absent(), + bool? isActivityEnabled, + int? order, + }) => RemoteAlbumEntityData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId.present + ? thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + RemoteAlbumEntityData copyWithCompanion(RemoteAlbumEntityCompanion data) { + return RemoteAlbumEntityData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + description: data.description.present + ? data.description.value + : this.description, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + thumbnailAssetId: data.thumbnailAssetId.present + ? data.thumbnailAssetId.value + : this.thumbnailAssetId, + isActivityEnabled: data.isActivityEnabled.present + ? data.isActivityEnabled.value + : this.isActivityEnabled, + order: data.order.present ? data.order.value : this.order, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + name, + description, + createdAt, + updatedAt, + ownerId, + thumbnailAssetId, + isActivityEnabled, + order, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumEntityData && + other.id == this.id && + other.name == this.name && + other.description == this.description && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.thumbnailAssetId == this.thumbnailAssetId && + other.isActivityEnabled == this.isActivityEnabled && + other.order == this.order); +} + +class RemoteAlbumEntityCompanion + extends UpdateCompanion { + final Value id; + final Value name; + final Value description; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value thumbnailAssetId; + final Value isActivityEnabled; + final Value order; + const RemoteAlbumEntityCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + this.order = const Value.absent(), + }); + RemoteAlbumEntityCompanion.insert({ + required String id, + required String name, + this.description = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + this.thumbnailAssetId = const Value.absent(), + this.isActivityEnabled = const Value.absent(), + required int order, + }) : id = Value(id), + name = Value(name), + ownerId = Value(ownerId), + order = Value(order); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? description, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? thumbnailAssetId, + Expression? isActivityEnabled, + Expression? order, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (description != null) 'description': description, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (thumbnailAssetId != null) 'thumbnail_asset_id': thumbnailAssetId, + if (isActivityEnabled != null) 'is_activity_enabled': isActivityEnabled, + if (order != null) 'order': order, + }); + } + + RemoteAlbumEntityCompanion copyWith({ + Value? id, + Value? name, + Value? description, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? thumbnailAssetId, + Value? isActivityEnabled, + Value? order, + }) { + return RemoteAlbumEntityCompanion( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + thumbnailAssetId: thumbnailAssetId ?? this.thumbnailAssetId, + isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, + order: order ?? this.order, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (thumbnailAssetId.present) { + map['thumbnail_asset_id'] = Variable(thumbnailAssetId.value); + } + if (isActivityEnabled.present) { + map['is_activity_enabled'] = Variable(isActivityEnabled.value); + } + if (order.present) { + map['order'] = Variable(order.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumEntityCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('description: $description, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('thumbnailAssetId: $thumbnailAssetId, ') + ..write('isActivityEnabled: $isActivityEnabled, ') + ..write('order: $order') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, albumId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_asset_entity'; + @override + Set get $primaryKey => {assetId, albumId}; + @override + RemoteAlbumAssetEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + ); + } + + @override + RemoteAlbumAssetEntity createAlias(String alias) { + return RemoteAlbumAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String albumId; + const RemoteAlbumAssetEntityData({ + required this.assetId, + required this.albumId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['album_id'] = Variable(albumId); + return map; + } + + factory RemoteAlbumAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + albumId: serializer.fromJson(json['albumId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'albumId': serializer.toJson(albumId), + }; + } + + RemoteAlbumAssetEntityData copyWith({String? assetId, String? albumId}) => + RemoteAlbumAssetEntityData( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + RemoteAlbumAssetEntityData copyWithCompanion( + RemoteAlbumAssetEntityCompanion data, + ) { + return RemoteAlbumAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + albumId: data.albumId.present ? data.albumId.value : this.albumId, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, albumId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumAssetEntityData && + other.assetId == this.assetId && + other.albumId == this.albumId); +} + +class RemoteAlbumAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value albumId; + const RemoteAlbumAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.albumId = const Value.absent(), + }); + RemoteAlbumAssetEntityCompanion.insert({ + required String assetId, + required String albumId, + }) : assetId = Value(assetId), + albumId = Value(albumId); + static Insertable custom({ + Expression? assetId, + Expression? albumId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (albumId != null) 'album_id': albumId, + }); + } + + RemoteAlbumAssetEntityCompanion copyWith({ + Value? assetId, + Value? albumId, + }) { + return RemoteAlbumAssetEntityCompanion( + assetId: assetId ?? this.assetId, + albumId: albumId ?? this.albumId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('albumId: $albumId') + ..write(')')) + .toString(); + } +} + +class RemoteAlbumUserEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + RemoteAlbumUserEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn albumId = GeneratedColumn( + 'album_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_album_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn userId = GeneratedColumn( + 'user_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn role = GeneratedColumn( + 'role', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [albumId, userId, role]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'remote_album_user_entity'; + @override + Set get $primaryKey => {albumId, userId}; + @override + RemoteAlbumUserEntityData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return RemoteAlbumUserEntityData( + albumId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}album_id'], + )!, + userId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}user_id'], + )!, + role: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}role'], + )!, + ); + } + + @override + RemoteAlbumUserEntity createAlias(String alias) { + return RemoteAlbumUserEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class RemoteAlbumUserEntityData extends DataClass + implements Insertable { + final String albumId; + final String userId; + final int role; + const RemoteAlbumUserEntityData({ + required this.albumId, + required this.userId, + required this.role, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['album_id'] = Variable(albumId); + map['user_id'] = Variable(userId); + map['role'] = Variable(role); + return map; + } + + factory RemoteAlbumUserEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return RemoteAlbumUserEntityData( + albumId: serializer.fromJson(json['albumId']), + userId: serializer.fromJson(json['userId']), + role: serializer.fromJson(json['role']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'albumId': serializer.toJson(albumId), + 'userId': serializer.toJson(userId), + 'role': serializer.toJson(role), + }; + } + + RemoteAlbumUserEntityData copyWith({ + String? albumId, + String? userId, + int? role, + }) => RemoteAlbumUserEntityData( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + RemoteAlbumUserEntityData copyWithCompanion( + RemoteAlbumUserEntityCompanion data, + ) { + return RemoteAlbumUserEntityData( + albumId: data.albumId.present ? data.albumId.value : this.albumId, + userId: data.userId.present ? data.userId.value : this.userId, + role: data.role.present ? data.role.value : this.role, + ); + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityData(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(albumId, userId, role); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is RemoteAlbumUserEntityData && + other.albumId == this.albumId && + other.userId == this.userId && + other.role == this.role); +} + +class RemoteAlbumUserEntityCompanion + extends UpdateCompanion { + final Value albumId; + final Value userId; + final Value role; + const RemoteAlbumUserEntityCompanion({ + this.albumId = const Value.absent(), + this.userId = const Value.absent(), + this.role = const Value.absent(), + }); + RemoteAlbumUserEntityCompanion.insert({ + required String albumId, + required String userId, + required int role, + }) : albumId = Value(albumId), + userId = Value(userId), + role = Value(role); + static Insertable custom({ + Expression? albumId, + Expression? userId, + Expression? role, + }) { + return RawValuesInsertable({ + if (albumId != null) 'album_id': albumId, + if (userId != null) 'user_id': userId, + if (role != null) 'role': role, + }); + } + + RemoteAlbumUserEntityCompanion copyWith({ + Value? albumId, + Value? userId, + Value? role, + }) { + return RemoteAlbumUserEntityCompanion( + albumId: albumId ?? this.albumId, + userId: userId ?? this.userId, + role: role ?? this.role, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (albumId.present) { + map['album_id'] = Variable(albumId.value); + } + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (role.present) { + map['role'] = Variable(role.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('RemoteAlbumUserEntityCompanion(') + ..write('albumId: $albumId, ') + ..write('userId: $userId, ') + ..write('role: $role') + ..write(')')) + .toString(); + } +} + +class MemoryEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn deletedAt = GeneratedColumn( + 'deleted_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn data = GeneratedColumn( + 'data', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn isSaved = GeneratedColumn( + 'is_saved', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_saved" IN (0, 1))', + ), + defaultValue: const CustomExpression('0'), + ); + late final GeneratedColumn memoryAt = GeneratedColumn( + 'memory_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: true, + ); + late final GeneratedColumn seenAt = GeneratedColumn( + 'seen_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn showAt = GeneratedColumn( + 'show_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + late final GeneratedColumn hideAt = GeneratedColumn( + 'hide_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_entity'; + @override + Set get $primaryKey => {id}; + @override + MemoryEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + deletedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}deleted_at'], + ), + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}type'], + )!, + data: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}data'], + )!, + isSaved: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_saved'], + )!, + memoryAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}memory_at'], + )!, + seenAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}seen_at'], + ), + showAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}show_at'], + ), + hideAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}hide_at'], + ), + ); + } + + @override + MemoryEntity createAlias(String alias) { + return MemoryEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final DateTime? deletedAt; + final String ownerId; + final int type; + final String data; + final bool isSaved; + final DateTime memoryAt; + final DateTime? seenAt; + final DateTime? showAt; + final DateTime? hideAt; + const MemoryEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.ownerId, + required this.type, + required this.data, + required this.isSaved, + required this.memoryAt, + this.seenAt, + this.showAt, + this.hideAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + if (!nullToAbsent || deletedAt != null) { + map['deleted_at'] = Variable(deletedAt); + } + map['owner_id'] = Variable(ownerId); + map['type'] = Variable(type); + map['data'] = Variable(data); + map['is_saved'] = Variable(isSaved); + map['memory_at'] = Variable(memoryAt); + if (!nullToAbsent || seenAt != null) { + map['seen_at'] = Variable(seenAt); + } + if (!nullToAbsent || showAt != null) { + map['show_at'] = Variable(showAt); + } + if (!nullToAbsent || hideAt != null) { + map['hide_at'] = Variable(hideAt); + } + return map; + } + + factory MemoryEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + deletedAt: serializer.fromJson(json['deletedAt']), + ownerId: serializer.fromJson(json['ownerId']), + type: serializer.fromJson(json['type']), + data: serializer.fromJson(json['data']), + isSaved: serializer.fromJson(json['isSaved']), + memoryAt: serializer.fromJson(json['memoryAt']), + seenAt: serializer.fromJson(json['seenAt']), + showAt: serializer.fromJson(json['showAt']), + hideAt: serializer.fromJson(json['hideAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'deletedAt': serializer.toJson(deletedAt), + 'ownerId': serializer.toJson(ownerId), + 'type': serializer.toJson(type), + 'data': serializer.toJson(data), + 'isSaved': serializer.toJson(isSaved), + 'memoryAt': serializer.toJson(memoryAt), + 'seenAt': serializer.toJson(seenAt), + 'showAt': serializer.toJson(showAt), + 'hideAt': serializer.toJson(hideAt), + }; + } + + MemoryEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + Value deletedAt = const Value.absent(), + String? ownerId, + int? type, + String? data, + bool? isSaved, + DateTime? memoryAt, + Value seenAt = const Value.absent(), + Value showAt = const Value.absent(), + Value hideAt = const Value.absent(), + }) => MemoryEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt.present ? seenAt.value : this.seenAt, + showAt: showAt.present ? showAt.value : this.showAt, + hideAt: hideAt.present ? hideAt.value : this.hideAt, + ); + MemoryEntityData copyWithCompanion(MemoryEntityCompanion data) { + return MemoryEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + deletedAt: data.deletedAt.present ? data.deletedAt.value : this.deletedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + type: data.type.present ? data.type.value : this.type, + data: data.data.present ? data.data.value : this.data, + isSaved: data.isSaved.present ? data.isSaved.value : this.isSaved, + memoryAt: data.memoryAt.present ? data.memoryAt.value : this.memoryAt, + seenAt: data.seenAt.present ? data.seenAt.value : this.seenAt, + showAt: data.showAt.present ? data.showAt.value : this.showAt, + hideAt: data.hideAt.present ? data.hideAt.value : this.hideAt, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + deletedAt, + ownerId, + type, + data, + isSaved, + memoryAt, + seenAt, + showAt, + hideAt, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.deletedAt == this.deletedAt && + other.ownerId == this.ownerId && + other.type == this.type && + other.data == this.data && + other.isSaved == this.isSaved && + other.memoryAt == this.memoryAt && + other.seenAt == this.seenAt && + other.showAt == this.showAt && + other.hideAt == this.hideAt); +} + +class MemoryEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value deletedAt; + final Value ownerId; + final Value type; + final Value data; + final Value isSaved; + final Value memoryAt; + final Value seenAt; + final Value showAt; + final Value hideAt; + const MemoryEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.type = const Value.absent(), + this.data = const Value.absent(), + this.isSaved = const Value.absent(), + this.memoryAt = const Value.absent(), + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }); + MemoryEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.deletedAt = const Value.absent(), + required String ownerId, + required int type, + required String data, + this.isSaved = const Value.absent(), + required DateTime memoryAt, + this.seenAt = const Value.absent(), + this.showAt = const Value.absent(), + this.hideAt = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + type = Value(type), + data = Value(data), + memoryAt = Value(memoryAt); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? deletedAt, + Expression? ownerId, + Expression? type, + Expression? data, + Expression? isSaved, + Expression? memoryAt, + Expression? seenAt, + Expression? showAt, + Expression? hideAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (deletedAt != null) 'deleted_at': deletedAt, + if (ownerId != null) 'owner_id': ownerId, + if (type != null) 'type': type, + if (data != null) 'data': data, + if (isSaved != null) 'is_saved': isSaved, + if (memoryAt != null) 'memory_at': memoryAt, + if (seenAt != null) 'seen_at': seenAt, + if (showAt != null) 'show_at': showAt, + if (hideAt != null) 'hide_at': hideAt, + }); + } + + MemoryEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? deletedAt, + Value? ownerId, + Value? type, + Value? data, + Value? isSaved, + Value? memoryAt, + Value? seenAt, + Value? showAt, + Value? hideAt, + }) { + return MemoryEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + deletedAt: deletedAt ?? this.deletedAt, + ownerId: ownerId ?? this.ownerId, + type: type ?? this.type, + data: data ?? this.data, + isSaved: isSaved ?? this.isSaved, + memoryAt: memoryAt ?? this.memoryAt, + seenAt: seenAt ?? this.seenAt, + showAt: showAt ?? this.showAt, + hideAt: hideAt ?? this.hideAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (deletedAt.present) { + map['deleted_at'] = Variable(deletedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (data.present) { + map['data'] = Variable(data.value); + } + if (isSaved.present) { + map['is_saved'] = Variable(isSaved.value); + } + if (memoryAt.present) { + map['memory_at'] = Variable(memoryAt.value); + } + if (seenAt.present) { + map['seen_at'] = Variable(seenAt.value); + } + if (showAt.present) { + map['show_at'] = Variable(showAt.value); + } + if (hideAt.present) { + map['hide_at'] = Variable(hideAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('deletedAt: $deletedAt, ') + ..write('ownerId: $ownerId, ') + ..write('type: $type, ') + ..write('data: $data, ') + ..write('isSaved: $isSaved, ') + ..write('memoryAt: $memoryAt, ') + ..write('seenAt: $seenAt, ') + ..write('showAt: $showAt, ') + ..write('hideAt: $hideAt') + ..write(')')) + .toString(); + } +} + +class MemoryAssetEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + MemoryAssetEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn memoryId = GeneratedColumn( + 'memory_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES memory_entity (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [assetId, memoryId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'memory_asset_entity'; + @override + Set get $primaryKey => {assetId, memoryId}; + @override + MemoryAssetEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MemoryAssetEntityData( + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + memoryId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}memory_id'], + )!, + ); + } + + @override + MemoryAssetEntity createAlias(String alias) { + return MemoryAssetEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class MemoryAssetEntityData extends DataClass + implements Insertable { + final String assetId; + final String memoryId; + const MemoryAssetEntityData({required this.assetId, required this.memoryId}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['asset_id'] = Variable(assetId); + map['memory_id'] = Variable(memoryId); + return map; + } + + factory MemoryAssetEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MemoryAssetEntityData( + assetId: serializer.fromJson(json['assetId']), + memoryId: serializer.fromJson(json['memoryId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'assetId': serializer.toJson(assetId), + 'memoryId': serializer.toJson(memoryId), + }; + } + + MemoryAssetEntityData copyWith({String? assetId, String? memoryId}) => + MemoryAssetEntityData( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + MemoryAssetEntityData copyWithCompanion(MemoryAssetEntityCompanion data) { + return MemoryAssetEntityData( + assetId: data.assetId.present ? data.assetId.value : this.assetId, + memoryId: data.memoryId.present ? data.memoryId.value : this.memoryId, + ); + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityData(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(assetId, memoryId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MemoryAssetEntityData && + other.assetId == this.assetId && + other.memoryId == this.memoryId); +} + +class MemoryAssetEntityCompanion + extends UpdateCompanion { + final Value assetId; + final Value memoryId; + const MemoryAssetEntityCompanion({ + this.assetId = const Value.absent(), + this.memoryId = const Value.absent(), + }); + MemoryAssetEntityCompanion.insert({ + required String assetId, + required String memoryId, + }) : assetId = Value(assetId), + memoryId = Value(memoryId); + static Insertable custom({ + Expression? assetId, + Expression? memoryId, + }) { + return RawValuesInsertable({ + if (assetId != null) 'asset_id': assetId, + if (memoryId != null) 'memory_id': memoryId, + }); + } + + MemoryAssetEntityCompanion copyWith({ + Value? assetId, + Value? memoryId, + }) { + return MemoryAssetEntityCompanion( + assetId: assetId ?? this.assetId, + memoryId: memoryId ?? this.memoryId, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (memoryId.present) { + map['memory_id'] = Variable(memoryId.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MemoryAssetEntityCompanion(') + ..write('assetId: $assetId, ') + ..write('memoryId: $memoryId') + ..write(')')) + .toString(); + } +} + +class PersonEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + PersonEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn updatedAt = GeneratedColumn( + 'updated_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + defaultValue: const CustomExpression('CURRENT_TIMESTAMP'), + ); + late final GeneratedColumn ownerId = GeneratedColumn( + 'owner_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES user_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn faceAssetId = GeneratedColumn( + 'face_asset_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn isFavorite = GeneratedColumn( + 'is_favorite', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_favorite" IN (0, 1))', + ), + ); + late final GeneratedColumn isHidden = GeneratedColumn( + 'is_hidden', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("is_hidden" IN (0, 1))', + ), + ); + late final GeneratedColumn color = GeneratedColumn( + 'color', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + late final GeneratedColumn birthDate = GeneratedColumn( + 'birth_date', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'person_entity'; + @override + Set get $primaryKey => {id}; + @override + PersonEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return PersonEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + updatedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}updated_at'], + )!, + ownerId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}owner_id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + faceAssetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}face_asset_id'], + ), + isFavorite: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_favorite'], + )!, + isHidden: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}is_hidden'], + )!, + color: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}color'], + ), + birthDate: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}birth_date'], + ), + ); + } + + @override + PersonEntity createAlias(String alias) { + return PersonEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class PersonEntityData extends DataClass + implements Insertable { + final String id; + final DateTime createdAt; + final DateTime updatedAt; + final String ownerId; + final String name; + final String? faceAssetId; + final bool isFavorite; + final bool isHidden; + final String? color; + final DateTime? birthDate; + const PersonEntityData({ + required this.id, + required this.createdAt, + required this.updatedAt, + required this.ownerId, + required this.name, + this.faceAssetId, + required this.isFavorite, + required this.isHidden, + this.color, + this.birthDate, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); + map['owner_id'] = Variable(ownerId); + map['name'] = Variable(name); + if (!nullToAbsent || faceAssetId != null) { + map['face_asset_id'] = Variable(faceAssetId); + } + map['is_favorite'] = Variable(isFavorite); + map['is_hidden'] = Variable(isHidden); + if (!nullToAbsent || color != null) { + map['color'] = Variable(color); + } + if (!nullToAbsent || birthDate != null) { + map['birth_date'] = Variable(birthDate); + } + return map; + } + + factory PersonEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return PersonEntityData( + id: serializer.fromJson(json['id']), + createdAt: serializer.fromJson(json['createdAt']), + updatedAt: serializer.fromJson(json['updatedAt']), + ownerId: serializer.fromJson(json['ownerId']), + name: serializer.fromJson(json['name']), + faceAssetId: serializer.fromJson(json['faceAssetId']), + isFavorite: serializer.fromJson(json['isFavorite']), + isHidden: serializer.fromJson(json['isHidden']), + color: serializer.fromJson(json['color']), + birthDate: serializer.fromJson(json['birthDate']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'createdAt': serializer.toJson(createdAt), + 'updatedAt': serializer.toJson(updatedAt), + 'ownerId': serializer.toJson(ownerId), + 'name': serializer.toJson(name), + 'faceAssetId': serializer.toJson(faceAssetId), + 'isFavorite': serializer.toJson(isFavorite), + 'isHidden': serializer.toJson(isHidden), + 'color': serializer.toJson(color), + 'birthDate': serializer.toJson(birthDate), + }; + } + + PersonEntityData copyWith({ + String? id, + DateTime? createdAt, + DateTime? updatedAt, + String? ownerId, + String? name, + Value faceAssetId = const Value.absent(), + bool? isFavorite, + bool? isHidden, + Value color = const Value.absent(), + Value birthDate = const Value.absent(), + }) => PersonEntityData( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId.present ? faceAssetId.value : this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color.present ? color.value : this.color, + birthDate: birthDate.present ? birthDate.value : this.birthDate, + ); + PersonEntityData copyWithCompanion(PersonEntityCompanion data) { + return PersonEntityData( + id: data.id.present ? data.id.value : this.id, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, + ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId, + name: data.name.present ? data.name.value : this.name, + faceAssetId: data.faceAssetId.present + ? data.faceAssetId.value + : this.faceAssetId, + isFavorite: data.isFavorite.present + ? data.isFavorite.value + : this.isFavorite, + isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden, + color: data.color.present ? data.color.value : this.color, + birthDate: data.birthDate.present ? data.birthDate.value : this.birthDate, + ); + } + + @override + String toString() { + return (StringBuffer('PersonEntityData(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + createdAt, + updatedAt, + ownerId, + name, + faceAssetId, + isFavorite, + isHidden, + color, + birthDate, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is PersonEntityData && + other.id == this.id && + other.createdAt == this.createdAt && + other.updatedAt == this.updatedAt && + other.ownerId == this.ownerId && + other.name == this.name && + other.faceAssetId == this.faceAssetId && + other.isFavorite == this.isFavorite && + other.isHidden == this.isHidden && + other.color == this.color && + other.birthDate == this.birthDate); +} + +class PersonEntityCompanion extends UpdateCompanion { + final Value id; + final Value createdAt; + final Value updatedAt; + final Value ownerId; + final Value name; + final Value faceAssetId; + final Value isFavorite; + final Value isHidden; + final Value color; + final Value birthDate; + const PersonEntityCompanion({ + this.id = const Value.absent(), + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + this.ownerId = const Value.absent(), + this.name = const Value.absent(), + this.faceAssetId = const Value.absent(), + this.isFavorite = const Value.absent(), + this.isHidden = const Value.absent(), + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }); + PersonEntityCompanion.insert({ + required String id, + this.createdAt = const Value.absent(), + this.updatedAt = const Value.absent(), + required String ownerId, + required String name, + this.faceAssetId = const Value.absent(), + required bool isFavorite, + required bool isHidden, + this.color = const Value.absent(), + this.birthDate = const Value.absent(), + }) : id = Value(id), + ownerId = Value(ownerId), + name = Value(name), + isFavorite = Value(isFavorite), + isHidden = Value(isHidden); + static Insertable custom({ + Expression? id, + Expression? createdAt, + Expression? updatedAt, + Expression? ownerId, + Expression? name, + Expression? faceAssetId, + Expression? isFavorite, + Expression? isHidden, + Expression? color, + Expression? birthDate, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (createdAt != null) 'created_at': createdAt, + if (updatedAt != null) 'updated_at': updatedAt, + if (ownerId != null) 'owner_id': ownerId, + if (name != null) 'name': name, + if (faceAssetId != null) 'face_asset_id': faceAssetId, + if (isFavorite != null) 'is_favorite': isFavorite, + if (isHidden != null) 'is_hidden': isHidden, + if (color != null) 'color': color, + if (birthDate != null) 'birth_date': birthDate, + }); + } + + PersonEntityCompanion copyWith({ + Value? id, + Value? createdAt, + Value? updatedAt, + Value? ownerId, + Value? name, + Value? faceAssetId, + Value? isFavorite, + Value? isHidden, + Value? color, + Value? birthDate, + }) { + return PersonEntityCompanion( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ownerId: ownerId ?? this.ownerId, + name: name ?? this.name, + faceAssetId: faceAssetId ?? this.faceAssetId, + isFavorite: isFavorite ?? this.isFavorite, + isHidden: isHidden ?? this.isHidden, + color: color ?? this.color, + birthDate: birthDate ?? this.birthDate, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (updatedAt.present) { + map['updated_at'] = Variable(updatedAt.value); + } + if (ownerId.present) { + map['owner_id'] = Variable(ownerId.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (faceAssetId.present) { + map['face_asset_id'] = Variable(faceAssetId.value); + } + if (isFavorite.present) { + map['is_favorite'] = Variable(isFavorite.value); + } + if (isHidden.present) { + map['is_hidden'] = Variable(isHidden.value); + } + if (color.present) { + map['color'] = Variable(color.value); + } + if (birthDate.present) { + map['birth_date'] = Variable(birthDate.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('PersonEntityCompanion(') + ..write('id: $id, ') + ..write('createdAt: $createdAt, ') + ..write('updatedAt: $updatedAt, ') + ..write('ownerId: $ownerId, ') + ..write('name: $name, ') + ..write('faceAssetId: $faceAssetId, ') + ..write('isFavorite: $isFavorite, ') + ..write('isHidden: $isHidden, ') + ..write('color: $color, ') + ..write('birthDate: $birthDate') + ..write(')')) + .toString(); + } +} + +class AssetFaceEntity extends Table + with TableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + AssetFaceEntity(this.attachedDatabase, [this._alias]); + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + late final GeneratedColumn assetId = GeneratedColumn( + 'asset_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES remote_asset_entity (id) ON DELETE CASCADE', + ), + ); + late final GeneratedColumn personId = GeneratedColumn( + 'person_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES person_entity (id) ON DELETE SET NULL', + ), + ); + late final GeneratedColumn imageWidth = GeneratedColumn( + 'image_width', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn imageHeight = GeneratedColumn( + 'image_height', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX1 = GeneratedColumn( + 'bounding_box_x1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY1 = GeneratedColumn( + 'bounding_box_y1', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxX2 = GeneratedColumn( + 'bounding_box_x2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn boundingBoxY2 = GeneratedColumn( + 'bounding_box_y2', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + late final GeneratedColumn sourceType = GeneratedColumn( + 'source_type', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'asset_face_entity'; + @override + Set get $primaryKey => {id}; + @override + AssetFaceEntityData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return AssetFaceEntityData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + assetId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}asset_id'], + )!, + personId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}person_id'], + ), + imageWidth: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_width'], + )!, + imageHeight: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}image_height'], + )!, + boundingBoxX1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x1'], + )!, + boundingBoxY1: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y1'], + )!, + boundingBoxX2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_x2'], + )!, + boundingBoxY2: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}bounding_box_y2'], + )!, + sourceType: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}source_type'], + )!, + ); + } + + @override + AssetFaceEntity createAlias(String alias) { + return AssetFaceEntity(attachedDatabase, alias); + } + + @override + bool get withoutRowId => true; + @override + bool get isStrict => true; +} + +class AssetFaceEntityData extends DataClass + implements Insertable { + final String id; + final String assetId; + final String? personId; + final int imageWidth; + final int imageHeight; + final int boundingBoxX1; + final int boundingBoxY1; + final int boundingBoxX2; + final int boundingBoxY2; + final String sourceType; + const AssetFaceEntityData({ + required this.id, + required this.assetId, + this.personId, + required this.imageWidth, + required this.imageHeight, + required this.boundingBoxX1, + required this.boundingBoxY1, + required this.boundingBoxX2, + required this.boundingBoxY2, + required this.sourceType, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['asset_id'] = Variable(assetId); + if (!nullToAbsent || personId != null) { + map['person_id'] = Variable(personId); + } + map['image_width'] = Variable(imageWidth); + map['image_height'] = Variable(imageHeight); + map['bounding_box_x1'] = Variable(boundingBoxX1); + map['bounding_box_y1'] = Variable(boundingBoxY1); + map['bounding_box_x2'] = Variable(boundingBoxX2); + map['bounding_box_y2'] = Variable(boundingBoxY2); + map['source_type'] = Variable(sourceType); + return map; + } + + factory AssetFaceEntityData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return AssetFaceEntityData( + id: serializer.fromJson(json['id']), + assetId: serializer.fromJson(json['assetId']), + personId: serializer.fromJson(json['personId']), + imageWidth: serializer.fromJson(json['imageWidth']), + imageHeight: serializer.fromJson(json['imageHeight']), + boundingBoxX1: serializer.fromJson(json['boundingBoxX1']), + boundingBoxY1: serializer.fromJson(json['boundingBoxY1']), + boundingBoxX2: serializer.fromJson(json['boundingBoxX2']), + boundingBoxY2: serializer.fromJson(json['boundingBoxY2']), + sourceType: serializer.fromJson(json['sourceType']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'assetId': serializer.toJson(assetId), + 'personId': serializer.toJson(personId), + 'imageWidth': serializer.toJson(imageWidth), + 'imageHeight': serializer.toJson(imageHeight), + 'boundingBoxX1': serializer.toJson(boundingBoxX1), + 'boundingBoxY1': serializer.toJson(boundingBoxY1), + 'boundingBoxX2': serializer.toJson(boundingBoxX2), + 'boundingBoxY2': serializer.toJson(boundingBoxY2), + 'sourceType': serializer.toJson(sourceType), + }; + } + + AssetFaceEntityData copyWith({ + String? id, + String? assetId, + Value personId = const Value.absent(), + int? imageWidth, + int? imageHeight, + int? boundingBoxX1, + int? boundingBoxY1, + int? boundingBoxX2, + int? boundingBoxY2, + String? sourceType, + }) => AssetFaceEntityData( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId.present ? personId.value : this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + AssetFaceEntityData copyWithCompanion(AssetFaceEntityCompanion data) { + return AssetFaceEntityData( + id: data.id.present ? data.id.value : this.id, + assetId: data.assetId.present ? data.assetId.value : this.assetId, + personId: data.personId.present ? data.personId.value : this.personId, + imageWidth: data.imageWidth.present + ? data.imageWidth.value + : this.imageWidth, + imageHeight: data.imageHeight.present + ? data.imageHeight.value + : this.imageHeight, + boundingBoxX1: data.boundingBoxX1.present + ? data.boundingBoxX1.value + : this.boundingBoxX1, + boundingBoxY1: data.boundingBoxY1.present + ? data.boundingBoxY1.value + : this.boundingBoxY1, + boundingBoxX2: data.boundingBoxX2.present + ? data.boundingBoxX2.value + : this.boundingBoxX2, + boundingBoxY2: data.boundingBoxY2.present + ? data.boundingBoxY2.value + : this.boundingBoxY2, + sourceType: data.sourceType.present + ? data.sourceType.value + : this.sourceType, + ); + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityData(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + assetId, + personId, + imageWidth, + imageHeight, + boundingBoxX1, + boundingBoxY1, + boundingBoxX2, + boundingBoxY2, + sourceType, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is AssetFaceEntityData && + other.id == this.id && + other.assetId == this.assetId && + other.personId == this.personId && + other.imageWidth == this.imageWidth && + other.imageHeight == this.imageHeight && + other.boundingBoxX1 == this.boundingBoxX1 && + other.boundingBoxY1 == this.boundingBoxY1 && + other.boundingBoxX2 == this.boundingBoxX2 && + other.boundingBoxY2 == this.boundingBoxY2 && + other.sourceType == this.sourceType); +} + +class AssetFaceEntityCompanion extends UpdateCompanion { + final Value id; + final Value assetId; + final Value personId; + final Value imageWidth; + final Value imageHeight; + final Value boundingBoxX1; + final Value boundingBoxY1; + final Value boundingBoxX2; + final Value boundingBoxY2; + final Value sourceType; + const AssetFaceEntityCompanion({ + this.id = const Value.absent(), + this.assetId = const Value.absent(), + this.personId = const Value.absent(), + this.imageWidth = const Value.absent(), + this.imageHeight = const Value.absent(), + this.boundingBoxX1 = const Value.absent(), + this.boundingBoxY1 = const Value.absent(), + this.boundingBoxX2 = const Value.absent(), + this.boundingBoxY2 = const Value.absent(), + this.sourceType = const Value.absent(), + }); + AssetFaceEntityCompanion.insert({ + required String id, + required String assetId, + this.personId = const Value.absent(), + required int imageWidth, + required int imageHeight, + required int boundingBoxX1, + required int boundingBoxY1, + required int boundingBoxX2, + required int boundingBoxY2, + required String sourceType, + }) : id = Value(id), + assetId = Value(assetId), + imageWidth = Value(imageWidth), + imageHeight = Value(imageHeight), + boundingBoxX1 = Value(boundingBoxX1), + boundingBoxY1 = Value(boundingBoxY1), + boundingBoxX2 = Value(boundingBoxX2), + boundingBoxY2 = Value(boundingBoxY2), + sourceType = Value(sourceType); + static Insertable custom({ + Expression? id, + Expression? assetId, + Expression? personId, + Expression? imageWidth, + Expression? imageHeight, + Expression? boundingBoxX1, + Expression? boundingBoxY1, + Expression? boundingBoxX2, + Expression? boundingBoxY2, + Expression? sourceType, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (assetId != null) 'asset_id': assetId, + if (personId != null) 'person_id': personId, + if (imageWidth != null) 'image_width': imageWidth, + if (imageHeight != null) 'image_height': imageHeight, + if (boundingBoxX1 != null) 'bounding_box_x1': boundingBoxX1, + if (boundingBoxY1 != null) 'bounding_box_y1': boundingBoxY1, + if (boundingBoxX2 != null) 'bounding_box_x2': boundingBoxX2, + if (boundingBoxY2 != null) 'bounding_box_y2': boundingBoxY2, + if (sourceType != null) 'source_type': sourceType, + }); + } + + AssetFaceEntityCompanion copyWith({ + Value? id, + Value? assetId, + Value? personId, + Value? imageWidth, + Value? imageHeight, + Value? boundingBoxX1, + Value? boundingBoxY1, + Value? boundingBoxX2, + Value? boundingBoxY2, + Value? sourceType, + }) { + return AssetFaceEntityCompanion( + id: id ?? this.id, + assetId: assetId ?? this.assetId, + personId: personId ?? this.personId, + imageWidth: imageWidth ?? this.imageWidth, + imageHeight: imageHeight ?? this.imageHeight, + boundingBoxX1: boundingBoxX1 ?? this.boundingBoxX1, + boundingBoxY1: boundingBoxY1 ?? this.boundingBoxY1, + boundingBoxX2: boundingBoxX2 ?? this.boundingBoxX2, + boundingBoxY2: boundingBoxY2 ?? this.boundingBoxY2, + sourceType: sourceType ?? this.sourceType, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (assetId.present) { + map['asset_id'] = Variable(assetId.value); + } + if (personId.present) { + map['person_id'] = Variable(personId.value); + } + if (imageWidth.present) { + map['image_width'] = Variable(imageWidth.value); + } + if (imageHeight.present) { + map['image_height'] = Variable(imageHeight.value); + } + if (boundingBoxX1.present) { + map['bounding_box_x1'] = Variable(boundingBoxX1.value); + } + if (boundingBoxY1.present) { + map['bounding_box_y1'] = Variable(boundingBoxY1.value); + } + if (boundingBoxX2.present) { + map['bounding_box_x2'] = Variable(boundingBoxX2.value); + } + if (boundingBoxY2.present) { + map['bounding_box_y2'] = Variable(boundingBoxY2.value); + } + if (sourceType.present) { + map['source_type'] = Variable(sourceType.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('AssetFaceEntityCompanion(') + ..write('id: $id, ') + ..write('assetId: $assetId, ') + ..write('personId: $personId, ') + ..write('imageWidth: $imageWidth, ') + ..write('imageHeight: $imageHeight, ') + ..write('boundingBoxX1: $boundingBoxX1, ') + ..write('boundingBoxY1: $boundingBoxY1, ') + ..write('boundingBoxX2: $boundingBoxX2, ') + ..write('boundingBoxY2: $boundingBoxY2, ') + ..write('sourceType: $sourceType') + ..write(')')) + .toString(); + } +} + +class DatabaseAtV6 extends GeneratedDatabase { + DatabaseAtV6(QueryExecutor e) : super(e); + late final UserEntity userEntity = UserEntity(this); + late final RemoteAssetEntity remoteAssetEntity = RemoteAssetEntity(this); + late final StackEntity stackEntity = StackEntity(this); + late final LocalAssetEntity localAssetEntity = LocalAssetEntity(this); + late final LocalAlbumEntity localAlbumEntity = LocalAlbumEntity(this); + late final LocalAlbumAssetEntity localAlbumAssetEntity = + LocalAlbumAssetEntity(this); + late final Index idxLocalAssetChecksum = Index( + 'idx_local_asset_checksum', + 'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)', + ); + late final Index idxRemoteAssetOwnerChecksum = Index( + 'idx_remote_asset_owner_checksum', + 'CREATE INDEX idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)', + ); + late final Index uQRemoteAssetsOwnerChecksum = 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)', + ); + late final Index uQRemoteAssetsOwnerLibraryChecksum = 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)', + ); + late final Index idxRemoteAssetChecksum = Index( + 'idx_remote_asset_checksum', + 'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)', + ); + late final UserMetadataEntity userMetadataEntity = UserMetadataEntity(this); + late final PartnerEntity partnerEntity = PartnerEntity(this); + late final RemoteExifEntity remoteExifEntity = RemoteExifEntity(this); + late final RemoteAlbumEntity remoteAlbumEntity = RemoteAlbumEntity(this); + late final RemoteAlbumAssetEntity remoteAlbumAssetEntity = + RemoteAlbumAssetEntity(this); + late final RemoteAlbumUserEntity remoteAlbumUserEntity = + RemoteAlbumUserEntity(this); + late final MemoryEntity memoryEntity = MemoryEntity(this); + late final MemoryAssetEntity memoryAssetEntity = MemoryAssetEntity(this); + late final PersonEntity personEntity = PersonEntity(this); + late final AssetFaceEntity assetFaceEntity = AssetFaceEntity(this); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [ + userEntity, + remoteAssetEntity, + stackEntity, + localAssetEntity, + localAlbumEntity, + localAlbumAssetEntity, + idxLocalAssetChecksum, + idxRemoteAssetOwnerChecksum, + uQRemoteAssetsOwnerChecksum, + uQRemoteAssetsOwnerLibraryChecksum, + idxRemoteAssetChecksum, + userMetadataEntity, + partnerEntity, + remoteExifEntity, + remoteAlbumEntity, + remoteAlbumAssetEntity, + remoteAlbumUserEntity, + memoryEntity, + memoryAssetEntity, + personEntity, + assetFaceEntity, + ]; + @override + int get schemaVersion => 6; + @override + DriftDatabaseOptions get options => + const DriftDatabaseOptions(storeDateTimeAsText: true); +} diff --git a/mobile/test/fixtures/album.stub.dart b/mobile/test/fixtures/album.stub.dart index 1e79f62faf..a22a4b72ab 100644 --- a/mobile/test/fixtures/album.stub.dart +++ b/mobile/test/fixtures/album.stub.dart @@ -42,20 +42,21 @@ final class AlbumStub { endDate: DateTime(2023), )..assets.addAll([AssetStub.image1]); - static final twoAsset = Album( - name: "album-with-two-assets", - localId: "album-with-two-assets-local", - remoteId: "album-with-two-assets-remote", - createdAt: DateTime(2001), - modifiedAt: DateTime(2010), - shared: false, - activityEnabled: false, - startDate: DateTime(2019), - endDate: DateTime(2020), - ) - ..assets.addAll([AssetStub.image1, AssetStub.image2]) - ..activityEnabled = true - ..owner.value = User.fromDto(UserStub.admin); + static final twoAsset = + Album( + name: "album-with-two-assets", + localId: "album-with-two-assets-local", + remoteId: "album-with-two-assets-remote", + createdAt: DateTime(2001), + modifiedAt: DateTime(2010), + shared: false, + activityEnabled: false, + startDate: DateTime(2019), + endDate: DateTime(2020), + ) + ..assets.addAll([AssetStub.image1, AssetStub.image2]) + ..activityEnabled = true + ..owner.value = User.fromDto(UserStub.admin); static final create2020end2020Album = Album( name: "create2020update2020Album", diff --git a/mobile/test/fixtures/sync_stream.stub.dart b/mobile/test/fixtures/sync_stream.stub.dart index de2d58bc9d..23c750d6d9 100644 --- a/mobile/test/fixtures/sync_stream.stub.dart +++ b/mobile/test/fixtures/sync_stream.stub.dart @@ -9,6 +9,9 @@ abstract final class SyncStreamStub { email: "admin@admin", id: "1", name: "Admin", + avatarColor: null, + hasProfileImage: false, + profileChangedAt: DateTime(2025), ), ack: "1", ); @@ -19,6 +22,9 @@ abstract final class SyncStreamStub { email: "user@user", id: "5", name: "User", + avatarColor: null, + hasProfileImage: false, + profileChangedAt: DateTime(2025), ), ack: "5", ); @@ -30,11 +36,7 @@ abstract final class SyncStreamStub { static final partnerV1 = SyncEvent( type: SyncEntityType.partnerV1, - data: SyncPartnerV1( - inTimeline: true, - sharedById: "1", - sharedWithId: "2", - ), + data: SyncPartnerV1(inTimeline: true, sharedById: "1", sharedWithId: "2"), ack: "3", ); static final partnerDeleteV1 = SyncEvent( @@ -70,19 +72,13 @@ abstract final class SyncStreamStub { static final memoryToAssetV1 = SyncEvent( type: SyncEntityType.memoryToAssetV1, - data: SyncMemoryAssetV1( - assetId: "asset-1", - memoryId: "memory-1", - ), + data: SyncMemoryAssetV1(assetId: "asset-1", memoryId: "memory-1"), ack: "7", ); static final memoryToAssetDeleteV1 = SyncEvent( type: SyncEntityType.memoryToAssetDeleteV1, - data: SyncMemoryAssetDeleteV1( - assetId: "asset-2", - memoryId: "memory-1", - ), + data: SyncMemoryAssetDeleteV1(assetId: "asset-2", memoryId: "memory-1"), ack: "8", ); } diff --git a/mobile/test/fixtures/user.stub.dart b/mobile/test/fixtures/user.stub.dart index 764342520f..369e62440d 100644 --- a/mobile/test/fixtures/user.stub.dart +++ b/mobile/test/fixtures/user.stub.dart @@ -10,7 +10,7 @@ abstract final class UserStub { name: "admin", isAdmin: true, updatedAt: DateTime(2021), - profileImagePath: null, + profileChangedAt: DateTime(2021), avatarColor: AvatarColor.green, ); @@ -20,7 +20,7 @@ abstract final class UserStub { name: "user1", isAdmin: false, updatedAt: DateTime(2022), - profileImagePath: null, + profileChangedAt: DateTime(2022), avatarColor: AvatarColor.red, ); @@ -30,7 +30,7 @@ abstract final class UserStub { name: "user2", isAdmin: false, updatedAt: DateTime(2023), - profileImagePath: null, + profileChangedAt: DateTime(2023), avatarColor: AvatarColor.primary, ); } diff --git a/mobile/test/infrastructure/repositories/local_album_repository_test.dart b/mobile/test/infrastructure/repositories/local_album_repository_test.dart index bab25de52a..fae0e09171 100644 --- a/mobile/test/infrastructure/repositories/local_album_repository_test.dart +++ b/mobile/test/infrastructure/repositories/local_album_repository_test.dart @@ -12,49 +12,21 @@ void main() { late MediumFactory mediumFactory; setUp(() { - db = Drift( - DatabaseConnection( - NativeDatabase.memory(), - closeStreamsSynchronously: true, - ), - ); + db = Drift(DatabaseConnection(NativeDatabase.memory(), closeStreamsSynchronously: true)); mediumFactory = MediumFactory(db); }); group('getAll', () { test('sorts albums by backupSelection & isIosSharedAlbum', () async { - final localAlbumRepo = - mediumFactory.getRepository(); + final localAlbumRepo = mediumFactory.getRepository(); + await localAlbumRepo.upsert(mediumFactory.localAlbum(id: '1', backupSelection: BackupSelection.none)); + await localAlbumRepo.upsert(mediumFactory.localAlbum(id: '2', backupSelection: BackupSelection.excluded)); await localAlbumRepo.upsert( - mediumFactory.localAlbum( - id: '1', - backupSelection: BackupSelection.none, - ), - ); - await localAlbumRepo.upsert( - mediumFactory.localAlbum( - id: '2', - backupSelection: BackupSelection.excluded, - ), - ); - await localAlbumRepo.upsert( - mediumFactory.localAlbum( - id: '3', - backupSelection: BackupSelection.selected, - isIosSharedAlbum: true, - ), - ); - await localAlbumRepo.upsert( - mediumFactory.localAlbum( - id: '4', - backupSelection: BackupSelection.selected, - ), + mediumFactory.localAlbum(id: '3', backupSelection: BackupSelection.selected, isIosSharedAlbum: true), ); + await localAlbumRepo.upsert(mediumFactory.localAlbum(id: '4', backupSelection: BackupSelection.selected)); final albums = await localAlbumRepo.getAll( - sortBy: { - SortLocalAlbumsBy.backupSelection, - SortLocalAlbumsBy.isIosSharedAlbum, - }, + sortBy: {SortLocalAlbumsBy.backupSelection, SortLocalAlbumsBy.isIosSharedAlbum}, ); expect(albums.length, 4); expect(albums[0].id, '4'); // selected diff --git a/mobile/test/infrastructure/repositories/store_repository_test.dart b/mobile/test/infrastructure/repositories/store_repository_test.dart index ce13c1ecdd..84d18ad955 100644 --- a/mobile/test/infrastructure/repositories/store_repository_test.dart +++ b/mobile/test/infrastructure/repositories/store_repository_test.dart @@ -24,16 +24,8 @@ Future _addStrStoreValue(Isar db, StoreKey key, String? value) async { Future _populateStore(Isar db) async { await db.writeTxn(() async { - await _addIntStoreValue( - db, - StoreKey.colorfulInterface, - _kTestColorfulInterface ? 1 : 0, - ); - await _addIntStoreValue( - db, - StoreKey.backupFailedSince, - _kTestBackupFailed.millisecondsSinceEpoch, - ); + await _addIntStoreValue(db, StoreKey.colorfulInterface, _kTestColorfulInterface ? 1 : 0); + await _addIntStoreValue(db, StoreKey.backupFailedSince, _kTestBackupFailed.millisecondsSinceEpoch); await _addStrStoreValue(db, StoreKey.accessToken, _kTestAccessToken); await _addIntStoreValue(db, StoreKey.version, _kTestVersion); }); @@ -66,8 +58,7 @@ void main() { }); test('converts datetime', () async { - DateTime? backupFailedSince = - await sut.tryGet(StoreKey.backupFailedSince); + DateTime? backupFailedSince = await sut.tryGet(StoreKey.backupFailedSince); expect(backupFailedSince, isNull); await sut.insert(StoreKey.backupFailedSince, _kTestBackupFailed); backupFailedSince = await sut.tryGet(StoreKey.backupFailedSince); @@ -144,21 +135,10 @@ void main() { stream, emitsInAnyOrder([ emits(const StoreDto(StoreKey.version, _kTestVersion)), - emits( - StoreDto(StoreKey.backupFailedSince, _kTestBackupFailed), - ), - emits( - const StoreDto(StoreKey.accessToken, _kTestAccessToken), - ), - emits( - const StoreDto( - StoreKey.colorfulInterface, - _kTestColorfulInterface, - ), - ), - emits( - const StoreDto(StoreKey.version, _kTestVersion + 10), - ), + emits(StoreDto(StoreKey.backupFailedSince, _kTestBackupFailed)), + emits(const StoreDto(StoreKey.accessToken, _kTestAccessToken)), + emits(const StoreDto(StoreKey.colorfulInterface, _kTestColorfulInterface)), + emits(const StoreDto(StoreKey.version, _kTestVersion + 10)), ]), ); await sut.update(StoreKey.version, _kTestVersion + 10); diff --git a/mobile/test/infrastructure/repositories/sync_api_repository_test.dart b/mobile/test/infrastructure/repositories/sync_api_repository_test.dart index 55b03a8116..d456b06f7c 100644 --- a/mobile/test/infrastructure/repositories/sync_api_repository_test.dart +++ b/mobile/test/infrastructure/repositories/sync_api_repository_test.dart @@ -39,23 +39,19 @@ void main() { mockSyncApi = MockSyncApi(); mockHttpClient = MockHttpClient(); mockStreamedResponse = MockStreamedResponse(); - responseStreamController = - StreamController>.broadcast(sync: true); + responseStreamController = StreamController>.broadcast(sync: true); registerFallbackValue(FakeBaseRequest()); when(() => mockApiService.apiClient).thenReturn(mockApiClient); when(() => mockApiService.syncApi).thenReturn(mockSyncApi); when(() => mockApiClient.basePath).thenReturn('http://demo.immich.app/api'); - when(() => mockApiService.applyToParams(any(), any())) - .thenAnswer((_) async => {}); + when(() => mockApiService.applyToParams(any(), any())).thenAnswer((_) async => {}); // Mock HTTP client behavior - when(() => mockHttpClient.send(any())) - .thenAnswer((_) async => mockStreamedResponse); + when(() => mockHttpClient.send(any())).thenAnswer((_) async => mockStreamedResponse); when(() => mockStreamedResponse.statusCode).thenReturn(200); - when(() => mockStreamedResponse.stream) - .thenAnswer((_) => http.ByteStream(responseStreamController.stream)); + when(() => mockStreamedResponse.stream).thenAnswer((_) => http.ByteStream(responseStreamController.stream)); when(() => mockHttpClient.close()).thenAnswer((_) => {}); sut = SyncApiRepository(mockApiService); @@ -67,14 +63,8 @@ void main() { } }); - Future streamChanges( - Function(List, Function() abort) onDataCallback, - ) { - return sut.streamChanges( - onDataCallback, - batchSize: testBatchSize, - httpClient: mockHttpClient, - ); + Future streamChanges(Function(List, Function() abort) onDataCallback) { + return sut.streamChanges(onDataCallback, batchSize: testBatchSize, httpClient: mockHttpClient); } test('streamChanges stops processing stream when abort is called', () async { @@ -100,11 +90,7 @@ void main() { for (int i = 0; i < testBatchSize; i++) { responseStreamController.add( utf8.encode( - _createJsonLine( - SyncEntityType.userDeleteV1.toString(), - SyncUserDeleteV1(userId: "user$i").toJson(), - 'ack$i', - ), + _createJsonLine(SyncEntityType.userDeleteV1.toString(), SyncUserDeleteV1(userId: "user$i").toJson(), 'ack$i'), ), ); } @@ -112,11 +98,7 @@ void main() { for (int i = testBatchSize; i < testBatchSize * 2; i++) { responseStreamController.add( utf8.encode( - _createJsonLine( - SyncEntityType.userDeleteV1.toString(), - SyncUserDeleteV1(userId: "user$i").toJson(), - 'ack$i', - ), + _createJsonLine(SyncEntityType.userDeleteV1.toString(), SyncUserDeleteV1(userId: "user$i").toJson(), 'ack$i'), ), ); } @@ -130,113 +112,91 @@ void main() { verify(() => mockHttpClient.close()).called(1); }); - test( - 'streamChanges does not process remaining lines in finally block if aborted', - () async { - int onDataCallCount = 0; - bool abortWasCalledInCallback = false; + test('streamChanges does not process remaining lines in finally block if aborted', () async { + int onDataCallCount = 0; + bool abortWasCalledInCallback = false; - onDataCallback(List events, Function() abort) { - onDataCallCount++; - if (onDataCallCount == 1) { - abort(); - abortWasCalledInCallback = true; - } else { - fail("onData called more than once after abort was invoked"); - } + onDataCallback(List events, Function() abort) { + onDataCallCount++; + if (onDataCallCount == 1) { + abort(); + abortWasCalledInCallback = true; + } else { + fail("onData called more than once after abort was invoked"); } + } - final streamChangesFuture = streamChanges(onDataCallback); + final streamChangesFuture = streamChanges(onDataCallback); - await pumpEventQueue(); + await pumpEventQueue(); - for (int i = 0; i < testBatchSize; i++) { - responseStreamController.add( - utf8.encode( - _createJsonLine( - SyncEntityType.userDeleteV1.toString(), - SyncUserDeleteV1(userId: "user$i").toJson(), - 'ack$i', - ), - ), - ); - } - - // emit a single event to skip batching and trigger finally + for (int i = 0; i < testBatchSize; i++) { responseStreamController.add( utf8.encode( - _createJsonLine( - SyncEntityType.userDeleteV1.toString(), - SyncUserDeleteV1(userId: "user100").toJson(), - 'ack100', - ), + _createJsonLine(SyncEntityType.userDeleteV1.toString(), SyncUserDeleteV1(userId: "user$i").toJson(), 'ack$i'), ), ); + } - await responseStreamController.close(); - await expectLater(streamChangesFuture, completes); + // emit a single event to skip batching and trigger finally + responseStreamController.add( + utf8.encode( + _createJsonLine(SyncEntityType.userDeleteV1.toString(), SyncUserDeleteV1(userId: "user100").toJson(), 'ack100'), + ), + ); - expect(onDataCallCount, 1); - expect(abortWasCalledInCallback, isTrue); - verify(() => mockHttpClient.close()).called(1); - }, - ); + await responseStreamController.close(); + await expectLater(streamChangesFuture, completes); - test( - 'streamChanges processes remaining lines in finally block if not aborted', - () async { - int onDataCallCount = 0; - List receivedEventsBatch1 = []; - List receivedEventsBatch2 = []; + expect(onDataCallCount, 1); + expect(abortWasCalledInCallback, isTrue); + verify(() => mockHttpClient.close()).called(1); + }); - onDataCallback(List events, Function() _) { - onDataCallCount++; - if (onDataCallCount == 1) { - receivedEventsBatch1 = events; - } else if (onDataCallCount == 2) { - receivedEventsBatch2 = events; - } else { - fail("onData called more than expected"); - } + test('streamChanges processes remaining lines in finally block if not aborted', () async { + int onDataCallCount = 0; + List receivedEventsBatch1 = []; + List receivedEventsBatch2 = []; + + onDataCallback(List events, Function() _) { + onDataCallCount++; + if (onDataCallCount == 1) { + receivedEventsBatch1 = events; + } else if (onDataCallCount == 2) { + receivedEventsBatch2 = events; + } else { + fail("onData called more than expected"); } + } - final streamChangesFuture = streamChanges(onDataCallback); + final streamChangesFuture = streamChanges(onDataCallback); - await pumpEventQueue(); + await pumpEventQueue(); - // Batch 1 - for (int i = 0; i < testBatchSize; i++) { - responseStreamController.add( - utf8.encode( - _createJsonLine( - SyncEntityType.userDeleteV1.toString(), - SyncUserDeleteV1(userId: "user$i").toJson(), - 'ack$i', - ), - ), - ); - } - - // Partial Batch 2 + // Batch 1 + for (int i = 0; i < testBatchSize; i++) { responseStreamController.add( utf8.encode( - _createJsonLine( - SyncEntityType.userDeleteV1.toString(), - SyncUserDeleteV1(userId: "user100").toJson(), - 'ack100', - ), + _createJsonLine(SyncEntityType.userDeleteV1.toString(), SyncUserDeleteV1(userId: "user$i").toJson(), 'ack$i'), ), ); + } - await responseStreamController.close(); - await expectLater(streamChangesFuture, completes); + // Partial Batch 2 + responseStreamController.add( + utf8.encode( + _createJsonLine(SyncEntityType.userDeleteV1.toString(), SyncUserDeleteV1(userId: "user100").toJson(), 'ack100'), + ), + ); - expect(onDataCallCount, 2); - expect(receivedEventsBatch1.length, testBatchSize); - expect(receivedEventsBatch2.length, 1); - verify(() => mockHttpClient.close()).called(1); - }, - ); + await responseStreamController.close(); + await expectLater(streamChangesFuture, completes); + + expect(onDataCallCount, 2); + expect(receivedEventsBatch1.length, testBatchSize); + expect(receivedEventsBatch2.length, 1); + verify(() => mockHttpClient.close()).called(1); + }); test('streamChanges handles stream error gracefully', () async { final streamError = Exception("Network Error"); @@ -252,11 +212,7 @@ void main() { responseStreamController.add( utf8.encode( - _createJsonLine( - SyncEntityType.userDeleteV1.toString(), - SyncUserDeleteV1(userId: "user1").toJson(), - 'ack1', - ), + _createJsonLine(SyncEntityType.userDeleteV1.toString(), SyncUserDeleteV1(userId: "user1").toJson(), 'ack1'), ), ); @@ -270,8 +226,7 @@ void main() { test('streamChanges throws ApiException on non-200 status code', () async { when(() => mockStreamedResponse.statusCode).thenReturn(401); final errorBodyController = StreamController>(sync: true); - when(() => mockStreamedResponse.stream) - .thenAnswer((_) => http.ByteStream(errorBodyController.stream)); + when(() => mockStreamedResponse.stream).thenAnswer((_) => http.ByteStream(errorBodyController.stream)); int onDataCallCount = 0; diff --git a/mobile/test/infrastructure/repository.mock.dart b/mobile/test/infrastructure/repository.mock.dart index 1fde303863..ed20f177b7 100644 --- a/mobile/test/infrastructure/repository.mock.dart +++ b/mobile/test/infrastructure/repository.mock.dart @@ -16,16 +16,13 @@ class MockLogRepository extends Mock implements IsarLogRepository {} class MockIsarUserRepository extends Mock implements IsarUserRepository {} -class MockDeviceAssetRepository extends Mock - implements IsarDeviceAssetRepository {} +class MockDeviceAssetRepository extends Mock implements IsarDeviceAssetRepository {} class MockSyncStreamRepository extends Mock implements SyncStreamRepository {} -class MockLocalAlbumRepository extends Mock - implements DriftLocalAlbumRepository {} +class MockLocalAlbumRepository extends Mock implements DriftLocalAlbumRepository {} -class MockLocalAssetRepository extends Mock - implements DriftLocalAssetRepository {} +class MockLocalAssetRepository extends Mock implements DriftLocalAssetRepository {} class MockStorageRepository extends Mock implements StorageRepository {} diff --git a/mobile/test/mock_http_override.dart b/mobile/test/mock_http_override.dart index c25fb79b50..877d6f8726 100644 --- a/mobile/test/mock_http_override.dart +++ b/mobile/test/mock_http_override.dart @@ -18,15 +18,12 @@ class MockHttpOverrides extends HttpOverrides { // Request mocks when(() => request.headers).thenAnswer((_) => headers); - when(() => request.close()) - .thenAnswer((_) => Future.value(response)); + when(() => request.close()).thenAnswer((_) => Future.value(response)); // Response mocks when(() => response.statusCode).thenReturn(HttpStatus.ok); - when(() => response.compressionState) - .thenReturn(HttpClientResponseCompressionState.decompressed); - when(() => response.contentLength) - .thenAnswer((_) => kTransparentImage.length); + when(() => response.compressionState).thenReturn(HttpClientResponseCompressionState.decompressed); + when(() => response.contentLength).thenAnswer((_) => kTransparentImage.length); when( () => response.listen( captureAny(), @@ -35,23 +32,17 @@ class MockHttpOverrides extends HttpOverrides { onError: captureAny(named: 'onError'), ), ).thenAnswer((invocation) { - final onData = - invocation.positionalArguments[0] as void Function(List); + final onData = invocation.positionalArguments[0] as void Function(List); final onDone = invocation.namedArguments[#onDone] as void Function(); - final onError = invocation.namedArguments[#onError] as void - Function(Object, [StackTrace]); + final onError = invocation.namedArguments[#onError] as void Function(Object, [StackTrace]); final cancelOnError = invocation.namedArguments[#cancelOnError] as bool; - return Stream>.fromIterable([kTransparentImage.toList()]) - .listen( - onData, - onDone: onDone, - onError: onError, - cancelOnError: cancelOnError, - ); + return Stream>.fromIterable([ + kTransparentImage.toList(), + ]).listen(onData, onDone: onDone, onError: onError, cancelOnError: cancelOnError); }); return client; diff --git a/mobile/test/modules/activity/activities_page_test.dart b/mobile/test/modules/activity/activities_page_test.dart index cf9238d205..05eac98111 100644 --- a/mobile/test/modules/activity/activities_page_test.dart +++ b/mobile/test/modules/activity/activities_page_test.dart @@ -49,19 +49,8 @@ final _activities = [ comment: 'Second Activity', user: UserStub.user1, ), - Activity( - id: '3', - createdAt: DateTime(300), - type: ActivityType.like, - assetId: 'asset-1', - user: UserStub.user2, - ), - Activity( - id: '4', - createdAt: DateTime(400), - type: ActivityType.like, - user: UserStub.user1, - ), + Activity(id: '3', createdAt: DateTime(300), type: ActivityType.like, assetId: 'asset-1', user: UserStub.user2), + Activity(id: '4', createdAt: DateTime(400), type: ActivityType.like, user: UserStub.user1), ]; void main() { @@ -85,10 +74,7 @@ void main() { mockCurrentAssetProvider = MockCurrentAssetProvider(AssetStub.image1); activityMock = MockAlbumActivity(_activities); overrides = [ - albumActivityProvider( - AlbumStub.twoAsset.remoteId!, - AssetStub.image1.remoteId!, - ).overrideWith(() => activityMock), + albumActivityProvider(AlbumStub.twoAsset.remoteId!, AssetStub.image1.remoteId!).overrideWith(() => activityMock), currentAlbumProvider.overrideWith(() => mockCurrentAlbumProvider), currentAssetProvider.overrideWith(() => mockCurrentAssetProvider), ]; @@ -108,148 +94,82 @@ void main() { }); group("App bar", () { - testWidgets( - "No title when currentAsset != null", - (tester) async { - await tester.pumpConsumerWidget( - const ActivitiesPage(), - overrides: overrides, - ); + testWidgets("No title when currentAsset != null", (tester) async { + await tester.pumpConsumerWidget(const ActivitiesPage(), overrides: overrides); - final listTile = tester.widget(find.byType(AppBar)); - expect(listTile.title, isNull); - }, - ); + final listTile = tester.widget(find.byType(AppBar)); + expect(listTile.title, isNull); + }); - testWidgets( - "Album name as title when currentAsset == null", - (tester) async { - await tester.pumpConsumerWidget( - const ActivitiesPage(), - overrides: overrides, - ); - await tester.pumpAndSettle(); + testWidgets("Album name as title when currentAsset == null", (tester) async { + await tester.pumpConsumerWidget(const ActivitiesPage(), overrides: overrides); + await tester.pumpAndSettle(); - mockCurrentAssetProvider.state = null; - await tester.pumpAndSettle(); + mockCurrentAssetProvider.state = null; + await tester.pumpAndSettle(); - expect(find.text(AlbumStub.twoAsset.name), findsOneWidget); - final listTile = tester.widget(find.byType(AppBar)); - expect(listTile.title, isNotNull); - }, - ); + expect(find.text(AlbumStub.twoAsset.name), findsOneWidget); + final listTile = tester.widget(find.byType(AppBar)); + expect(listTile.title, isNotNull); + }); }); group("Body", () { - testWidgets( - "Contains a stack with Activity List and Activity Input", - (tester) async { - await tester.pumpConsumerWidget( - const ActivitiesPage(), - overrides: overrides, - ); - await tester.pumpAndSettle(); + testWidgets("Contains a stack with Activity List and Activity Input", (tester) async { + await tester.pumpConsumerWidget(const ActivitiesPage(), overrides: overrides); + await tester.pumpAndSettle(); - expect( - find.descendant( - of: find.byType(Stack), - matching: find.byType(ActivityTextField), - ), - findsOneWidget, - ); + expect(find.descendant(of: find.byType(Stack), matching: find.byType(ActivityTextField)), findsOneWidget); - expect( - find.descendant( - of: find.byType(Stack), - matching: find.byType(ListView), - ), - findsOneWidget, - ); - }, - ); + expect(find.descendant(of: find.byType(Stack), matching: find.byType(ListView)), findsOneWidget); + }); - testWidgets( - "List Contains all dismissible activities", - (tester) async { - await tester.pumpConsumerWidget( - const ActivitiesPage(), - overrides: overrides, - ); - await tester.pumpAndSettle(); + testWidgets("List Contains all dismissible activities", (tester) async { + await tester.pumpConsumerWidget(const ActivitiesPage(), overrides: overrides); + await tester.pumpAndSettle(); - final listFinder = find.descendant( - of: find.byType(Stack), - matching: find.byType(ListView), - ); - final listChildren = find.descendant( - of: listFinder, - matching: find.byType(DismissibleActivity), - ); - expect(listChildren, findsNWidgets(_activities.length)); - }, - ); + final listFinder = find.descendant(of: find.byType(Stack), matching: find.byType(ListView)); + final listChildren = find.descendant(of: listFinder, matching: find.byType(DismissibleActivity)); + expect(listChildren, findsNWidgets(_activities.length)); + }); - testWidgets( - "Submitting text input adds a comment with the text", - (tester) async { - await tester.pumpConsumerWidget( - const ActivitiesPage(), - overrides: overrides, - ); - await tester.pumpAndSettle(); + testWidgets("Submitting text input adds a comment with the text", (tester) async { + await tester.pumpConsumerWidget(const ActivitiesPage(), overrides: overrides); + await tester.pumpAndSettle(); - when(() => activityMock.addComment(any())) - .thenAnswer((_) => Future.value()); + when(() => activityMock.addComment(any())).thenAnswer((_) => Future.value()); - final textField = find.byType(TextField); - await tester.enterText(textField, 'Test comment'); - await tester.testTextInput.receiveAction(TextInputAction.done); + final textField = find.byType(TextField); + await tester.enterText(textField, 'Test comment'); + await tester.testTextInput.receiveAction(TextInputAction.done); - verify(() => activityMock.addComment('Test comment')); - }, - ); + verify(() => activityMock.addComment('Test comment')); + }); - testWidgets( - "Owner can remove all activities", - (tester) async { - await tester.pumpConsumerWidget( - const ActivitiesPage(), - overrides: overrides, - ); - await tester.pumpAndSettle(); + testWidgets("Owner can remove all activities", (tester) async { + await tester.pumpConsumerWidget(const ActivitiesPage(), overrides: overrides); + await tester.pumpAndSettle(); - final deletableActivityFinder = find.byWidgetPredicate( - (widget) => widget is DismissibleActivity && widget.onDismiss != null, - ); - expect(deletableActivityFinder, findsNWidgets(_activities.length)); - }, - ); + final deletableActivityFinder = find.byWidgetPredicate( + (widget) => widget is DismissibleActivity && widget.onDismiss != null, + ); + expect(deletableActivityFinder, findsNWidgets(_activities.length)); + }); - testWidgets( - "Non-Owner can remove only their activities", - (tester) async { - final mockCurrentUser = MockCurrentUserProvider(); + testWidgets("Non-Owner can remove only their activities", (tester) async { + final mockCurrentUser = MockCurrentUserProvider(); - await tester.pumpConsumerWidget( - const ActivitiesPage(), - overrides: [ - ...overrides, - currentUserProvider.overrideWith((ref) => mockCurrentUser), - ], - ); - mockCurrentUser.state = UserStub.user1; - await tester.pumpAndSettle(); + await tester.pumpConsumerWidget( + const ActivitiesPage(), + overrides: [...overrides, currentUserProvider.overrideWith((ref) => mockCurrentUser)], + ); + mockCurrentUser.state = UserStub.user1; + await tester.pumpAndSettle(); - final deletableActivityFinder = find.byWidgetPredicate( - (widget) => widget is DismissibleActivity && widget.onDismiss != null, - ); - expect( - deletableActivityFinder, - findsNWidgets( - _activities.where((a) => a.user == UserStub.user1).length, - ), - ); - }, - ); + final deletableActivityFinder = find.byWidgetPredicate( + (widget) => widget is DismissibleActivity && widget.onDismiss != null, + ); + expect(deletableActivityFinder, findsNWidgets(_activities.where((a) => a.user == UserStub.user1).length)); + }); }); } diff --git a/mobile/test/modules/activity/activity_mocks.dart b/mobile/test/modules/activity/activity_mocks.dart index 22fbafdbf3..c50810795e 100644 --- a/mobile/test/modules/activity/activity_mocks.dart +++ b/mobile/test/modules/activity/activity_mocks.dart @@ -6,9 +6,7 @@ import 'package:mocktail/mocktail.dart'; class ActivityServiceMock extends Mock implements ActivityService {} -class MockAlbumActivity extends AlbumActivityInternal - with Mock - implements AlbumActivity { +class MockAlbumActivity extends AlbumActivityInternal with Mock implements AlbumActivity { List? initActivities; MockAlbumActivity([this.initActivities]); @@ -18,6 +16,4 @@ class MockAlbumActivity extends AlbumActivityInternal } } -class ActivityStatisticsMock extends ActivityStatisticsInternal - with Mock - implements ActivityStatistics {} +class ActivityStatisticsMock extends ActivityStatisticsInternal with Mock implements ActivityStatistics {} diff --git a/mobile/test/modules/activity/activity_provider_test.dart b/mobile/test/modules/activity/activity_provider_test.dart index a3b3e2466e..7964b43cad 100644 --- a/mobile/test/modules/activity/activity_provider_test.dart +++ b/mobile/test/modules/activity/activity_provider_test.dart @@ -26,19 +26,8 @@ final _activities = [ comment: 'Second Activity', user: UserStub.user1, ), - Activity( - id: '3', - createdAt: DateTime(300), - type: ActivityType.like, - assetId: 'asset-1', - user: UserStub.admin, - ), - Activity( - id: '4', - createdAt: DateTime(400), - type: ActivityType.like, - user: UserStub.user1, - ), + Activity(id: '3', createdAt: DateTime(300), type: ActivityType.like, assetId: 'asset-1', user: UserStub.admin), + Activity(id: '4', createdAt: DateTime(400), type: ActivityType.like, user: UserStub.user1), ]; void main() { @@ -58,8 +47,7 @@ void main() { container = TestUtils.createContainer( overrides: [ activityServiceProvider.overrideWith((ref) => activityMock), - activityStatisticsProvider('test-album', 'test-asset') - .overrideWith(() => activityStatisticsMock), + activityStatisticsProvider('test-album', 'test-asset').overrideWith(() => activityStatisticsMock), ], ); @@ -71,11 +59,7 @@ void main() { // Init and wait for providers future to complete provider = albumActivityProvider('test-album', 'test-asset'); listener = ListenerMock(); - container.listen( - provider, - listener.call, - fireImmediately: true, - ); + container.listen(provider, listener.call, fireImmediately: true); await container.read(provider.future); }); @@ -84,19 +68,14 @@ void main() { verifyInOrder([ () => listener.call(null, const AsyncLoading()), () => listener.call( - const AsyncLoading(), - any( - that: allOf( - [ - isA>>(), - predicate( - (AsyncData> ad) => - ad.requireValue.every((e) => _activities.contains(e)), - ), - ], - ), - ), - ), + const AsyncLoading(), + any( + that: allOf([ + isA>>(), + predicate((AsyncData> ad) => ad.requireValue.every((e) => _activities.contains(e))), + ]), + ), + ), ]); verifyNoMoreInteractions(listener); @@ -104,30 +83,15 @@ void main() { group('addLike()', () { test('Like successfully added', () async { - final like = Activity( - id: '5', - createdAt: DateTime(2023), - type: ActivityType.like, - user: UserStub.admin, - ); + final like = Activity(id: '5', createdAt: DateTime(2023), type: ActivityType.like, user: UserStub.admin); when( - () => activityMock.addActivity( - 'test-album', - ActivityType.like, - assetId: 'test-asset', - ), + () => activityMock.addActivity('test-album', ActivityType.like, assetId: 'test-asset'), ).thenAnswer((_) async => AsyncData(like)); await container.read(provider.notifier).addLike(); - verify( - () => activityMock.addActivity( - 'test-album', - ActivityType.like, - assetId: 'test-asset', - ), - ); + verify(() => activityMock.addActivity('test-album', ActivityType.like, assetId: 'test-asset')); final activities = await container.read(provider.future); expect(activities, hasLength(5)); @@ -138,31 +102,14 @@ void main() { }); test('Like failed', () async { - final like = Activity( - id: '5', - createdAt: DateTime(2023), - type: ActivityType.like, - user: UserStub.admin, - ); + final like = Activity(id: '5', createdAt: DateTime(2023), type: ActivityType.like, user: UserStub.admin); when( - () => activityMock.addActivity( - 'test-album', - ActivityType.like, - assetId: 'test-asset', - ), - ).thenAnswer( - (_) async => AsyncError(Exception('Mock'), StackTrace.current), - ); + () => activityMock.addActivity('test-album', ActivityType.like, assetId: 'test-asset'), + ).thenAnswer((_) async => AsyncError(Exception('Mock'), StackTrace.current)); await container.read(provider.notifier).addLike(); - verify( - () => activityMock.addActivity( - 'test-album', - ActivityType.like, - assetId: 'test-asset', - ), - ); + verify(() => activityMock.addActivity('test-album', ActivityType.like, assetId: 'test-asset')); final activities = await container.read(provider.future); expect(activities, hasLength(4)); @@ -172,50 +119,36 @@ void main() { group('removeActivity()', () { test('Like successfully removed', () async { - when(() => activityMock.removeActivity('3')) - .thenAnswer((_) async => true); + when(() => activityMock.removeActivity('3')).thenAnswer((_) async => true); await container.read(provider.notifier).removeActivity('3'); - verify( - () => activityMock.removeActivity('3'), - ); + verify(() => activityMock.removeActivity('3')); final activities = await container.read(provider.future); expect(activities, hasLength(3)); - expect( - activities, - isNot(anyElement(predicate((Activity a) => a.id == '3'))), - ); + expect(activities, isNot(anyElement(predicate((Activity a) => a.id == '3')))); verifyNever(() => activityStatisticsMock.removeActivity()); }); test('Remove Like failed', () async { - when(() => activityMock.removeActivity('3')) - .thenAnswer((_) async => false); + when(() => activityMock.removeActivity('3')).thenAnswer((_) async => false); await container.read(provider.notifier).removeActivity('3'); final activities = await container.read(provider.future); expect(activities, hasLength(4)); - expect( - activities, - anyElement(predicate((Activity a) => a.id == '3')), - ); + expect(activities, anyElement(predicate((Activity a) => a.id == '3'))); }); test('Comment successfully removed', () async { - when(() => activityMock.removeActivity('1')) - .thenAnswer((_) async => true); + when(() => activityMock.removeActivity('1')).thenAnswer((_) async => true); await container.read(provider.notifier).removeActivity('1'); final activities = await container.read(provider.future); - expect( - activities, - isNot(anyElement(predicate((Activity a) => a.id == '1'))), - ); + expect(activities, isNot(anyElement(predicate((Activity a) => a.id == '1')))); verify(() => activityStatisticsMock.removeActivity()); }); @@ -229,10 +162,8 @@ void main() { container = TestUtils.createContainer( overrides: [ activityServiceProvider.overrideWith((ref) => activityMock), - activityStatisticsProvider('test-album', 'test-asset') - .overrideWith(() => activityStatisticsMock), - activityStatisticsProvider('test-album') - .overrideWith(() => albumActivityStatisticsMock), + activityStatisticsProvider('test-album', 'test-asset').overrideWith(() => activityStatisticsMock), + activityStatisticsProvider('test-album').overrideWith(() => albumActivityStatisticsMock), ], ); }); @@ -255,8 +186,7 @@ void main() { comment: 'Test-Comment', ), ).thenAnswer((_) async => AsyncData(comment)); - when(() => activityStatisticsMock.build('test-album', 'test-asset')) - .thenReturn(4); + when(() => activityStatisticsMock.build('test-album', 'test-asset')).thenReturn(4); when(() => albumActivityStatisticsMock.build('test-album')).thenReturn(2); await container.read(provider.notifier).addComment('Test-Comment'); @@ -289,26 +219,16 @@ void main() { ); when( - () => activityMock.addActivity( - 'test-album', - ActivityType.comment, - comment: 'Test-Comment', - ), + () => activityMock.addActivity('test-album', ActivityType.comment, comment: 'Test-Comment'), ).thenAnswer((_) async => AsyncData(comment)); when(() => albumActivityStatisticsMock.build('test-album')).thenReturn(2); - when(() => activityMock.getAllActivities('test-album')) - .thenAnswer((_) async => [..._activities]); + when(() => activityMock.getAllActivities('test-album')).thenAnswer((_) async => [..._activities]); final albumProvider = albumActivityProvider('test-album'); await container.read(albumProvider.notifier).addComment('Test-Comment'); verify( - () => activityMock.addActivity( - 'test-album', - ActivityType.comment, - assetId: null, - comment: 'Test-Comment', - ), + () => activityMock.addActivity('test-album', ActivityType.comment, assetId: null, comment: 'Test-Comment'), ); final activities = await container.read(albumProvider.future); @@ -336,9 +256,7 @@ void main() { assetId: 'test-asset', comment: 'Test-Comment', ), - ).thenAnswer( - (_) async => AsyncError(Exception('Error'), StackTrace.current), - ); + ).thenAnswer((_) async => AsyncError(Exception('Error'), StackTrace.current)); await container.read(provider.notifier).addComment('Test-Comment'); diff --git a/mobile/test/modules/activity/activity_statistics_provider_test.dart b/mobile/test/modules/activity/activity_statistics_provider_test.dart index 0216528ddd..7fe73868f5 100644 --- a/mobile/test/modules/activity/activity_statistics_provider_test.dart +++ b/mobile/test/modules/activity/activity_statistics_provider_test.dart @@ -15,11 +15,7 @@ void main() { setUp(() async { activityMock = ActivityServiceMock(); - container = TestUtils.createContainer( - overrides: [ - activityServiceProvider.overrideWith((ref) => activityMock), - ], - ); + container = TestUtils.createContainer(overrides: [activityServiceProvider.overrideWith((ref) => activityMock)]); listener = ListenerMock(); }); @@ -31,34 +27,21 @@ void main() { // Read here to make the getStatistics call container.read(activityStatisticsProvider('test-album', 'test-asset')); - container.listen( - activityStatisticsProvider('test-album', 'test-asset'), - listener.call, - fireImmediately: true, - ); + container.listen(activityStatisticsProvider('test-album', 'test-asset'), listener.call, fireImmediately: true); // Sleep for the getStatistics future to resolve await Future.delayed(const Duration(milliseconds: 1)); - verifyInOrder([ - () => listener.call(null, 0), - () => listener.call(0, 5), - ]); + verifyInOrder([() => listener.call(null, 0), () => listener.call(0, 5)]); verifyNoMoreInteractions(listener); }); test('Adds activity', () async { - when( - () => activityMock.getStatistics('test-album'), - ).thenAnswer((_) async => const ActivityStats(comments: 10)); + when(() => activityMock.getStatistics('test-album')).thenAnswer((_) async => const ActivityStats(comments: 10)); final provider = activityStatisticsProvider('test-album'); - container.listen( - provider, - listener.call, - fireImmediately: true, - ); + container.listen(provider, listener.call, fireImmediately: true); // Sleep for the getStatistics future to resolve await Future.delayed(const Duration(milliseconds: 1)); @@ -75,11 +58,7 @@ void main() { ).thenAnswer((_) async => const ActivityStats(comments: 10)); final provider = activityStatisticsProvider('new-album', 'test-asset'); - container.listen( - provider, - listener.call, - fireImmediately: true, - ); + container.listen(provider, listener.call, fireImmediately: true); // Sleep for the getStatistics future to resolve await Future.delayed(const Duration(milliseconds: 1)); diff --git a/mobile/test/modules/activity/activity_text_field_test.dart b/mobile/test/modules/activity/activity_text_field_test.dart index a124af0db9..1163330c54 100644 --- a/mobile/test/modules/activity/activity_text_field_test.dart +++ b/mobile/test/modules/activity/activity_text_field_test.dart @@ -44,18 +44,12 @@ void main() { activityMock = MockAlbumActivity(); overrides = [ currentAlbumProvider.overrideWith(() => mockCurrentAlbumProvider), - albumActivityProvider(AlbumStub.twoAsset.remoteId!) - .overrideWith(() => activityMock), + albumActivityProvider(AlbumStub.twoAsset.remoteId!).overrideWith(() => activityMock), ]; }); testWidgets('Returns an Input text field', (tester) async { - await tester.pumpConsumerWidget( - ActivityTextField( - onSubmit: (_) {}, - ), - overrides: overrides, - ); + await tester.pumpConsumerWidget(ActivityTextField(onSubmit: (_) {}), overrides: overrides); expect(find.byType(TextField), findsOneWidget); }); @@ -64,76 +58,38 @@ void main() { final userProvider = MockCurrentUserProvider(); await tester.pumpConsumerWidget( - ActivityTextField( - onSubmit: (_) {}, - ), - overrides: [ - currentUserProvider.overrideWith((ref) => userProvider), - ...overrides, - ], + ActivityTextField(onSubmit: (_) {}), + overrides: [currentUserProvider.overrideWith((ref) => userProvider), ...overrides], ); expect(find.byType(UserCircleAvatar), findsNothing); }); testWidgets('UserCircleAvatar displayed when user != null', (tester) async { - await tester.pumpConsumerWidget( - ActivityTextField( - onSubmit: (_) {}, - ), - overrides: overrides, - ); + await tester.pumpConsumerWidget(ActivityTextField(onSubmit: (_) {}), overrides: overrides); expect(find.byType(UserCircleAvatar), findsOneWidget); }); - testWidgets( - 'Filled icon if likedId != null', - (tester) async { - await tester.pumpConsumerWidget( - ActivityTextField( - onSubmit: (_) {}, - likeId: '1', - ), - overrides: overrides, - ); - - expect( - find.widgetWithIcon(IconButton, Icons.favorite_rounded), - findsOneWidget, - ); - expect( - find.widgetWithIcon(IconButton, Icons.favorite_border_rounded), - findsNothing, - ); - }, - ); - - testWidgets('Bordered icon if likedId == null', (tester) async { + testWidgets('Filled icon if likedId != null', (tester) async { await tester.pumpConsumerWidget( - ActivityTextField( - onSubmit: (_) {}, - ), + ActivityTextField(onSubmit: (_) {}, likeId: '1'), overrides: overrides, ); - expect( - find.widgetWithIcon(IconButton, Icons.favorite_border_rounded), - findsOneWidget, - ); - expect( - find.widgetWithIcon(IconButton, Icons.favorite_rounded), - findsNothing, - ); + expect(find.widgetWithIcon(IconButton, Icons.favorite_rounded), findsOneWidget); + expect(find.widgetWithIcon(IconButton, Icons.favorite_border_rounded), findsNothing); + }); + + testWidgets('Bordered icon if likedId == null', (tester) async { + await tester.pumpConsumerWidget(ActivityTextField(onSubmit: (_) {}), overrides: overrides); + + expect(find.widgetWithIcon(IconButton, Icons.favorite_border_rounded), findsOneWidget); + expect(find.widgetWithIcon(IconButton, Icons.favorite_rounded), findsNothing); }); testWidgets('Adds new like', (tester) async { - await tester.pumpConsumerWidget( - ActivityTextField( - onSubmit: (_) {}, - ), - overrides: overrides, - ); + await tester.pumpConsumerWidget(ActivityTextField(onSubmit: (_) {}), overrides: overrides); when(() => activityMock.addLike()).thenAnswer((_) => Future.value()); @@ -145,15 +101,11 @@ void main() { testWidgets('Removes like if already liked', (tester) async { await tester.pumpConsumerWidget( - ActivityTextField( - onSubmit: (_) {}, - likeId: 'test-suffix', - ), + ActivityTextField(onSubmit: (_) {}, likeId: 'test-suffix'), overrides: overrides, ); - when(() => activityMock.removeActivity(any())) - .thenAnswer((_) => Future.value()); + when(() => activityMock.removeActivity(any())).thenAnswer((_) => Future.value()); final suffixIcon = find.byType(IconButton); await tester.tap(suffixIcon); @@ -165,10 +117,7 @@ void main() { String? receivedText; await tester.pumpConsumerWidget( - ActivityTextField( - onSubmit: (text) => receivedText = text, - likeId: 'test-suffix', - ), + ActivityTextField(onSubmit: (text) => receivedText = text, likeId: 'test-suffix'), overrides: overrides, ); @@ -182,11 +131,7 @@ void main() { String? receviedText; await tester.pumpConsumerWidget( - ActivityTextField( - onSubmit: (text) => receviedText = text, - isEnabled: false, - likeId: 'test-suffix', - ), + ActivityTextField(onSubmit: (text) => receviedText = text, isEnabled: false, likeId: 'test-suffix'), overrides: overrides, ); diff --git a/mobile/test/modules/activity/activity_tile_test.dart b/mobile/test/modules/activity/activity_tile_test.dart index 22dd606540..eb4bb25848 100644 --- a/mobile/test/modules/activity/activity_tile_test.dart +++ b/mobile/test/modules/activity/activity_tile_test.dart @@ -43,31 +43,16 @@ void main() { testWidgets('Returns a ListTile', (tester) async { await tester.pumpConsumerWidget( - ActivityTile( - Activity( - id: '1', - createdAt: DateTime(100), - type: ActivityType.like, - user: UserStub.admin, - ), - ), + ActivityTile(Activity(id: '1', createdAt: DateTime(100), type: ActivityType.like, user: UserStub.admin)), overrides: overrides, ); expect(find.byType(ListTile), findsOneWidget); }); - testWidgets('No trailing widget when activity assetId == null', - (tester) async { + testWidgets('No trailing widget when activity assetId == null', (tester) async { await tester.pumpConsumerWidget( - ActivityTile( - Activity( - id: '1', - createdAt: DateTime(100), - type: ActivityType.like, - user: UserStub.admin, - ), - ), + ActivityTile(Activity(id: '1', createdAt: DateTime(100), type: ActivityType.like, user: UserStub.admin)), overrides: overrides, ); @@ -75,18 +60,10 @@ void main() { expect(listTile.trailing, isNull); }); - testWidgets( - 'Asset Thumbanil as trailing widget when activity assetId != null', - (tester) async { + testWidgets('Asset Thumbanil as trailing widget when activity assetId != null', (tester) async { await tester.pumpConsumerWidget( ActivityTile( - Activity( - id: '1', - createdAt: DateTime(100), - type: ActivityType.like, - user: UserStub.admin, - assetId: '1', - ), + Activity(id: '1', createdAt: DateTime(100), type: ActivityType.like, user: UserStub.admin, assetId: '1'), ), overrides: overrides, ); @@ -99,13 +76,7 @@ void main() { testWidgets('No trailing widget when current asset != null', (tester) async { await tester.pumpConsumerWidget( ActivityTile( - Activity( - id: '1', - createdAt: DateTime(100), - type: ActivityType.like, - user: UserStub.admin, - assetId: '1', - ), + Activity(id: '1', createdAt: DateTime(100), type: ActivityType.like, user: UserStub.admin, assetId: '1'), ), overrides: overrides, ); @@ -118,37 +89,23 @@ void main() { }); group('Like Activity', () { - final activity = Activity( - id: '1', - createdAt: DateTime(100), - type: ActivityType.like, - user: UserStub.admin, - ); + final activity = Activity(id: '1', createdAt: DateTime(100), type: ActivityType.like, user: UserStub.admin); testWidgets('Like contains filled heart as leading', (tester) async { - await tester.pumpConsumerWidget( - ActivityTile(activity), - overrides: overrides, - ); + await tester.pumpConsumerWidget(ActivityTile(activity), overrides: overrides); // Leading widget should not be null final listTile = tester.widget(find.byType(ListTile)); expect(listTile.leading, isNotNull); // And should have a favorite icon - final favoIconFinder = find.widgetWithIcon( - listTile.leading!.runtimeType, - Icons.favorite_rounded, - ); + final favoIconFinder = find.widgetWithIcon(listTile.leading!.runtimeType, Icons.favorite_rounded); expect(favoIconFinder, findsOneWidget); }); testWidgets('Like title is center aligned', (tester) async { - await tester.pumpConsumerWidget( - ActivityTile(activity), - overrides: overrides, - ); + await tester.pumpConsumerWidget(ActivityTile(activity), overrides: overrides); final listTile = tester.widget(find.byType(ListTile)); @@ -156,10 +113,7 @@ void main() { }); testWidgets('No subtitle for likes', (tester) async { - await tester.pumpConsumerWidget( - ActivityTile(activity), - overrides: overrides, - ); + await tester.pumpConsumerWidget(ActivityTile(activity), overrides: overrides); final listTile = tester.widget(find.byType(ListTile)); @@ -176,12 +130,8 @@ void main() { user: UserStub.admin, ); - testWidgets('Comment contains User Circle Avatar as leading', - (tester) async { - await tester.pumpConsumerWidget( - ActivityTile(activity), - overrides: overrides, - ); + testWidgets('Comment contains User Circle Avatar as leading', (tester) async { + await tester.pumpConsumerWidget(ActivityTile(activity), overrides: overrides); final userAvatarFinder = find.byType(UserCircleAvatar); expect(userAvatarFinder, findsOneWidget); @@ -196,10 +146,7 @@ void main() { }); testWidgets('Comment title is top aligned', (tester) async { - await tester.pumpConsumerWidget( - ActivityTile(activity), - overrides: overrides, - ); + await tester.pumpConsumerWidget(ActivityTile(activity), overrides: overrides); final listTile = tester.widget(find.byType(ListTile)); @@ -207,21 +154,12 @@ void main() { }); testWidgets('Contains comment text as subtitle', (tester) async { - await tester.pumpConsumerWidget( - ActivityTile(activity), - overrides: overrides, - ); + await tester.pumpConsumerWidget(ActivityTile(activity), overrides: overrides); final listTile = tester.widget(find.byType(ListTile)); expect(listTile.subtitle, isNotNull); - expect( - find.descendant( - of: find.byType(ListTile), - matching: find.text(activity.comment!), - ), - findsOneWidget, - ); + expect(find.descendant(of: find.byType(ListTile), matching: find.text(activity.comment!)), findsOneWidget); }); }); } diff --git a/mobile/test/modules/activity/dismissible_activity_test.dart b/mobile/test/modules/activity/dismissible_activity_test.dart index 7bfa400a37..e5f6258ee9 100644 --- a/mobile/test/modules/activity/dismissible_activity_test.dart +++ b/mobile/test/modules/activity/dismissible_activity_test.dart @@ -15,12 +15,7 @@ import '../../test_utils.dart'; import '../../widget_tester_extensions.dart'; import '../asset_viewer/asset_viewer_mocks.dart'; -final activity = Activity( - id: '1', - createdAt: DateTime(100), - type: ActivityType.like, - user: UserStub.admin, -); +final activity = Activity(id: '1', createdAt: DateTime(100), type: ActivityType.like, user: UserStub.admin); void main() { late MockCurrentAssetProvider assetProvider; @@ -34,10 +29,7 @@ void main() { }); testWidgets('Returns a Dismissible', (tester) async { - await tester.pumpConsumerWidget( - DismissibleActivity('1', ActivityTile(activity)), - overrides: overrides, - ); + await tester.pumpConsumerWidget(DismissibleActivity('1', ActivityTile(activity)), overrides: overrides); expect(find.byType(Dismissible), findsOneWidget); }); @@ -55,16 +47,10 @@ void main() { expect(find.byType(ConfirmDialog), findsOneWidget); }); - testWidgets( - 'Ok action in ConfirmDialog should call onDismiss with activityId', - (tester) async { + testWidgets('Ok action in ConfirmDialog should call onDismiss with activityId', (tester) async { String? receivedActivityId; await tester.pumpConsumerWidget( - DismissibleActivity( - '1', - ActivityTile(activity), - onDismiss: (id) => receivedActivityId = id, - ), + DismissibleActivity('1', ActivityTile(activity), onDismiss: (id) => receivedActivityId = id), overrides: overrides, ); @@ -93,10 +79,7 @@ void main() { }); testWidgets('No delete dialog if onDismiss is not set', (tester) async { - await tester.pumpConsumerWidget( - DismissibleActivity('1', ActivityTile(activity)), - overrides: overrides, - ); + await tester.pumpConsumerWidget(DismissibleActivity('1', ActivityTile(activity)), overrides: overrides); final dismissible = find.byType(Dismissible); await tester.drag(dismissible, const Offset(500, 0)); @@ -106,10 +89,7 @@ void main() { }); testWidgets('No icon for background if onDismiss is not set', (tester) async { - await tester.pumpConsumerWidget( - DismissibleActivity('1', ActivityTile(activity)), - overrides: overrides, - ); + await tester.pumpConsumerWidget(DismissibleActivity('1', ActivityTile(activity)), overrides: overrides); final dismissible = find.byType(Dismissible); await tester.drag(dismissible, const Offset(-500, 0)); diff --git a/mobile/test/modules/album/album_mocks.dart b/mobile/test/modules/album/album_mocks.dart index 147d7b4221..7a1b76e0c7 100644 --- a/mobile/test/modules/album/album_mocks.dart +++ b/mobile/test/modules/album/album_mocks.dart @@ -2,9 +2,7 @@ import 'package:immich_mobile/providers/album/current_album.provider.dart'; import 'package:immich_mobile/entities/album.entity.dart'; import 'package:mocktail/mocktail.dart'; -class MockCurrentAlbumProvider extends CurrentAlbum - with Mock - implements CurrentAlbumInternal { +class MockCurrentAlbumProvider extends CurrentAlbum with Mock implements CurrentAlbumInternal { Album? initAlbum; MockCurrentAlbumProvider([this.initAlbum]); diff --git a/mobile/test/modules/album/album_sort_by_options_provider_test.dart b/mobile/test/modules/album/album_sort_by_options_provider_test.dart index df59f03c56..a35255bc21 100644 --- a/mobile/test/modules/album/album_sort_by_options_provider_test.dart +++ b/mobile/test/modules/album/album_sort_by_options_provider_test.dart @@ -22,12 +22,7 @@ void main() { db = await TestUtils.initIsar(); }); - final albums = [ - AlbumStub.emptyAlbum, - AlbumStub.sharedWithUser, - AlbumStub.oneAsset, - AlbumStub.twoAsset, - ]; + final albums = [AlbumStub.emptyAlbum, AlbumStub.sharedWithUser, AlbumStub.oneAsset, AlbumStub.twoAsset]; setUp(() { db.writeTxnSync(() { @@ -48,23 +43,13 @@ void main() { const created = AlbumSortMode.created; test("Created time - ASC", () { final sorted = created.sortFn(albums, false); - final sortedList = [ - AlbumStub.emptyAlbum, - AlbumStub.twoAsset, - AlbumStub.oneAsset, - AlbumStub.sharedWithUser, - ]; + final sortedList = [AlbumStub.emptyAlbum, AlbumStub.twoAsset, AlbumStub.oneAsset, AlbumStub.sharedWithUser]; expect(sorted, orderedEquals(sortedList)); }); test("Created time - DESC", () { final sorted = created.sortFn(albums, true); - final sortedList = [ - AlbumStub.sharedWithUser, - AlbumStub.oneAsset, - AlbumStub.twoAsset, - AlbumStub.emptyAlbum, - ]; + final sortedList = [AlbumStub.sharedWithUser, AlbumStub.oneAsset, AlbumStub.twoAsset, AlbumStub.emptyAlbum]; expect(sorted, orderedEquals(sortedList)); }); }); @@ -73,23 +58,13 @@ void main() { const assetCount = AlbumSortMode.assetCount; test("Asset Count - ASC", () { final sorted = assetCount.sortFn(albums, false); - final sortedList = [ - AlbumStub.emptyAlbum, - AlbumStub.sharedWithUser, - AlbumStub.oneAsset, - AlbumStub.twoAsset, - ]; + final sortedList = [AlbumStub.emptyAlbum, AlbumStub.sharedWithUser, AlbumStub.oneAsset, AlbumStub.twoAsset]; expect(sorted, orderedEquals(sortedList)); }); test("Asset Count - DESC", () { final sorted = assetCount.sortFn(albums, true); - final sortedList = [ - AlbumStub.twoAsset, - AlbumStub.oneAsset, - AlbumStub.sharedWithUser, - AlbumStub.emptyAlbum, - ]; + final sortedList = [AlbumStub.twoAsset, AlbumStub.oneAsset, AlbumStub.sharedWithUser, AlbumStub.emptyAlbum]; expect(sorted, orderedEquals(sortedList)); }); }); @@ -98,23 +73,13 @@ void main() { const lastModified = AlbumSortMode.lastModified; test("Last modified - ASC", () { final sorted = lastModified.sortFn(albums, false); - final sortedList = [ - AlbumStub.twoAsset, - AlbumStub.emptyAlbum, - AlbumStub.sharedWithUser, - AlbumStub.oneAsset, - ]; + final sortedList = [AlbumStub.twoAsset, AlbumStub.emptyAlbum, AlbumStub.sharedWithUser, AlbumStub.oneAsset]; expect(sorted, orderedEquals(sortedList)); }); test("Last modified - DESC", () { final sorted = lastModified.sortFn(albums, true); - final sortedList = [ - AlbumStub.oneAsset, - AlbumStub.sharedWithUser, - AlbumStub.emptyAlbum, - AlbumStub.twoAsset, - ]; + final sortedList = [AlbumStub.oneAsset, AlbumStub.sharedWithUser, AlbumStub.emptyAlbum, AlbumStub.twoAsset]; expect(sorted, orderedEquals(sortedList)); }); }); @@ -123,23 +88,13 @@ void main() { const created = AlbumSortMode.created; test("Created - ASC", () { final sorted = created.sortFn(albums, false); - final sortedList = [ - AlbumStub.emptyAlbum, - AlbumStub.twoAsset, - AlbumStub.oneAsset, - AlbumStub.sharedWithUser, - ]; + final sortedList = [AlbumStub.emptyAlbum, AlbumStub.twoAsset, AlbumStub.oneAsset, AlbumStub.sharedWithUser]; expect(sorted, orderedEquals(sortedList)); }); test("Created - DESC", () { final sorted = created.sortFn(albums, true); - final sortedList = [ - AlbumStub.sharedWithUser, - AlbumStub.oneAsset, - AlbumStub.twoAsset, - AlbumStub.emptyAlbum, - ]; + final sortedList = [AlbumStub.sharedWithUser, AlbumStub.oneAsset, AlbumStub.twoAsset, AlbumStub.emptyAlbum]; expect(sorted, orderedEquals(sortedList)); }); }); @@ -148,15 +103,12 @@ void main() { const mostRecent = AlbumSortMode.mostRecent; test("Most Recent - DESC", () { - final sorted = mostRecent.sortFn( - [ - AlbumStub.create2020end2020Album, - AlbumStub.create2020end2022Album, - AlbumStub.create2020end2024Album, - AlbumStub.create2020end2026Album, - ], - false, - ); + final sorted = mostRecent.sortFn([ + AlbumStub.create2020end2020Album, + AlbumStub.create2020end2022Album, + AlbumStub.create2020end2024Album, + AlbumStub.create2020end2026Album, + ], false); final sortedList = [ AlbumStub.create2020end2026Album, AlbumStub.create2020end2024Album, @@ -167,15 +119,12 @@ void main() { }); test("Most Recent - ASC", () { - final sorted = mostRecent.sortFn( - [ - AlbumStub.create2020end2020Album, - AlbumStub.create2020end2022Album, - AlbumStub.create2020end2024Album, - AlbumStub.create2020end2026Album, - ], - true, - ); + final sorted = mostRecent.sortFn([ + AlbumStub.create2020end2020Album, + AlbumStub.create2020end2022Album, + AlbumStub.create2020end2024Album, + AlbumStub.create2020end2026Album, + ], true); final sortedList = [ AlbumStub.create2020end2020Album, AlbumStub.create2020end2022Album, @@ -191,23 +140,13 @@ void main() { test("Most Oldest - ASC", () { final sorted = mostOldest.sortFn(albums, false); - final sortedList = [ - AlbumStub.twoAsset, - AlbumStub.emptyAlbum, - AlbumStub.oneAsset, - AlbumStub.sharedWithUser, - ]; + final sortedList = [AlbumStub.twoAsset, AlbumStub.emptyAlbum, AlbumStub.oneAsset, AlbumStub.sharedWithUser]; expect(sorted, orderedEquals(sortedList)); }); test("Most Oldest - DESC", () { final sorted = mostOldest.sortFn(albums, true); - final sortedList = [ - AlbumStub.sharedWithUser, - AlbumStub.oneAsset, - AlbumStub.emptyAlbum, - AlbumStub.twoAsset, - ]; + final sortedList = [AlbumStub.sharedWithUser, AlbumStub.oneAsset, AlbumStub.emptyAlbum, AlbumStub.twoAsset]; expect(sorted, orderedEquals(sortedList)); }); }); @@ -221,79 +160,49 @@ void main() { setUp(() async { settingsMock = MockAppSettingsService(); container = TestUtils.createContainer( - overrides: [ - appSettingsServiceProvider.overrideWith((ref) => settingsMock), - ], + overrides: [appSettingsServiceProvider.overrideWith((ref) => settingsMock)], ); when( - () => settingsMock.setSetting( - AppSettingsEnum.selectedAlbumSortReverse, - any(), - ), + () => settingsMock.setSetting(AppSettingsEnum.selectedAlbumSortReverse, any()), ).thenAnswer((_) async => {}); when( - () => settingsMock.setSetting( - AppSettingsEnum.selectedAlbumSortOrder, - any(), - ), + () => settingsMock.setSetting(AppSettingsEnum.selectedAlbumSortOrder, any()), ).thenAnswer((_) async => {}); }); test('Returns the default sort mode when none set', () { // Returns the default value when nothing is set - when( - () => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortOrder), - ).thenReturn(0); + when(() => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortOrder)).thenReturn(0); expect(container.read(albumSortByOptionsProvider), AlbumSortMode.created); }); test('Returns the correct sort mode with index from Store', () { // Returns the default value when nothing is set - when( - () => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortOrder), - ).thenReturn(3); + when(() => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortOrder)).thenReturn(3); - expect( - container.read(albumSortByOptionsProvider), - AlbumSortMode.lastModified, - ); + expect(container.read(albumSortByOptionsProvider), AlbumSortMode.lastModified); }); test('Properly saves the correct store index of sort mode', () { - container - .read(albumSortByOptionsProvider.notifier) - .changeSortMode(AlbumSortMode.mostOldest); + container.read(albumSortByOptionsProvider.notifier).changeSortMode(AlbumSortMode.mostOldest); verify( - () => settingsMock.setSetting( - AppSettingsEnum.selectedAlbumSortOrder, - AlbumSortMode.mostOldest.storeIndex, - ), + () => settingsMock.setSetting(AppSettingsEnum.selectedAlbumSortOrder, AlbumSortMode.mostOldest.storeIndex), ); }); test('Notifies listeners on state change', () { - when( - () => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortOrder), - ).thenReturn(0); + when(() => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortOrder)).thenReturn(0); final listener = ListenerMock(); - container.listen( - albumSortByOptionsProvider, - listener.call, - fireImmediately: true, - ); + container.listen(albumSortByOptionsProvider, listener.call, fireImmediately: true); // Created -> Most Oldest - container - .read(albumSortByOptionsProvider.notifier) - .changeSortMode(AlbumSortMode.mostOldest); + container.read(albumSortByOptionsProvider.notifier).changeSortMode(AlbumSortMode.mostOldest); // Most Oldest -> Title - container - .read(albumSortByOptionsProvider.notifier) - .changeSortMode(AlbumSortMode.title); + container.read(albumSortByOptionsProvider.notifier).changeSortMode(AlbumSortMode.title); verifyInOrder([ () => listener.call(null, AlbumSortMode.created), @@ -315,28 +224,18 @@ void main() { setUp(() async { settingsMock = MockAppSettingsService(); container = TestUtils.createContainer( - overrides: [ - appSettingsServiceProvider.overrideWith((ref) => settingsMock), - ], + overrides: [appSettingsServiceProvider.overrideWith((ref) => settingsMock)], ); when( - () => settingsMock.setSetting( - AppSettingsEnum.selectedAlbumSortReverse, - any(), - ), + () => settingsMock.setSetting(AppSettingsEnum.selectedAlbumSortReverse, any()), ).thenAnswer((_) async => {}); when( - () => settingsMock.setSetting( - AppSettingsEnum.selectedAlbumSortOrder, - any(), - ), + () => settingsMock.setSetting(AppSettingsEnum.selectedAlbumSortOrder, any()), ).thenAnswer((_) async => {}); }); test('Returns the default sort order when none set - false', () { - when( - () => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortReverse), - ).thenReturn(false); + when(() => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortReverse)).thenReturn(false); expect(container.read(albumSortOrderProvider), isFalse); }); @@ -344,33 +243,20 @@ void main() { test('Properly saves the correct order', () { container.read(albumSortOrderProvider.notifier).changeSortDirection(true); - verify( - () => settingsMock.setSetting( - AppSettingsEnum.selectedAlbumSortReverse, - true, - ), - ); + verify(() => settingsMock.setSetting(AppSettingsEnum.selectedAlbumSortReverse, true)); }); test('Notifies listeners on state change', () { - when( - () => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortReverse), - ).thenReturn(false); + when(() => settingsMock.getSetting(AppSettingsEnum.selectedAlbumSortReverse)).thenReturn(false); final listener = ListenerMock(); - container.listen( - albumSortOrderProvider, - listener.call, - fireImmediately: true, - ); + container.listen(albumSortOrderProvider, listener.call, fireImmediately: true); // false -> true container.read(albumSortOrderProvider.notifier).changeSortDirection(true); // true -> false - container - .read(albumSortOrderProvider.notifier) - .changeSortDirection(false); + container.read(albumSortOrderProvider.notifier).changeSortDirection(false); verifyInOrder([ () => listener.call(null, false), diff --git a/mobile/test/modules/asset_viewer/asset_viewer_mocks.dart b/mobile/test/modules/asset_viewer/asset_viewer_mocks.dart index f81f5a9a19..89b06d3c09 100644 --- a/mobile/test/modules/asset_viewer/asset_viewer_mocks.dart +++ b/mobile/test/modules/asset_viewer/asset_viewer_mocks.dart @@ -2,9 +2,7 @@ import 'package:immich_mobile/providers/asset_viewer/current_asset.provider.dart import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:mocktail/mocktail.dart'; -class MockCurrentAssetProvider extends CurrentAssetInternal - with Mock - implements CurrentAsset { +class MockCurrentAssetProvider extends CurrentAssetInternal with Mock implements CurrentAsset { Asset? initAsset; MockCurrentAssetProvider([this.initAsset]); diff --git a/mobile/test/modules/extensions/asset_extensions_test.dart b/mobile/test/modules/extensions/asset_extensions_test.dart index dd334c7b9d..2b9b740ca7 100644 --- a/mobile/test/modules/extensions/asset_extensions_test.dart +++ b/mobile/test/modules/extensions/asset_extensions_test.dart @@ -5,21 +5,11 @@ import 'package:immich_mobile/extensions/asset_extensions.dart'; import 'package:timezone/data/latest.dart'; import 'package:timezone/timezone.dart'; -ExifInfo makeExif({ - DateTime? dateTimeOriginal, - String? timeZone, -}) { - return ExifInfo( - dateTimeOriginal: dateTimeOriginal, - timeZone: timeZone, - ); +ExifInfo makeExif({DateTime? dateTimeOriginal, String? timeZone}) { + return ExifInfo(dateTimeOriginal: dateTimeOriginal, timeZone: timeZone); } -Asset makeAsset({ - required String id, - required DateTime createdAt, - ExifInfo? exifInfo, -}) { +Asset makeAsset({required String id, required DateTime createdAt, ExifInfo? exifInfo}) { return Asset( checksum: '', localId: id, @@ -76,14 +66,10 @@ void main() { expect(dateTimeInUTC.timeZoneOffset, tz); }); - test('Returns dateTimeOriginal in UTC from exifInfo with invalid timezone', - () { + test('Returns dateTimeOriginal in UTC from exifInfo with invalid timezone', () { final createdAt = DateTime.parse("2023-01-27T14:00:00-0500"); final dateTimeOriginal = DateTime.parse("2022-01-27T14:00:00+0530"); - final e = makeExif( - dateTimeOriginal: dateTimeOriginal, - timeZone: "#_#", - ); // Invalid timezone + final e = makeExif(dateTimeOriginal: dateTimeOriginal, timeZone: "#_#"); // Invalid timezone final a = makeAsset(id: '1', createdAt: createdAt, exifInfo: e); final (dt, tz) = a.getTZAdjustedTimeAndOffset(); @@ -98,13 +84,11 @@ void main() { final createdAt = DateTime.parse("2023-01-27T14:00:00-0500"); final dateTimeOriginal = DateTime.parse("2022-01-27T14:00:00+0530"); const location = "Asia/Hong_Kong"; - final e = - makeExif(dateTimeOriginal: dateTimeOriginal, timeZone: location); + final e = makeExif(dateTimeOriginal: dateTimeOriginal, timeZone: location); final a = makeAsset(id: '1', createdAt: createdAt, exifInfo: e); final (dt, tz) = a.getTZAdjustedTimeAndOffset(); - final adjustedTime = - TZDateTime.from(dateTimeOriginal.toUtc(), getLocation(location)); + final adjustedTime = TZDateTime.from(dateTimeOriginal.toUtc(), getLocation(location)); expect(adjustedTime, dt); expect(adjustedTime.timeZoneOffset, tz); }); @@ -118,8 +102,7 @@ void main() { final (dt, tz) = a.getTZAdjustedTimeAndOffset(); final location = getLocation("Asia/Hong_Kong"); - final offsetFromLocation = - Duration(milliseconds: location.currentTimeZone.offset); + final offsetFromLocation = Duration(milliseconds: location.currentTimeZone.offset); final adjustedTime = dateTimeOriginal.toUtc().add(offsetFromLocation); // Adds the offset to the actual time and returns the offset separately diff --git a/mobile/test/modules/extensions/builtin_extensions_test.dart b/mobile/test/modules/extensions/builtin_extensions_test.dart index 2de450a952..e52362f3d3 100644 --- a/mobile/test/modules/extensions/builtin_extensions_test.dart +++ b/mobile/test/modules/extensions/builtin_extensions_test.dart @@ -5,10 +5,7 @@ import 'package:immich_mobile/extensions/string_extensions.dart'; void main() { group('Test toDuration', () { test('ok', () { - expect( - "1:02:33".toDuration(), - const Duration(hours: 1, minutes: 2, seconds: 33), - ); + expect("1:02:33".toDuration(), const Duration(hours: 1, minutes: 2, seconds: 33)); }); test('malformed', () { expect("".toDuration(), isNull); @@ -45,9 +42,7 @@ void main() { test('withKey', () { final a = ["a", "bb", "cc", "ddd"]; expect( - a.uniqueConsecutive( - compare: (s1, s2) => s1.length.compareTo(s2.length), - ), + a.uniqueConsecutive(compare: (s1, s2) => s1.length.compareTo(s2.length)), orderedEquals(["a", "bb", "ddd"]), ); }); diff --git a/mobile/test/modules/extensions/datetime_extensions_test.dart b/mobile/test/modules/extensions/datetime_extensions_test.dart index 412d946b1f..b1b1542fcd 100644 --- a/mobile/test/modules/extensions/datetime_extensions_test.dart +++ b/mobile/test/modules/extensions/datetime_extensions_test.dart @@ -25,24 +25,21 @@ void main() { test('returns date range format for this year', () { final startDate = DateTime(currentYear, 3, 23); // Mar 23 final endDate = DateTime(currentYear, 5, 31); // May 31 - final result = - DateRangeFormatting.formatDateRange(startDate, endDate, null); + final result = DateRangeFormatting.formatDateRange(startDate, endDate, null); expect(result, 'Mar 23 - May 31'); }); test('returns date range format for other year (same year)', () { final startDate = DateTime(2023, 8, 28); // Aug 28 final endDate = DateTime(2023, 9, 30); // Sep 30 - final result = - DateRangeFormatting.formatDateRange(startDate, endDate, null); + final result = DateRangeFormatting.formatDateRange(startDate, endDate, null); expect(result, 'Aug 28 - Sep 30, 2023'); }); test('returns date range format over multiple years', () { final startDate = DateTime(2021, 4, 17); // Apr 17, 2021 final endDate = DateTime(2022, 4, 9); // Apr 9, 2022 - final result = - DateRangeFormatting.formatDateRange(startDate, endDate, null); + final result = DateRangeFormatting.formatDateRange(startDate, endDate, null); expect(result, 'Apr 17, 2021 - Apr 9, 2022'); }); }); diff --git a/mobile/test/modules/home/asset_grid_data_structure_test.dart b/mobile/test/modules/home/asset_grid_data_structure_test.dart index b4ee851969..3e1fe06c68 100644 --- a/mobile/test/modules/home/asset_grid_data_structure_test.dart +++ b/mobile/test/modules/home/asset_grid_data_structure_test.dart @@ -58,10 +58,7 @@ void main() { group('Test grouped', () { test('test grouped check months', () async { - final renderList = await RenderList.fromAssets( - assets, - GroupAssetsBy.day, - ); + final renderList = await RenderList.fromAssets(assets, GroupAssetsBy.day); // Oct // Day 1 @@ -75,33 +72,18 @@ void main() { // Day 1 // 5 Assets => 2 Rows expect(renderList.elements, hasLength(4)); - expect( - renderList.elements[0].type, - RenderAssetGridElementType.monthTitle, - ); + expect(renderList.elements[0].type, RenderAssetGridElementType.monthTitle); expect(renderList.elements[0].date.month, 1); - expect( - renderList.elements[1].type, - RenderAssetGridElementType.groupDividerTitle, - ); + expect(renderList.elements[1].type, RenderAssetGridElementType.groupDividerTitle); expect(renderList.elements[1].date.month, 1); - expect( - renderList.elements[2].type, - RenderAssetGridElementType.monthTitle, - ); + expect(renderList.elements[2].type, RenderAssetGridElementType.monthTitle); expect(renderList.elements[2].date.month, 2); - expect( - renderList.elements[3].type, - RenderAssetGridElementType.monthTitle, - ); + expect(renderList.elements[3].type, RenderAssetGridElementType.monthTitle); expect(renderList.elements[3].date.month, 10); }); test('test grouped check types', () async { - final renderList = await RenderList.fromAssets( - assets, - GroupAssetsBy.day, - ); + final renderList = await RenderList.fromAssets(assets, GroupAssetsBy.day); // Oct // Day 1 diff --git a/mobile/test/modules/map/map_mocks.dart b/mobile/test/modules/map/map_mocks.dart index cb525b2d17..959cad3da6 100644 --- a/mobile/test/modules/map/map_mocks.dart +++ b/mobile/test/modules/map/map_mocks.dart @@ -3,9 +3,7 @@ import 'package:immich_mobile/models/map/map_state.model.dart'; import 'package:immich_mobile/providers/map/map_state.provider.dart'; import 'package:mocktail/mocktail.dart'; -class MockMapStateNotifier extends Notifier - with Mock - implements MapStateNotifier { +class MockMapStateNotifier extends Notifier with Mock implements MapStateNotifier { final MapState initState; MockMapStateNotifier(this.initState); diff --git a/mobile/test/modules/map/map_theme_override_test.dart b/mobile/test/modules/map/map_theme_override_test.dart index 59f9b9e246..de16b7f24f 100644 --- a/mobile/test/modules/map/map_theme_override_test.dart +++ b/mobile/test/modules/map/map_theme_override_test.dart @@ -38,8 +38,7 @@ void main() { ]; }); - testWidgets("Return dark theme style when theme mode is dark", - (tester) async { + testWidgets("Return dark theme style when theme mode is dark", (tester) async { AsyncValue? mapStyle; await tester.pumpConsumerWidget( MapThemeOverride( @@ -51,8 +50,7 @@ void main() { overrides: overrides, ); - mapStateNotifier.state = - mapState.copyWith(darkStyleFetched: const AsyncData("dark")); + mapStateNotifier.state = mapState.copyWith(darkStyleFetched: const AsyncData("dark")); await tester.pumpAndSettle(); expect(mapStyle?.valueOrNull, "dark"); }); @@ -69,15 +67,12 @@ void main() { overrides: overrides, ); - mapStateNotifier.state = mapState.copyWith( - darkStyleFetched: const AsyncError("Error", StackTrace.empty), - ); + mapStateNotifier.state = mapState.copyWith(darkStyleFetched: const AsyncError("Error", StackTrace.empty)); await tester.pumpAndSettle(); expect(mapStyle?.hasError, isTrue); }); - testWidgets("Return light theme style when theme mode is light", - (tester) async { + testWidgets("Return light theme style when theme mode is light", (tester) async { AsyncValue? mapStyle; await tester.pumpConsumerWidget( MapThemeOverride( @@ -89,10 +84,7 @@ void main() { overrides: overrides, ); - mapStateNotifier.state = mapState.copyWith( - themeMode: ThemeMode.light, - lightStyleFetched: const AsyncData("light"), - ); + mapStateNotifier.state = mapState.copyWith(themeMode: ThemeMode.light, lightStyleFetched: const AsyncData("light")); await tester.pumpAndSettle(); expect(mapStyle?.valueOrNull, "light"); }); @@ -110,8 +102,7 @@ void main() { overrides: overrides, ); - tester.binding.platformDispatcher.platformBrightnessTestValue = - Brightness.dark; + tester.binding.platformDispatcher.platformBrightnessTestValue = Brightness.dark; mapStateNotifier.state = mapState.copyWith( themeMode: ThemeMode.system, darkStyleFetched: const AsyncData("dark"), @@ -121,8 +112,7 @@ void main() { expect(mapStyle?.valueOrNull, "dark"); }); - testWidgets("Return light theme style when system is light", - (tester) async { + testWidgets("Return light theme style when system is light", (tester) async { AsyncValue? mapStyle; await tester.pumpConsumerWidget( MapThemeOverride( @@ -134,8 +124,7 @@ void main() { overrides: overrides, ); - tester.binding.platformDispatcher.platformBrightnessTestValue = - Brightness.light; + tester.binding.platformDispatcher.platformBrightnessTestValue = Brightness.light; mapStateNotifier.state = mapState.copyWith( themeMode: ThemeMode.system, lightStyleFetched: const AsyncData("light"), @@ -145,8 +134,7 @@ void main() { expect(mapStyle?.valueOrNull, "light"); }); - testWidgets("Switches style when system brightness changes", - (tester) async { + testWidgets("Switches style when system brightness changes", (tester) async { AsyncValue? mapStyle; await tester.pumpConsumerWidget( MapThemeOverride( @@ -158,8 +146,7 @@ void main() { overrides: overrides, ); - tester.binding.platformDispatcher.platformBrightnessTestValue = - Brightness.light; + tester.binding.platformDispatcher.platformBrightnessTestValue = Brightness.light; mapStateNotifier.state = mapState.copyWith( themeMode: ThemeMode.system, lightStyleFetched: const AsyncData("light"), @@ -168,8 +155,7 @@ void main() { await tester.pumpAndSettle(); expect(mapStyle?.valueOrNull, "light"); - tester.binding.platformDispatcher.platformBrightnessTestValue = - Brightness.dark; + tester.binding.platformDispatcher.platformBrightnessTestValue = Brightness.dark; await tester.pumpAndSettle(); expect(mapStyle?.valueOrNull, "dark"); }); diff --git a/mobile/test/modules/shared/shared_mocks.dart b/mobile/test/modules/shared/shared_mocks.dart index f50fde7040..790bbbd815 100644 --- a/mobile/test/modules/shared/shared_mocks.dart +++ b/mobile/test/modules/shared/shared_mocks.dart @@ -3,9 +3,7 @@ import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:mocktail/mocktail.dart'; -class MockCurrentUserProvider extends StateNotifier - with Mock - implements CurrentUserProvider { +class MockCurrentUserProvider extends StateNotifier with Mock implements CurrentUserProvider { MockCurrentUserProvider() : super(null); @override diff --git a/mobile/test/modules/shared/sync_service_test.dart b/mobile/test/modules/shared/sync_service_test.dart index a78f65af67..22fd3cacfc 100644 --- a/mobile/test/modules/shared/sync_service_test.dart +++ b/mobile/test/modules/shared/sync_service_test.dart @@ -57,14 +57,11 @@ void main() { final MockExifInfoRepository exifInfoRepository = MockExifInfoRepository(); final MockIsarUserRepository userRepository = MockIsarUserRepository(); final MockETagRepository eTagRepository = MockETagRepository(); - final MockAlbumMediaRepository albumMediaRepository = - MockAlbumMediaRepository(); + final MockAlbumMediaRepository albumMediaRepository = MockAlbumMediaRepository(); final MockAlbumApiRepository albumApiRepository = MockAlbumApiRepository(); final MockAppSettingService appSettingService = MockAppSettingService(); - final MockLocalFilesManagerRepository localFilesManagerRepository = - MockLocalFilesManagerRepository(); - final MockPartnerApiRepository partnerApiRepository = - MockPartnerApiRepository(); + final MockLocalFilesManagerRepository localFilesManagerRepository = MockLocalFilesManagerRepository(); + final MockPartnerApiRepository partnerApiRepository = MockPartnerApiRepository(); final MockUserApiRepository userApiRepository = MockUserApiRepository(); final MockPartnerRepository partnerRepository = MockPartnerRepository(); final MockUserService userService = MockUserService(); @@ -75,6 +72,7 @@ void main() { email: "a@b.c", name: "first last", isAdmin: false, + profileChangedAt: DateTime(2021), ); late SyncService s; setUpAll(() async { @@ -84,10 +82,7 @@ void main() { db.writeTxnSync(() => db.clearSync()); await StoreService.init(storeRepository: IsarStoreRepository(db)); await Store.put(StoreKey.currentUser, owner); - await LogService.init( - logRepository: IsarLogRepository(db), - storeRepository: IsarStoreRepository(db), - ); + await LogService.init(logRepository: IsarLogRepository(db), storeRepository: IsarStoreRepository(db)); }); final List initialAssets = [ makeAsset(checksum: "a", remoteId: "0-1"), @@ -115,36 +110,30 @@ void main() { userApiRepository, ); when(() => userService.getMyUser()).thenReturn(owner); - when(() => eTagRepository.get(owner.id)) - .thenAnswer((_) async => ETag(id: owner.id, time: DateTime.now())); + when(() => eTagRepository.get(owner.id)).thenAnswer((_) async => ETag(id: owner.id, time: DateTime.now())); when(() => eTagRepository.deleteByIds(["1"])).thenAnswer((_) async {}); when(() => eTagRepository.upsertAll(any())).thenAnswer((_) async {}); when(() => partnerRepository.getSharedWith()).thenAnswer((_) async => []); - when(() => userRepository.getAll(sortBy: SortUserBy.id)) - .thenAnswer((_) async => [owner]); + when(() => userRepository.getAll(sortBy: SortUserBy.id)).thenAnswer((_) async => [owner]); when(() => userRepository.getAll()).thenAnswer((_) async => [owner]); when( - () => assetRepository.getAll( - ownerId: owner.id, - sortBy: AssetSort.checksum, - ), + () => assetRepository.getAll(ownerId: owner.id, sortBy: AssetSort.checksum), ).thenAnswer((_) async => initialAssets); - when(() => assetRepository.getAllByOwnerIdChecksum(any(), any())) - .thenAnswer((_) async => [initialAssets[3], null, null]); + when( + () => assetRepository.getAllByOwnerIdChecksum(any(), any()), + ).thenAnswer((_) async => [initialAssets[3], null, null]); when(() => assetRepository.updateAll(any())).thenAnswer((_) async => []); when(() => assetRepository.deleteByIds(any())).thenAnswer((_) async {}); - when(() => exifInfoRepository.updateAll(any())) - .thenAnswer((_) async => []); - when(() => assetRepository.transaction(any())).thenAnswer( - (call) => (call.positionalArguments.first as Function).call(), - ); - when(() => assetRepository.transaction(any())).thenAnswer( - (call) => (call.positionalArguments.first as Function).call(), - ); + when(() => exifInfoRepository.updateAll(any())).thenAnswer((_) async => []); + when( + () => assetRepository.transaction(any()), + ).thenAnswer((call) => (call.positionalArguments.first as Function).call()); + when( + () => assetRepository.transaction(any()), + ).thenAnswer((call) => (call.positionalArguments.first as Function).call()); when(() => userApiRepository.getAll()).thenAnswer((_) async => [owner]); registerFallbackValue(Direction.sharedByMe); - when(() => partnerApiRepository.getAll(any())) - .thenAnswer((_) async => []); + when(() => partnerApiRepository.getAll(any())).thenAnswer((_) async => []); }); test('test inserting existing assets', () async { final List remoteAssets = [ @@ -177,10 +166,7 @@ void main() { ); expect(c1, isTrue); final updatedAsset = initialAssets[3].updatedCopy(remoteAssets[3]); - verify( - () => assetRepository - .updateAll([remoteAssets[4], remoteAssets[5], updatedAsset]), - ); + verify(() => assetRepository.updateAll([remoteAssets[4], remoteAssets[5], updatedAsset])); }); test('test syncing duplicate assets', () async { @@ -199,10 +185,7 @@ void main() { ); expect(c1, isTrue); when( - () => assetRepository.getAll( - ownerId: owner.id, - sortBy: AssetSort.checksum, - ), + () => assetRepository.getAll(ownerId: owner.id, sortBy: AssetSort.checksum), ).thenAnswer((_) async => remoteAssets); final bool c2 = await s.syncRemoteAssetsToDb( users: [owner], @@ -212,10 +195,7 @@ void main() { expect(c2, isFalse); final currentState = [...remoteAssets]; when( - () => assetRepository.getAll( - ownerId: owner.id, - sortBy: AssetSort.checksum, - ), + () => assetRepository.getAll(ownerId: owner.id, sortBy: AssetSort.checksum), ).thenAnswer((_) async => currentState); remoteAssets.removeAt(4); final bool c3 = await s.syncRemoteAssetsToDb( @@ -236,19 +216,19 @@ void main() { test('test efficient sync', () async { when( - () => assetRepository.deleteAllByRemoteId( - [initialAssets[1].remoteId!, initialAssets[2].remoteId!], - state: AssetState.remote, - ), + () => assetRepository.deleteAllByRemoteId([ + initialAssets[1].remoteId!, + initialAssets[2].remoteId!, + ], state: AssetState.remote), ).thenAnswer((_) async { return; }); when( - () => assetRepository - .getAllByRemoteId(["2-1", "1-1"], state: AssetState.merged), + () => assetRepository.getAllByRemoteId(["2-1", "1-1"], state: AssetState.merged), ).thenAnswer((_) async => [initialAssets[2]]); - when(() => assetRepository.getAllByOwnerIdChecksum(any(), any())) - .thenAnswer((_) async => [initialAssets[0], null, null]); //afg + when( + () => assetRepository.getAllByOwnerIdChecksum(any(), any()), + ).thenAnswer((_) async => [initialAssets[0], null, null]); //afg final List toUpsert = [ makeAsset(checksum: "a", remoteId: "0-1"), // changed makeAsset(checksum: "f", remoteId: "0-2"), // new @@ -271,18 +251,11 @@ void main() { test('test upsert with EXIF data', () async { final assets = [AssetStub.image1, AssetStub.image2]; - expect( - assets.map((a) => a.exifInfo?.assetId), - List.filled(assets.length, null), - ); + expect(assets.map((a) => a.exifInfo?.assetId), List.filled(assets.length, null)); await s.upsertAssetsWithExif(assets); verify( () => exifInfoRepository.updateAll( - any( - that: containsAll( - assets.map((a) => a.exifInfo!.copyWith(assetId: a.id)), - ), - ), + any(that: containsAll(assets.map((a) => a.exifInfo!.copyWith(assetId: a.id)))), ), ); expect(assets.map((a) => a.exifInfo?.assetId), assets.map((a) => a.id)); @@ -291,8 +264,4 @@ void main() { }); } -Future<(List?, List?)> _failDiff( - List user, - DateTime time, -) => - Future.value((null, null)); +Future<(List?, List?)> _failDiff(List user, DateTime time) => Future.value((null, null)); diff --git a/mobile/test/modules/utils/async_mutex_test.dart b/mobile/test/modules/utils/async_mutex_test.dart index c1b39035b4..d50567721b 100644 --- a/mobile/test/modules/utils/async_mutex_test.dart +++ b/mobile/test/modules/utils/async_mutex_test.dart @@ -7,33 +7,13 @@ void main() { AsyncMutex lock = AsyncMutex(); List events = []; expect(0, lock.enqueued); - lock.run( - () => Future.delayed( - const Duration(milliseconds: 10), - () => events.add(1), - ), - ); + lock.run(() => Future.delayed(const Duration(milliseconds: 10), () => events.add(1))); expect(1, lock.enqueued); - lock.run( - () => Future.delayed( - const Duration(milliseconds: 3), - () => events.add(2), - ), - ); + lock.run(() => Future.delayed(const Duration(milliseconds: 3), () => events.add(2))); expect(2, lock.enqueued); - lock.run( - () => Future.delayed( - const Duration(milliseconds: 1), - () => events.add(3), - ), - ); + lock.run(() => Future.delayed(const Duration(milliseconds: 1), () => events.add(3))); expect(3, lock.enqueued); - await lock.run( - () => Future.delayed( - const Duration(milliseconds: 10), - () => events.add(4), - ), - ); + await lock.run(() => Future.delayed(const Duration(milliseconds: 10), () => events.add(4))); expect(0, lock.enqueued); expect(events, [1, 2, 3, 4]); }); diff --git a/mobile/test/modules/utils/throttler_test.dart b/mobile/test/modules/utils/throttler_test.dart index 76d8bd2ad7..ba5d542fe6 100644 --- a/mobile/test/modules/utils/throttler_test.dart +++ b/mobile/test/modules/utils/throttler_test.dart @@ -14,8 +14,7 @@ class _Counter { } void main() { - test('Executes the method immediately if no calls received previously', - () async { + test('Executes the method immediately if no calls received previously', () async { var counter = _Counter(); final throttler = Throttler(interval: const Duration(milliseconds: 300)); throttler.run(() => counter.increment()); diff --git a/mobile/test/modules/utils/thumbnail_utils_test.dart b/mobile/test/modules/utils/thumbnail_utils_test.dart index 6fa0127f16..dd4588fc80 100644 --- a/mobile/test/modules/utils/thumbnail_utils_test.dart +++ b/mobile/test/modules/utils/thumbnail_utils_test.dart @@ -9,22 +9,12 @@ void main() { final dateTimeString = DateFormat.yMMMMd().format(dateTime); test('returns description if it has one', () { - final result = getAltText( - const ExifInfo(description: 'description'), - dateTime, - AssetType.image, - [], - ); + final result = getAltText(const ExifInfo(description: 'description'), dateTime, AssetType.image, []); expect(result, 'description'); }); test('returns image alt text with date if no location', () { - final (template, args) = getAltTextTemplate( - const ExifInfo(), - dateTime, - AssetType.image, - [], - ); + final (template, args) = getAltTextTemplate(const ExifInfo(), dateTime, AssetType.image, []); expect(template, "image_alt_text_date"); expect(args["isVideo"], "false"); expect(args["date"], dateTimeString); @@ -45,12 +35,7 @@ void main() { }); test('returns image alt text with date and some people', () { - final (template, args) = getAltTextTemplate( - const ExifInfo(), - dateTime, - AssetType.image, - ["Alice", "Bob"], - ); + final (template, args) = getAltTextTemplate(const ExifInfo(), dateTime, AssetType.image, ["Alice", "Bob"]); expect(template, "image_alt_text_date_2_people"); expect(args["isVideo"], "false"); expect(args["date"], dateTimeString); diff --git a/mobile/test/modules/utils/url_helper_test.dart b/mobile/test/modules/utils/url_helper_test.dart index 840ac91f1f..0e8a8e2aa0 100644 --- a/mobile/test/modules/utils/url_helper_test.dart +++ b/mobile/test/modules/utils/url_helper_test.dart @@ -28,9 +28,7 @@ void main() { expect(punycodeEncodeUrl(url), equals(expected)); }); - test( - 'should encode multi-segment Unicode host with multiple non-ASCII segments', - () { + test('should encode multi-segment Unicode host with multiple non-ASCII segments', () { const url = 'https://bücher.münchen'; const expected = 'https://xn--bcher-kva.xn--mnchen-3ya'; expect(punycodeEncodeUrl(url), equals(expected)); diff --git a/mobile/test/modules/utils/version_compatibility_test.dart b/mobile/test/modules/utils/version_compatibility_test.dart index bdcc0c8fce..82f89237c0 100644 --- a/mobile/test/modules/utils/version_compatibility_test.dart +++ b/mobile/test/modules/utils/version_compatibility_test.dart @@ -6,10 +6,7 @@ void main() { String? result; result = getVersionCompatibilityMessage(1, 0, 2, 0); - expect( - result, - 'Your app major version is not compatible with the server!', - ); + expect(result, 'Your app major version is not compatible with the server!'); result = getVersionCompatibilityMessage(1, 106, 1, 105); expect( diff --git a/mobile/test/pages/search/search.page_test.dart b/mobile/test/pages/search/search.page_test.dart index fa7f037da5..9592623a28 100644 --- a/mobile/test/pages/search/search.page_test.dart +++ b/mobile/test/pages/search/search.page_test.dart @@ -42,22 +42,14 @@ void main() { ]; }); - final emptyTextSearch = isA() - .having((s) => s.originalFileName, 'originalFileName', null); + final emptyTextSearch = isA().having((s) => s.originalFileName, 'originalFileName', null); testWidgets('contextual search with/without text', (tester) async { - await tester.pumpConsumerWidget( - const SearchPage(), - overrides: overrides, - ); + await tester.pumpConsumerWidget(const SearchPage(), overrides: overrides); await tester.pumpAndSettle(); - expect( - find.byIcon(Icons.abc_rounded), - findsOneWidget, - reason: 'Should have contextual search icon', - ); + expect(find.byIcon(Icons.abc_rounded), findsOneWidget, reason: 'Should have contextual search icon'); final searchField = find.byKey(const Key('search_text_field')); expect(searchField, findsOneWidget); @@ -65,14 +57,9 @@ void main() { await tester.enterText(searchField, 'test'); await tester.testTextInput.receiveAction(TextInputAction.search); - var captured = verify( - () => mockSearchApi.searchSmart(captureAny()), - ).captured; + var captured = verify(() => mockSearchApi.searchSmart(captureAny())).captured; - expect( - captured.first, - isA().having((s) => s.query, 'query', 'test'), - ); + expect(captured.first, isA().having((s) => s.query, 'query', 'test')); await tester.enterText(searchField, ''); await tester.testTextInput.receiveAction(TextInputAction.search); @@ -82,10 +69,7 @@ void main() { }); testWidgets('not contextual search with/without text', (tester) async { - await tester.pumpConsumerWidget( - const SearchPage(), - overrides: overrides, - ); + await tester.pumpConsumerWidget(const SearchPage(), overrides: overrides); await tester.pumpAndSettle(); @@ -93,11 +77,7 @@ void main() { await tester.pumpAndSettle(); - expect( - find.byIcon(Icons.image_search_rounded), - findsOneWidget, - reason: 'Should not have contextual search icon', - ); + expect(find.byIcon(Icons.image_search_rounded), findsOneWidget, reason: 'Should not have contextual search icon'); final searchField = find.byKey(const Key('search_text_field')); expect(searchField, findsOneWidget); @@ -105,15 +85,9 @@ void main() { await tester.enterText(searchField, 'test'); await tester.testTextInput.receiveAction(TextInputAction.search); - var captured = verify( - () => mockSearchApi.searchAssets(captureAny()), - ).captured; + var captured = verify(() => mockSearchApi.searchAssets(captureAny())).captured; - expect( - captured.first, - isA() - .having((s) => s.originalFileName, 'originalFileName', 'test'), - ); + expect(captured.first, isA().having((s) => s.originalFileName, 'originalFileName', 'test')); await tester.enterText(searchField, ''); await tester.testTextInput.receiveAction(TextInputAction.search); diff --git a/mobile/test/repository.mocks.dart b/mobile/test/repository.mocks.dart index 54a7e2d4a4..4b54ec4055 100644 --- a/mobile/test/repository.mocks.dart +++ b/mobile/test/repository.mocks.dart @@ -45,5 +45,4 @@ class MockPartnerRepository extends Mock implements PartnerRepository {} class MockPartnerApiRepository extends Mock implements PartnerApiRepository {} -class MockLocalFilesManagerRepository extends Mock - implements LocalFilesManagerRepository {} +class MockLocalFilesManagerRepository extends Mock implements LocalFilesManagerRepository {} diff --git a/mobile/test/services/album.service_test.dart b/mobile/test/services/album.service_test.dart index 443e37e75d..97683cdab1 100644 --- a/mobile/test/services/album.service_test.dart +++ b/mobile/test/services/album.service_test.dart @@ -33,12 +33,12 @@ void main() { when(() => userService.getMyUser()).thenReturn(UserStub.user1); - when(() => albumRepository.transaction(any())).thenAnswer( - (call) => (call.positionalArguments.first as Function).call(), - ); - when(() => assetRepository.transaction(any())).thenAnswer( - (call) => (call.positionalArguments.first as Function).call(), - ); + when( + () => albumRepository.transaction(any()), + ).thenAnswer((call) => (call.positionalArguments.first as Function).call()); + when( + () => assetRepository.transaction(any()), + ).thenAnswer((call) => (call.positionalArguments.first as Function).call()); sut = AlbumService( syncService, @@ -54,33 +54,26 @@ void main() { group('refreshDeviceAlbums', () { test('empty selection with one album in db', () async { - when(() => backupRepository.getIdsBySelection(BackupSelection.exclude)) - .thenAnswer((_) async => []); - when(() => backupRepository.getIdsBySelection(BackupSelection.select)) - .thenAnswer((_) async => []); + when(() => backupRepository.getIdsBySelection(BackupSelection.exclude)).thenAnswer((_) async => []); + when(() => backupRepository.getIdsBySelection(BackupSelection.select)).thenAnswer((_) async => []); when(() => albumMediaRepository.getAll()).thenAnswer((_) async => []); when(() => albumRepository.count(local: true)).thenAnswer((_) async => 1); - when(() => syncService.removeAllLocalAlbumsAndAssets()) - .thenAnswer((_) async => true); + when(() => syncService.removeAllLocalAlbumsAndAssets()).thenAnswer((_) async => true); final result = await sut.refreshDeviceAlbums(); expect(result, false); verify(() => syncService.removeAllLocalAlbumsAndAssets()); }); test('one selected albums, two on device', () async { - when(() => backupRepository.getIdsBySelection(BackupSelection.exclude)) - .thenAnswer((_) async => []); - when(() => backupRepository.getIdsBySelection(BackupSelection.select)) - .thenAnswer((_) async => [AlbumStub.oneAsset.localId!]); - when(() => albumMediaRepository.getAll()) - .thenAnswer((_) async => [AlbumStub.oneAsset, AlbumStub.twoAsset]); - when(() => syncService.syncLocalAlbumAssetsToDb(any(), any())) - .thenAnswer((_) async => true); + when(() => backupRepository.getIdsBySelection(BackupSelection.exclude)).thenAnswer((_) async => []); + when( + () => backupRepository.getIdsBySelection(BackupSelection.select), + ).thenAnswer((_) async => [AlbumStub.oneAsset.localId!]); + when(() => albumMediaRepository.getAll()).thenAnswer((_) async => [AlbumStub.oneAsset, AlbumStub.twoAsset]); + when(() => syncService.syncLocalAlbumAssetsToDb(any(), any())).thenAnswer((_) async => true); final result = await sut.refreshDeviceAlbums(); expect(result, true); - verify( - () => syncService.syncLocalAlbumAssetsToDb([AlbumStub.oneAsset], null), - ).called(1); + verify(() => syncService.syncLocalAlbumAssetsToDb([AlbumStub.oneAsset], null)).called(1); verifyNoMoreInteractions(syncService); }); }); @@ -88,20 +81,15 @@ void main() { group('refreshRemoteAlbums', () { test('is working', () async { when(() => syncService.getUsersFromServer()).thenAnswer((_) async => []); - when(() => syncService.syncUsersFromServer(any())) - .thenAnswer((_) async => true); - when(() => albumApiRepository.getAll(shared: true)) - .thenAnswer((_) async => [AlbumStub.sharedWithUser]); - - when(() => albumApiRepository.getAll(shared: null)) - .thenAnswer((_) async => [AlbumStub.oneAsset, AlbumStub.twoAsset]); + when(() => syncService.syncUsersFromServer(any())).thenAnswer((_) async => true); + when(() => albumApiRepository.getAll(shared: true)).thenAnswer((_) async => [AlbumStub.sharedWithUser]); when( - () => syncService.syncRemoteAlbumsToDb([ - AlbumStub.twoAsset, - AlbumStub.oneAsset, - AlbumStub.sharedWithUser, - ]), + () => albumApiRepository.getAll(shared: null), + ).thenAnswer((_) async => [AlbumStub.oneAsset, AlbumStub.twoAsset]); + + when( + () => syncService.syncRemoteAlbumsToDb([AlbumStub.twoAsset, AlbumStub.oneAsset, AlbumStub.sharedWithUser]), ).thenAnswer((_) async => true); final result = await sut.refreshRemoteAlbums(); expect(result, true); @@ -110,13 +98,7 @@ void main() { verify(() => albumApiRepository.getAll(shared: true)).called(1); verify(() => albumApiRepository.getAll(shared: null)).called(1); verify( - () => syncService.syncRemoteAlbumsToDb( - [ - AlbumStub.twoAsset, - AlbumStub.oneAsset, - AlbumStub.sharedWithUser, - ], - ), + () => syncService.syncRemoteAlbumsToDb([AlbumStub.twoAsset, AlbumStub.oneAsset, AlbumStub.sharedWithUser]), ).called(1); verifyNoMoreInteractions(userService); verifyNoMoreInteractions(albumApiRepository); @@ -138,12 +120,9 @@ void main() { () => entityService.fillAlbumWithDatabaseEntities(AlbumStub.oneAsset), ).thenAnswer((_) async => AlbumStub.oneAsset); - when( - () => albumRepository.create(AlbumStub.oneAsset), - ).thenAnswer((_) async => AlbumStub.twoAsset); + when(() => albumRepository.create(AlbumStub.oneAsset)).thenAnswer((_) async => AlbumStub.twoAsset); - final result = - await sut.createAlbum("name", [AssetStub.image1], [UserStub.user1]); + final result = await sut.createAlbum("name", [AssetStub.image1], [UserStub.user1]); expect(result, AlbumStub.twoAsset); verify( () => albumApiRepository.create( @@ -152,9 +131,7 @@ void main() { sharedUserIds: [UserStub.user1.id], ), ).called(1); - verify( - () => entityService.fillAlbumWithDatabaseEntities(AlbumStub.oneAsset), - ).called(1); + verify(() => entityService.fillAlbumWithDatabaseEntities(AlbumStub.oneAsset)).called(1); }); }); @@ -162,32 +139,14 @@ void main() { test('one added, one duplicate', () async { when( () => albumApiRepository.addAssets(AlbumStub.oneAsset.remoteId!, any()), - ).thenAnswer( - (_) async => ( - added: [AssetStub.image2.remoteId!], - duplicates: [AssetStub.image1.remoteId!] - ), - ); - when( - () => albumRepository.get(AlbumStub.oneAsset.id), - ).thenAnswer((_) async => AlbumStub.oneAsset); - when( - () => albumRepository.addAssets(AlbumStub.oneAsset, [AssetStub.image2]), - ).thenAnswer((_) async {}); - when( - () => albumRepository.removeAssets(AlbumStub.oneAsset, []), - ).thenAnswer((_) async {}); - when( - () => albumRepository.recalculateMetadata(AlbumStub.oneAsset), - ).thenAnswer((_) async => AlbumStub.oneAsset); - when( - () => albumRepository.update(AlbumStub.oneAsset), - ).thenAnswer((_) async => AlbumStub.oneAsset); + ).thenAnswer((_) async => (added: [AssetStub.image2.remoteId!], duplicates: [AssetStub.image1.remoteId!])); + when(() => albumRepository.get(AlbumStub.oneAsset.id)).thenAnswer((_) async => AlbumStub.oneAsset); + when(() => albumRepository.addAssets(AlbumStub.oneAsset, [AssetStub.image2])).thenAnswer((_) async {}); + when(() => albumRepository.removeAssets(AlbumStub.oneAsset, [])).thenAnswer((_) async {}); + when(() => albumRepository.recalculateMetadata(AlbumStub.oneAsset)).thenAnswer((_) async => AlbumStub.oneAsset); + when(() => albumRepository.update(AlbumStub.oneAsset)).thenAnswer((_) async => AlbumStub.oneAsset); - final result = await sut.addAssets( - AlbumStub.oneAsset, - [AssetStub.image1, AssetStub.image2], - ); + final result = await sut.addAssets(AlbumStub.oneAsset, [AssetStub.image1, AssetStub.image2]); expect(result != null, true); expect(result!.alreadyInAlbum, [AssetStub.image1.remoteId!]); @@ -198,11 +157,8 @@ void main() { group('addAdditionalUserToAlbum', () { test('one added', () async { when( - () => - albumApiRepository.addUsers(AlbumStub.emptyAlbum.remoteId!, any()), - ).thenAnswer( - (_) async => AlbumStub.sharedWithUser, - ); + () => albumApiRepository.addUsers(AlbumStub.emptyAlbum.remoteId!, any()), + ).thenAnswer((_) async => AlbumStub.sharedWithUser); when( () => albumRepository.addUsers( @@ -211,14 +167,9 @@ void main() { ), ).thenAnswer((_) async => AlbumStub.emptyAlbum); - when( - () => albumRepository.update(AlbumStub.emptyAlbum), - ).thenAnswer((_) async => AlbumStub.emptyAlbum); + when(() => albumRepository.update(AlbumStub.emptyAlbum)).thenAnswer((_) async => AlbumStub.emptyAlbum); - final result = await sut.addUsers( - AlbumStub.emptyAlbum, - [UserStub.user2.id], - ); + final result = await sut.addUsers(AlbumStub.emptyAlbum, [UserStub.user2.id]); expect(result, true); }); diff --git a/mobile/test/services/asset.service_test.dart b/mobile/test/services/asset.service_test.dart index 293e5ec76b..b741150165 100644 --- a/mobile/test/services/asset.service_test.dart +++ b/mobile/test/services/asset.service_test.dart @@ -69,8 +69,7 @@ void main() { setUp(() { assetsApi = MockAssetsApi(); when(() => apiService.assetsApi).thenReturn(assetsApi); - when(() => assetsApi.updateAssets(any())) - .thenAnswer((_) async => Future.value()); + when(() => assetsApi.updateAssets(any())).thenAnswer((_) async => Future.value()); }); test("asset is updated with DateTime", () async { @@ -79,14 +78,10 @@ void main() { await sut.changeDateTime(assets, dateTime.toIso8601String()); verify(() => assetsApi.updateAssets(any())).called(1); - final upsertExifCallback = - verify(() => syncService.upsertAssetsWithExif(captureAny())); + final upsertExifCallback = verify(() => syncService.upsertAssetsWithExif(captureAny())); upsertExifCallback.called(1); - final receivedAssets = - upsertExifCallback.captured.firstOrNull as List? ?? []; - final receivedDatetime = receivedAssets.cast().map( - (a) => a.exifInfo?.dateTimeOriginal ?? DateTime(0), - ); + final receivedAssets = upsertExifCallback.captured.firstOrNull as List? ?? []; + final receivedDatetime = receivedAssets.cast().map((a) => a.exifInfo?.dateTimeOriginal ?? DateTime(0)); expect(receivedDatetime.every((d) => d == dateTime), isTrue); }); @@ -96,15 +91,12 @@ void main() { await sut.changeLocation(assets, latLng); verify(() => assetsApi.updateAssets(any())).called(1); - final upsertExifCallback = - verify(() => syncService.upsertAssetsWithExif(captureAny())); + final upsertExifCallback = verify(() => syncService.upsertAssetsWithExif(captureAny())); upsertExifCallback.called(1); - final receivedAssets = - upsertExifCallback.captured.firstOrNull as List? ?? []; + final receivedAssets = upsertExifCallback.captured.firstOrNull as List? ?? []; final receivedCoords = receivedAssets.cast().map( - (a) => - LatLng(a.exifInfo?.latitude ?? 0, a.exifInfo?.longitude ?? 0), - ); + (a) => LatLng(a.exifInfo?.latitude ?? 0, a.exifInfo?.longitude ?? 0), + ); expect(receivedCoords.every((l) => l == latLng), isTrue); }); }); diff --git a/mobile/test/services/auth.service_test.dart b/mobile/test/services/auth.service_test.dart index 46ad8bbc4c..1bad780ca7 100644 --- a/mobile/test/services/auth.service_test.dart +++ b/mobile/test/services/auth.service_test.dart @@ -3,6 +3,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:immich_mobile/domain/services/store.service.dart'; import 'package:immich_mobile/infrastructure/repositories/store.repository.dart'; import 'package:immich_mobile/models/auth/auxilary_endpoint.model.dart'; +import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/services/auth.service.dart'; import 'package:isar/isar.dart'; import 'package:mocktail/mocktail.dart'; @@ -20,6 +21,8 @@ void main() { late MockApiService apiService; late MockNetworkService networkService; late MockBackgroundSyncManager backgroundSyncManager; + late MockUploadService uploadService; + late MockAppSettingService appSettingsService; late Isar db; setUp(() async { @@ -28,6 +31,8 @@ void main() { apiService = MockApiService(); networkService = MockNetworkService(); backgroundSyncManager = MockBackgroundSyncManager(); + uploadService = MockUploadService(); + appSettingsService = MockAppSettingService(); sut = AuthService( authApiRepository, @@ -35,6 +40,7 @@ void main() { apiService, networkService, backgroundSyncManager, + appSettingsService, ); registerFallbackValue(Uri()); @@ -58,8 +64,7 @@ void main() { const testUrl = 'http://ip:2283'; const resolvedUrl = 'http://ip:2283/api'; - when(() => apiService.resolveAndSetEndpoint(testUrl)) - .thenAnswer((_) async => resolvedUrl); + when(() => apiService.resolveAndSetEndpoint(testUrl)).thenAnswer((_) async => resolvedUrl); when(() => apiService.setDeviceInfoHeader()).thenAnswer((_) async => {}); final result = await sut.validateServerUrl(testUrl); @@ -74,8 +79,7 @@ void main() { const testUrl = 'https://immich.domain.com'; const resolvedUrl = 'https://immich.domain.com/api'; - when(() => apiService.resolveAndSetEndpoint(testUrl)) - .thenAnswer((_) async => resolvedUrl); + when(() => apiService.resolveAndSetEndpoint(testUrl)).thenAnswer((_) async => resolvedUrl); when(() => apiService.setDeviceInfoHeader()).thenAnswer((_) async => {}); final result = await sut.validateServerUrl(testUrl); @@ -89,13 +93,9 @@ void main() { test('Should throw error on invalid URL', () async { const testUrl = 'invalid-url'; - when(() => apiService.resolveAndSetEndpoint(testUrl)) - .thenThrow(Exception('Invalid URL')); + when(() => apiService.resolveAndSetEndpoint(testUrl)).thenThrow(Exception('Invalid URL')); - expect( - () async => await sut.validateServerUrl(testUrl), - throwsA(isA()), - ); + expect(() async => await sut.validateServerUrl(testUrl), throwsA(isA())); verify(() => apiService.resolveAndSetEndpoint(testUrl)).called(1); verifyNever(() => apiService.setDeviceInfoHeader()); @@ -104,13 +104,9 @@ void main() { test('Should throw error on unreachable server', () async { const testUrl = 'https://unreachable.server'; - when(() => apiService.resolveAndSetEndpoint(testUrl)) - .thenThrow(Exception('Server is not reachable')); + when(() => apiService.resolveAndSetEndpoint(testUrl)).thenThrow(Exception('Server is not reachable')); - expect( - () async => await sut.validateServerUrl(testUrl), - throwsA(isA()), - ); + expect(() async => await sut.validateServerUrl(testUrl), throwsA(isA())); verify(() => apiService.resolveAndSetEndpoint(testUrl)).called(1); verifyNever(() => apiService.setDeviceInfoHeader()); @@ -121,9 +117,11 @@ void main() { test('Should logout user', () async { when(() => authApiRepository.logout()).thenAnswer((_) async => {}); when(() => backgroundSyncManager.cancel()).thenAnswer((_) async => {}); - when(() => authRepository.clearLocalData()) - .thenAnswer((_) => Future.value(null)); - + when(() => authRepository.clearLocalData()).thenAnswer((_) => Future.value(null)); + when(() => uploadService.cancelBackup()).thenAnswer((_) => Future.value(1)); + when( + () => appSettingsService.setSetting(AppSettingsEnum.enableBackup, false), + ).thenAnswer((_) => Future.value(null)); await sut.logout(); verify(() => authApiRepository.logout()).called(1); @@ -132,12 +130,13 @@ void main() { }); test('Should clear local data even on server error', () async { - when(() => authApiRepository.logout()) - .thenThrow(Exception('Server error')); + when(() => authApiRepository.logout()).thenThrow(Exception('Server error')); when(() => backgroundSyncManager.cancel()).thenAnswer((_) async => {}); - when(() => authRepository.clearLocalData()) - .thenAnswer((_) => Future.value(null)); - + when(() => authRepository.clearLocalData()).thenAnswer((_) => Future.value(null)); + when(() => uploadService.cancelBackup()).thenAnswer((_) => Future.value(1)); + when( + () => appSettingsService.setSetting(AppSettingsEnum.enableBackup, false), + ).thenAnswer((_) => Future.value(null)); await sut.logout(); verify(() => authApiRepository.logout()).called(1); @@ -148,13 +147,11 @@ void main() { group('setOpenApiServiceEndpoint', () { setUp(() { - when(() => networkService.getWifiName()) - .thenAnswer((_) async => 'TestWifi'); + when(() => networkService.getWifiName()).thenAnswer((_) async => 'TestWifi'); }); test('Should return null if auto endpoint switching is disabled', () async { - when(() => authRepository.getEndpointSwitchingFeature()) - .thenReturn((false)); + when(() => authRepository.getEndpointSwitchingFeature()).thenReturn((false)); final result = await sut.setOpenApiServiceEndpoint(); @@ -166,10 +163,10 @@ void main() { test('Should set local connection if wifi name matches', () async { when(() => authRepository.getEndpointSwitchingFeature()).thenReturn(true); when(() => authRepository.getPreferredWifiName()).thenReturn('TestWifi'); - when(() => authRepository.getLocalEndpoint()) - .thenReturn('http://local.endpoint'); - when(() => apiService.resolveAndSetEndpoint('http://local.endpoint')) - .thenAnswer((_) async => 'http://local.endpoint'); + when(() => authRepository.getLocalEndpoint()).thenReturn('http://local.endpoint'); + when( + () => apiService.resolveAndSetEndpoint('http://local.endpoint'), + ).thenAnswer((_) async => 'http://local.endpoint'); final result = await sut.setOpenApiServiceEndpoint(); @@ -178,20 +175,15 @@ void main() { verify(() => networkService.getWifiName()).called(1); verify(() => authRepository.getPreferredWifiName()).called(1); verify(() => authRepository.getLocalEndpoint()).called(1); - verify(() => apiService.resolveAndSetEndpoint('http://local.endpoint')) - .called(1); + verify(() => apiService.resolveAndSetEndpoint('http://local.endpoint')).called(1); }); test('Should set external endpoint if wifi name not matching', () async { when(() => authRepository.getEndpointSwitchingFeature()).thenReturn(true); - when(() => authRepository.getPreferredWifiName()) - .thenReturn('DifferentWifi'); - when(() => authRepository.getExternalEndpointList()).thenReturn([ - const AuxilaryEndpoint( - url: 'https://external.endpoint', - status: AuxCheckStatus.valid, - ), - ]); + when(() => authRepository.getPreferredWifiName()).thenReturn('DifferentWifi'); + when( + () => authRepository.getExternalEndpointList(), + ).thenReturn([const AuxilaryEndpoint(url: 'https://external.endpoint', status: AuxCheckStatus.valid)]); when( () => apiService.resolveAndSetEndpoint('https://external.endpoint'), ).thenAnswer((_) async => 'https://external.endpoint/api'); @@ -203,25 +195,15 @@ void main() { verify(() => networkService.getWifiName()).called(1); verify(() => authRepository.getPreferredWifiName()).called(1); verify(() => authRepository.getExternalEndpointList()).called(1); - verify( - () => apiService.resolveAndSetEndpoint('https://external.endpoint'), - ).called(1); + verify(() => apiService.resolveAndSetEndpoint('https://external.endpoint')).called(1); }); - test('Should set second external endpoint if the first throw any error', - () async { + test('Should set second external endpoint if the first throw any error', () async { when(() => authRepository.getEndpointSwitchingFeature()).thenReturn(true); - when(() => authRepository.getPreferredWifiName()) - .thenReturn('DifferentWifi'); + when(() => authRepository.getPreferredWifiName()).thenReturn('DifferentWifi'); when(() => authRepository.getExternalEndpointList()).thenReturn([ - const AuxilaryEndpoint( - url: 'https://external.endpoint', - status: AuxCheckStatus.valid, - ), - const AuxilaryEndpoint( - url: 'https://external.endpoint2', - status: AuxCheckStatus.valid, - ), + const AuxilaryEndpoint(url: 'https://external.endpoint', status: AuxCheckStatus.valid), + const AuxilaryEndpoint(url: 'https://external.endpoint2', status: AuxCheckStatus.valid), ]); when( @@ -238,25 +220,15 @@ void main() { verify(() => networkService.getWifiName()).called(1); verify(() => authRepository.getPreferredWifiName()).called(1); verify(() => authRepository.getExternalEndpointList()).called(1); - verify( - () => apiService.resolveAndSetEndpoint('https://external.endpoint2'), - ).called(1); + verify(() => apiService.resolveAndSetEndpoint('https://external.endpoint2')).called(1); }); - test('Should set second external endpoint if the first throw ApiException', - () async { + test('Should set second external endpoint if the first throw ApiException', () async { when(() => authRepository.getEndpointSwitchingFeature()).thenReturn(true); - when(() => authRepository.getPreferredWifiName()) - .thenReturn('DifferentWifi'); + when(() => authRepository.getPreferredWifiName()).thenReturn('DifferentWifi'); when(() => authRepository.getExternalEndpointList()).thenReturn([ - const AuxilaryEndpoint( - url: 'https://external.endpoint', - status: AuxCheckStatus.valid, - ), - const AuxilaryEndpoint( - url: 'https://external.endpoint2', - status: AuxCheckStatus.valid, - ), + const AuxilaryEndpoint(url: 'https://external.endpoint', status: AuxCheckStatus.valid), + const AuxilaryEndpoint(url: 'https://external.endpoint2', status: AuxCheckStatus.valid), ]); when( @@ -273,18 +245,16 @@ void main() { verify(() => networkService.getWifiName()).called(1); verify(() => authRepository.getPreferredWifiName()).called(1); verify(() => authRepository.getExternalEndpointList()).called(1); - verify( - () => apiService.resolveAndSetEndpoint('https://external.endpoint2'), - ).called(1); + verify(() => apiService.resolveAndSetEndpoint('https://external.endpoint2')).called(1); }); test('Should handle error when setting local connection', () async { when(() => authRepository.getEndpointSwitchingFeature()).thenReturn(true); when(() => authRepository.getPreferredWifiName()).thenReturn('TestWifi'); - when(() => authRepository.getLocalEndpoint()) - .thenReturn('http://local.endpoint'); - when(() => apiService.resolveAndSetEndpoint('http://local.endpoint')) - .thenThrow(Exception('Local endpoint error')); + when(() => authRepository.getLocalEndpoint()).thenReturn('http://local.endpoint'); + when( + () => apiService.resolveAndSetEndpoint('http://local.endpoint'), + ).thenThrow(Exception('Local endpoint error')); final result = await sut.setOpenApiServiceEndpoint(); @@ -293,20 +263,15 @@ void main() { verify(() => networkService.getWifiName()).called(1); verify(() => authRepository.getPreferredWifiName()).called(1); verify(() => authRepository.getLocalEndpoint()).called(1); - verify(() => apiService.resolveAndSetEndpoint('http://local.endpoint')) - .called(1); + verify(() => apiService.resolveAndSetEndpoint('http://local.endpoint')).called(1); }); test('Should handle error when setting external connection', () async { when(() => authRepository.getEndpointSwitchingFeature()).thenReturn(true); - when(() => authRepository.getPreferredWifiName()) - .thenReturn('DifferentWifi'); - when(() => authRepository.getExternalEndpointList()).thenReturn([ - const AuxilaryEndpoint( - url: 'https://external.endpoint', - status: AuxCheckStatus.valid, - ), - ]); + when(() => authRepository.getPreferredWifiName()).thenReturn('DifferentWifi'); + when( + () => authRepository.getExternalEndpointList(), + ).thenReturn([const AuxilaryEndpoint(url: 'https://external.endpoint', status: AuxCheckStatus.valid)]); when( () => apiService.resolveAndSetEndpoint('https://external.endpoint'), ).thenThrow(Exception('External endpoint error')); @@ -318,9 +283,7 @@ void main() { verify(() => networkService.getWifiName()).called(1); verify(() => authRepository.getPreferredWifiName()).called(1); verify(() => authRepository.getExternalEndpointList()).called(1); - verify( - () => apiService.resolveAndSetEndpoint('https://external.endpoint'), - ).called(1); + verify(() => apiService.resolveAndSetEndpoint('https://external.endpoint')).called(1); }); }); } diff --git a/mobile/test/services/entity.service_test.dart b/mobile/test/services/entity.service_test.dart index 1642be7fb3..64b9fc604b 100644 --- a/mobile/test/services/entity.service_test.dart +++ b/mobile/test/services/entity.service_test.dart @@ -21,60 +21,50 @@ void main() { }); group('fillAlbumWithDatabaseEntities', () { - test('remote album with owner, thumbnail, sharedUsers and assets', - () async { - final Album album = Album( - name: "album-with-two-assets-and-two-users", - localId: "album-with-two-assets-and-two-users-local", - remoteId: "album-with-two-assets-and-two-users-remote", - createdAt: DateTime(2001), - modifiedAt: DateTime(2010), - shared: true, - activityEnabled: true, - startDate: DateTime(2019), - endDate: DateTime(2020), - ) - ..remoteThumbnailAssetId = AssetStub.image1.remoteId - ..assets.addAll([AssetStub.image1, AssetStub.image1]) - ..owner.value = User.fromDto(UserStub.user1) - ..sharedUsers.addAll( - [User.fromDto(UserStub.admin), User.fromDto(UserStub.admin)], - ); + test('remote album with owner, thumbnail, sharedUsers and assets', () async { + final Album album = + Album( + name: "album-with-two-assets-and-two-users", + localId: "album-with-two-assets-and-two-users-local", + remoteId: "album-with-two-assets-and-two-users-remote", + createdAt: DateTime(2001), + modifiedAt: DateTime(2010), + shared: true, + activityEnabled: true, + startDate: DateTime(2019), + endDate: DateTime(2020), + ) + ..remoteThumbnailAssetId = AssetStub.image1.remoteId + ..assets.addAll([AssetStub.image1, AssetStub.image1]) + ..owner.value = User.fromDto(UserStub.user1) + ..sharedUsers.addAll([User.fromDto(UserStub.admin), User.fromDto(UserStub.admin)]); - when(() => userRepository.getByUserId(any())) - .thenAnswer((_) async => UserStub.admin); - when(() => userRepository.getByUserId(any())) - .thenAnswer((_) async => UserStub.admin); + when(() => userRepository.getByUserId(any())).thenAnswer((_) async => UserStub.admin); + when(() => userRepository.getByUserId(any())).thenAnswer((_) async => UserStub.admin); - when(() => assetRepository.getByRemoteId(AssetStub.image1.remoteId!)) - .thenAnswer((_) async => AssetStub.image1); + when(() => assetRepository.getByRemoteId(AssetStub.image1.remoteId!)).thenAnswer((_) async => AssetStub.image1); - when(() => userRepository.getByUserIds(any())) - .thenAnswer((_) async => [UserStub.user1, UserStub.user2]); + when(() => userRepository.getByUserIds(any())).thenAnswer((_) async => [UserStub.user1, UserStub.user2]); - when(() => assetRepository.getAllByRemoteId(any())) - .thenAnswer((_) async => [AssetStub.image1, AssetStub.image2]); + when(() => assetRepository.getAllByRemoteId(any())).thenAnswer((_) async => [AssetStub.image1, AssetStub.image2]); await sut.fillAlbumWithDatabaseEntities(album); expect(album.owner.value?.toDto(), UserStub.admin); expect(album.thumbnail.value, AssetStub.image1); - expect( - album.remoteUsers.map((u) => u.toDto()).toSet(), - {UserStub.user1, UserStub.user2}, - ); + expect(album.remoteUsers.map((u) => u.toDto()).toSet(), {UserStub.user1, UserStub.user2}); expect(album.remoteAssets.toSet(), {AssetStub.image1, AssetStub.image2}); }); test('remote album without any info', () async { makeEmptyAlbum() => Album( - name: "album-without-info", - localId: "album-without-info-local", - remoteId: "album-without-info-remote", - createdAt: DateTime(2001), - modifiedAt: DateTime(2010), - shared: false, - activityEnabled: false, - ); + name: "album-without-info", + localId: "album-without-info-local", + remoteId: "album-without-info-remote", + createdAt: DateTime(2001), + modifiedAt: DateTime(2010), + shared: false, + activityEnabled: false, + ); final album = makeEmptyAlbum(); await sut.fillAlbumWithDatabaseEntities(album); diff --git a/mobile/test/services/hash_service_test.dart b/mobile/test/services/hash_service_test.dart index 2ba9c356a0..74b8575e40 100644 --- a/mobile/test/services/hash_service_test.dart +++ b/mobile/test/services/hash_service_test.dart @@ -31,48 +31,32 @@ void main() { mockBackgroundService = MockBackgroundService(); mockDeviceAssetRepository = MockDeviceAssetRepository(); - sut = HashService( - deviceAssetRepository: mockDeviceAssetRepository, - backgroundService: mockBackgroundService, - ); + sut = HashService(deviceAssetRepository: mockDeviceAssetRepository, backgroundService: mockBackgroundService); - when(() => mockDeviceAssetRepository.transaction(any())) - .thenAnswer((_) async { - final capturedCallback = verify( - () => mockDeviceAssetRepository.transaction(captureAny()), - ).captured; + when(() => mockDeviceAssetRepository.transaction(any())).thenAnswer((_) async { + final capturedCallback = verify(() => mockDeviceAssetRepository.transaction(captureAny())).captured; // Invoke the transaction callback await (capturedCallback.firstOrNull as Future Function()?)?.call(); }); - when(() => mockDeviceAssetRepository.updateAll(any())) - .thenAnswer((_) async => true); - when(() => mockDeviceAssetRepository.deleteIds(any())) - .thenAnswer((_) async => true); + when(() => mockDeviceAssetRepository.updateAll(any())).thenAnswer((_) async => true); + when(() => mockDeviceAssetRepository.deleteIds(any())).thenAnswer((_) async => true); }); group("HashService: No DeviceAsset entry", () { test("hash successfully", () async { - final (mockAsset, file, deviceAsset, hash) = - await _createAssetMock(AssetStub.image1); + final (mockAsset, file, deviceAsset, hash) = await _createAssetMock(AssetStub.image1); - when(() => mockBackgroundService.digestFiles([file.path])) - .thenAnswer((_) async => [hash]); + when(() => mockBackgroundService.digestFiles([file.path])).thenAnswer((_) async => [hash]); // No DB entries for this asset - when( - () => mockDeviceAssetRepository.getByIds([AssetStub.image1.localId!]), - ).thenAnswer((_) async => []); + when(() => mockDeviceAssetRepository.getByIds([AssetStub.image1.localId!])).thenAnswer((_) async => []); final result = await sut.hashAssets([mockAsset]); // Verify we stored the new hash in DB - when(() => mockDeviceAssetRepository.transaction(any())) - .thenAnswer((_) async { - final capturedCallback = verify( - () => mockDeviceAssetRepository.transaction(captureAny()), - ).captured; + when(() => mockDeviceAssetRepository.transaction(any())).thenAnswer((_) async { + final capturedCallback = verify(() => mockDeviceAssetRepository.transaction(captureAny())).captured; // Invoke the transaction callback - await (capturedCallback.firstOrNull as Future Function()?) - ?.call(); + await (capturedCallback.firstOrNull as Future Function()?)?.call(); verify( () => mockDeviceAssetRepository.updateAll([ deviceAsset.copyWith(modifiedTime: AssetStub.image1.fileModifiedAt), @@ -80,10 +64,7 @@ void main() { ).called(1); verify(() => mockDeviceAssetRepository.deleteIds([])).called(1); }); - expect( - result, - [AssetStub.image1.copyWith(checksum: base64.encode(hash))], - ); + expect(result, [AssetStub.image1.copyWith(checksum: base64.encode(hash))]); }); }); @@ -91,15 +72,9 @@ void main() { test("when the asset is not modified", () async { final hash = utf8.encode("image1-hash"); - when( - () => mockDeviceAssetRepository.getByIds([AssetStub.image1.localId!]), - ).thenAnswer( + when(() => mockDeviceAssetRepository.getByIds([AssetStub.image1.localId!])).thenAnswer( (_) async => [ - DeviceAsset( - assetId: AssetStub.image1.localId!, - hash: hash, - modifiedTime: AssetStub.image1.fileModifiedAt, - ), + DeviceAsset(assetId: AssetStub.image1.localId!, hash: hash, modifiedTime: AssetStub.image1.fileModifiedAt), ], ); final result = await sut.hashAssets([AssetStub.image1]); @@ -109,31 +84,23 @@ void main() { verifyNever(() => mockDeviceAssetRepository.updateAll(any())); verifyNever(() => mockDeviceAssetRepository.deleteIds(any())); - expect(result, [ - AssetStub.image1.copyWith(checksum: base64.encode(hash)), - ]); + expect(result, [AssetStub.image1.copyWith(checksum: base64.encode(hash))]); }); test("hashed successful when asset is modified", () async { - final (mockAsset, file, deviceAsset, hash) = - await _createAssetMock(AssetStub.image1); + final (mockAsset, file, deviceAsset, hash) = await _createAssetMock(AssetStub.image1); - when(() => mockBackgroundService.digestFiles([file.path])) - .thenAnswer((_) async => [hash]); + when(() => mockBackgroundService.digestFiles([file.path])).thenAnswer((_) async => [hash]); when( () => mockDeviceAssetRepository.getByIds([AssetStub.image1.localId!]), ).thenAnswer((_) async => [deviceAsset]); final result = await sut.hashAssets([mockAsset]); - when(() => mockDeviceAssetRepository.transaction(any())) - .thenAnswer((_) async { - final capturedCallback = verify( - () => mockDeviceAssetRepository.transaction(captureAny()), - ).captured; + when(() => mockDeviceAssetRepository.transaction(any())).thenAnswer((_) async { + final capturedCallback = verify(() => mockDeviceAssetRepository.transaction(captureAny())).captured; // Invoke the transaction callback - await (capturedCallback.firstOrNull as Future Function()?) - ?.call(); + await (capturedCallback.firstOrNull as Future Function()?)?.call(); verify( () => mockDeviceAssetRepository.updateAll([ deviceAsset.copyWith(modifiedTime: AssetStub.image1.fileModifiedAt), @@ -144,9 +111,7 @@ void main() { verify(() => mockBackgroundService.digestFiles([file.path])).called(1); - expect(result, [ - AssetStub.image1.copyWith(checksum: base64.encode(hash)), - ]); + expect(result, [AssetStub.image1.copyWith(checksum: base64.encode(hash))]); }); }); @@ -157,11 +122,9 @@ void main() { late File file; setUp(() async { - (mockAsset, file, deviceAsset, hash) = - await _createAssetMock(AssetStub.image1); + (mockAsset, file, deviceAsset, hash) = await _createAssetMock(AssetStub.image1); - when(() => mockBackgroundService.digestFiles([file.path])) - .thenAnswer((_) async => [hash]); + when(() => mockBackgroundService.digestFiles([file.path])).thenAnswer((_) async => [hash]); when( () => mockDeviceAssetRepository.getByIds([AssetStub.image1.localId!]), ).thenAnswer((_) async => [deviceAsset]); @@ -174,22 +137,16 @@ void main() { verifyNever(() => mockBackgroundService.digestFiles(any())); verifyNever(() => mockBackgroundService.digestFile(any())); verifyNever(() => mockDeviceAssetRepository.updateAll(any())); - verify( - () => mockDeviceAssetRepository.deleteIds([AssetStub.image1.localId!]), - ).called(1); + verify(() => mockDeviceAssetRepository.deleteIds([AssetStub.image1.localId!])).called(1); expect(result, isEmpty); }); test("cleanups DeviceAsset when hashing failed", () async { - when(() => mockDeviceAssetRepository.transaction(any())) - .thenAnswer((_) async { - final capturedCallback = verify( - () => mockDeviceAssetRepository.transaction(captureAny()), - ).captured; + when(() => mockDeviceAssetRepository.transaction(any())).thenAnswer((_) async { + final capturedCallback = verify(() => mockDeviceAssetRepository.transaction(captureAny())).captured; // Invoke the transaction callback - await (capturedCallback.firstOrNull as Future Function()?) - ?.call(); + await (capturedCallback.firstOrNull as Future Function()?)?.call(); // Verify the callback inside the transaction because, doing it outside results // in a small delay before the callback is invoked, resulting in other LOCs getting executed @@ -209,10 +166,7 @@ void main() { // To avoid this, we capture the callback and execute it within the transaction stub itself // and verify the results inside the transaction stub verify(() => mockDeviceAssetRepository.updateAll([])).called(1); - verify( - () => - mockDeviceAssetRepository.deleteIds([AssetStub.image1.localId!]), - ).called(1); + verify(() => mockDeviceAssetRepository.deleteIds([AssetStub.image1.localId!])).called(1); }); when(() => mockBackgroundService.digestFiles([file.path])).thenAnswer( @@ -240,14 +194,11 @@ void main() { final (asset2, file2, deviceAsset2, hash2) = mock2; final (asset3, file3, deviceAsset3, hash3) = mock3; - when(() => mockDeviceAssetRepository.getByIds(any())) - .thenAnswer((_) async => []); + when(() => mockDeviceAssetRepository.getByIds(any())).thenAnswer((_) async => []); // Setup for multiple batch processing calls - when(() => mockBackgroundService.digestFiles([file1.path, file2.path])) - .thenAnswer((_) async => [hash1, hash2]); - when(() => mockBackgroundService.digestFiles([file3.path])) - .thenAnswer((_) async => [hash3]); + when(() => mockBackgroundService.digestFiles([file1.path, file2.path])).thenAnswer((_) async => [hash1, hash2]); + when(() => mockBackgroundService.digestFiles([file3.path])).thenAnswer((_) async => [hash3]); final size = await file1.length() + await file2.length(); @@ -259,18 +210,14 @@ void main() { final result = await sut.hashAssets([asset1, asset2, asset3]); // Verify multiple batch process calls - verify(() => mockBackgroundService.digestFiles([file1.path, file2.path])) - .called(1); + verify(() => mockBackgroundService.digestFiles([file1.path, file2.path])).called(1); verify(() => mockBackgroundService.digestFiles([file3.path])).called(1); - expect( - result, - [ - AssetStub.image1.copyWith(checksum: base64.encode(hash1)), - AssetStub.image2.copyWith(checksum: base64.encode(hash2)), - AssetStub.image3.copyWith(checksum: base64.encode(hash3)), - ], - ); + expect(result, [ + AssetStub.image1.copyWith(checksum: base64.encode(hash1)), + AssetStub.image2.copyWith(checksum: base64.encode(hash2)), + AssetStub.image3.copyWith(checksum: base64.encode(hash3)), + ]); }); test("processes assets in batches when file limit is reached", () async { @@ -285,15 +232,11 @@ void main() { final (asset2, file2, deviceAsset2, hash2) = mock2; final (asset3, file3, deviceAsset3, hash3) = mock3; - when(() => mockDeviceAssetRepository.getByIds(any())) - .thenAnswer((_) async => []); + when(() => mockDeviceAssetRepository.getByIds(any())).thenAnswer((_) async => []); - when(() => mockBackgroundService.digestFiles([file1.path])) - .thenAnswer((_) async => [hash1]); - when(() => mockBackgroundService.digestFiles([file2.path])) - .thenAnswer((_) async => [hash2]); - when(() => mockBackgroundService.digestFiles([file3.path])) - .thenAnswer((_) async => [hash3]); + when(() => mockBackgroundService.digestFiles([file1.path])).thenAnswer((_) async => [hash1]); + when(() => mockBackgroundService.digestFiles([file2.path])).thenAnswer((_) async => [hash2]); + when(() => mockBackgroundService.digestFiles([file3.path])).thenAnswer((_) async => [hash3]); sut = HashService( deviceAssetRepository: mockDeviceAssetRepository, @@ -307,28 +250,20 @@ void main() { verify(() => mockBackgroundService.digestFiles([file2.path])).called(1); verify(() => mockBackgroundService.digestFiles([file3.path])).called(1); - expect( - result, - [ - AssetStub.image1.copyWith(checksum: base64.encode(hash1)), - AssetStub.image2.copyWith(checksum: base64.encode(hash2)), - AssetStub.image3.copyWith(checksum: base64.encode(hash3)), - ], - ); + expect(result, [ + AssetStub.image1.copyWith(checksum: base64.encode(hash1)), + AssetStub.image2.copyWith(checksum: base64.encode(hash2)), + AssetStub.image3.copyWith(checksum: base64.encode(hash3)), + ]); }); test("HashService: Sort & Process different states", () async { - final (asset1, file1, deviceAsset1, hash1) = - await _createAssetMock(AssetStub.image1); // Will need rehashing - final (asset2, file2, deviceAsset2, hash2) = - await _createAssetMock(AssetStub.image2); // Will have matching hash - final (asset3, file3, deviceAsset3, hash3) = - await _createAssetMock(AssetStub.image3); // No DB entry - final asset4 = - AssetStub.image3.copyWith(localId: "image4"); // Cannot be hashed + final (asset1, file1, deviceAsset1, hash1) = await _createAssetMock(AssetStub.image1); // Will need rehashing + final (asset2, file2, deviceAsset2, hash2) = await _createAssetMock(AssetStub.image2); // Will have matching hash + final (asset3, file3, deviceAsset3, hash3) = await _createAssetMock(AssetStub.image3); // No DB entry + final asset4 = AssetStub.image3.copyWith(localId: "image4"); // Cannot be hashed - when(() => mockBackgroundService.digestFiles([file1.path, file3.path])) - .thenAnswer((_) async => [hash1, hash3]); + when(() => mockBackgroundService.digestFiles([file1.path, file3.path])).thenAnswer((_) async => [hash1, hash3]); // DB entries are not sorted and a dummy entry added when( () => mockDeviceAssetRepository.getByIds([ @@ -349,8 +284,7 @@ void main() { final result = await sut.hashAssets([asset1, asset2, asset3, asset4]); // Verify correct processing of all assets - verify(() => mockBackgroundService.digestFiles([file1.path, file3.path])) - .called(1); + verify(() => mockBackgroundService.digestFiles([file1.path, file3.path])).called(1); expect(result.length, 3); expect(result, [ AssetStub.image2.copyWith(checksum: base64.encode(hash2)), @@ -361,8 +295,7 @@ void main() { group("HashService: Edge cases", () { test("handles empty list of assets", () async { - when(() => mockDeviceAssetRepository.getByIds(any())) - .thenAnswer((_) async => []); + when(() => mockDeviceAssetRepository.getByIds(any())).thenAnswer((_) async => []); final result = await sut.hashAssets([]); @@ -376,15 +309,10 @@ void main() { test("handles all file access failures", () async { // No DB entries when( - () => mockDeviceAssetRepository.getByIds( - [AssetStub.image1.localId!, AssetStub.image2.localId!], - ), + () => mockDeviceAssetRepository.getByIds([AssetStub.image1.localId!, AssetStub.image2.localId!]), ).thenAnswer((_) async => []); - final result = await sut.hashAssets([ - AssetStub.image1, - AssetStub.image2, - ]); + final result = await sut.hashAssets([AssetStub.image1, AssetStub.image2]); verifyNever(() => mockBackgroundService.digestFiles(any())); verifyNever(() => mockDeviceAssetRepository.updateAll(any())); @@ -394,12 +322,9 @@ void main() { }); } -Future<(Asset, File, DeviceAsset, Uint8List)> _createAssetMock( - Asset asset, -) async { +Future<(Asset, File, DeviceAsset, Uint8List)> _createAssetMock(Asset asset) async { final random = Random(); - final hash = - Uint8List.fromList(List.generate(20, (i) => random.nextInt(255))); + final hash = Uint8List.fromList(List.generate(20, (i) => random.nextInt(255))); final mockAsset = MockAsset(); final mockAssetEntity = MockAssetEntity(); final fs = MemoryFileSystem(); @@ -416,8 +341,9 @@ Future<(Asset, File, DeviceAsset, Uint8List)> _createAssetMock( when(() => mockAsset.fileName).thenReturn(asset.fileName); when(() => mockAsset.fileCreatedAt).thenReturn(asset.fileCreatedAt); when(() => mockAsset.fileModifiedAt).thenReturn(asset.fileModifiedAt); - when(() => mockAsset.copyWith(checksum: any(named: "checksum"))) - .thenReturn(asset.copyWith(checksum: base64.encode(hash))); + when( + () => mockAsset.copyWith(checksum: any(named: "checksum")), + ).thenReturn(asset.copyWith(checksum: base64.encode(hash))); when(() => mockAsset.local).thenAnswer((_) => mockAssetEntity); when(() => mockAssetEntity.originFile).thenAnswer((_) async => file); diff --git a/mobile/test/test_utils.dart b/mobile/test/test_utils.dart index 596d3bcd1c..d932e2ffc7 100644 --- a/mobile/test/test_utils.dart +++ b/mobile/test/test_utils.dart @@ -73,11 +73,7 @@ abstract final class TestUtils { List overrides = const [], List? observers, }) { - final container = ProviderContainer( - parent: parent, - overrides: overrides, - observers: observers, - ); + final container = ProviderContainer(parent: parent, overrides: overrides, observers: observers); // Dispose on test end addTearDown(container.dispose); @@ -94,23 +90,22 @@ abstract final class TestUtils { // Workaround till the following issue is resolved // https://github.com/dart-lang/test/issues/2307 - static T fakeAsync( - Future Function(FakeAsync _) callback, { - DateTime? initialTime, - }) { + static T fakeAsync(Future Function(FakeAsync _) callback, {DateTime? initialTime}) { late final T result; Object? error; StackTrace? stack; FakeAsync(initialTime: initialTime).run((FakeAsync async) { bool shouldPump = true; unawaited( - callback(async).then( - (value) => result = value, - onError: (e, s) { - error = e; - stack = s; - }, - ).whenComplete(() => shouldPump = false), + callback(async) + .then( + (value) => result = value, + onError: (e, s) { + error = e; + stack = s; + }, + ) + .whenComplete(() => shouldPump = false), ); while (shouldPump) { diff --git a/mobile/test/test_utils/medium_factory.dart b/mobile/test/test_utils/medium_factory.dart index 8dafc564c1..19ad7166c6 100644 --- a/mobile/test/test_utils/medium_factory.dart +++ b/mobile/test/test_utils/medium_factory.dart @@ -25,10 +25,8 @@ class MediumFactory { name: name ?? 'Asset ${random.nextInt(1000000)}', checksum: checksum ?? '${random.nextInt(1000000)}', type: type ?? AssetType.image, - createdAt: createdAt ?? - DateTime.fromMillisecondsSinceEpoch(random.nextInt(1000000000)), - updatedAt: updatedAt ?? - DateTime.fromMillisecondsSinceEpoch(random.nextInt(1000000000)), + createdAt: createdAt ?? DateTime.fromMillisecondsSinceEpoch(random.nextInt(1000000000)), + updatedAt: updatedAt ?? DateTime.fromMillisecondsSinceEpoch(random.nextInt(1000000000)), ); } @@ -45,8 +43,7 @@ class MediumFactory { return LocalAlbum( id: id ?? '${random.nextInt(1000000)}', name: name ?? 'Album ${random.nextInt(1000000)}', - updatedAt: updatedAt ?? - DateTime.fromMillisecondsSinceEpoch(random.nextInt(1000000000)), + updatedAt: updatedAt ?? DateTime.fromMillisecondsSinceEpoch(random.nextInt(1000000000)), assetCount: assetCount ?? random.nextInt(100), backupSelection: backupSelection ?? BackupSelection.none, isIosSharedAlbum: isIosSharedAlbum ?? false, diff --git a/mobile/test/widget_tester_extensions.dart b/mobile/test/widget_tester_extensions.dart index 7d5b266224..bb3fc3f418 100644 --- a/mobile/test/widget_tester_extensions.dart +++ b/mobile/test/widget_tester_extensions.dart @@ -18,10 +18,7 @@ extension PumpConsumerWidget on WidgetTester { return pumpWidget( ProviderScope( overrides: overrides, - child: MaterialApp( - debugShowCheckedModeBanner: false, - home: Material(child: widget), - ), + child: MaterialApp(debugShowCheckedModeBanner: false, home: Material(child: widget)), ), duration: duration, phase: phase, diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 71329c3f75..d97585a39e 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -77,7 +77,9 @@ ], "tags": [ "Activities" - ] + ], + "x-immich-permission": "activity.read", + "description": "This endpoint requires the `activity.read` permission." }, "post": { "operationId": "createActivity", @@ -117,7 +119,9 @@ ], "tags": [ "Activities" - ] + ], + "x-immich-permission": "activity.create", + "description": "This endpoint requires the `activity.create` permission." } }, "/activities/statistics": { @@ -168,7 +172,9 @@ ], "tags": [ "Activities" - ] + ], + "x-immich-permission": "activity.statistics", + "description": "This endpoint requires the `activity.statistics` permission." } }, "/activities/{id}": { @@ -203,7 +209,9 @@ ], "tags": [ "Activities" - ] + ], + "x-immich-permission": "activity.delete", + "description": "This endpoint requires the `activity.delete` permission." } }, "/admin/notifications": { @@ -245,7 +253,8 @@ ], "tags": [ "Notifications (Admin)" - ] + ], + "x-immich-admin-only": true } }, "/admin/notifications/templates/{name}": { @@ -296,7 +305,8 @@ ], "tags": [ "Notifications (Admin)" - ] + ], + "x-immich-admin-only": true } }, "/admin/notifications/test-email": { @@ -338,7 +348,8 @@ ], "tags": [ "Notifications (Admin)" - ] + ], + "x-immich-admin-only": true } }, "/admin/users": { @@ -391,7 +402,10 @@ ], "tags": [ "Users (admin)" - ] + ], + "x-immich-admin-only": true, + "x-immich-permission": "adminUser.read", + "description": "This endpoint is an admin-only route, and requires the `adminUser.read` permission." }, "post": { "operationId": "createUserAdmin", @@ -431,7 +445,10 @@ ], "tags": [ "Users (admin)" - ] + ], + "x-immich-admin-only": true, + "x-immich-permission": "adminUser.create", + "description": "This endpoint is an admin-only route, and requires the `adminUser.create` permission." } }, "/admin/users/{id}": { @@ -483,7 +500,10 @@ ], "tags": [ "Users (admin)" - ] + ], + "x-immich-admin-only": true, + "x-immich-permission": "adminUser.delete", + "description": "This endpoint is an admin-only route, and requires the `adminUser.delete` permission." }, "get": { "operationId": "getUserAdmin", @@ -523,7 +543,10 @@ ], "tags": [ "Users (admin)" - ] + ], + "x-immich-admin-only": true, + "x-immich-permission": "adminUser.read", + "description": "This endpoint is an admin-only route, and requires the `adminUser.read` permission." }, "put": { "operationId": "updateUserAdmin", @@ -573,7 +596,10 @@ ], "tags": [ "Users (admin)" - ] + ], + "x-immich-admin-only": true, + "x-immich-permission": "adminUser.update", + "description": "This endpoint is an admin-only route, and requires the `adminUser.update` permission." } }, "/admin/users/{id}/preferences": { @@ -615,7 +641,10 @@ ], "tags": [ "Users (admin)" - ] + ], + "x-immich-admin-only": true, + "x-immich-permission": "adminUser.read", + "description": "This endpoint is an admin-only route, and requires the `adminUser.read` permission." }, "put": { "operationId": "updateUserPreferencesAdmin", @@ -665,7 +694,10 @@ ], "tags": [ "Users (admin)" - ] + ], + "x-immich-admin-only": true, + "x-immich-permission": "adminUser.update", + "description": "This endpoint is an admin-only route, and requires the `adminUser.update` permission." } }, "/admin/users/{id}/restore": { @@ -707,7 +739,10 @@ ], "tags": [ "Users (admin)" - ] + ], + "x-immich-admin-only": true, + "x-immich-permission": "adminUser.delete", + "description": "This endpoint is an admin-only route, and requires the `adminUser.delete` permission." } }, "/admin/users/{id}/statistics": { @@ -773,7 +808,10 @@ ], "tags": [ "Users (admin)" - ] + ], + "x-immich-admin-only": true, + "x-immich-permission": "adminUser.read", + "description": "This endpoint is an admin-only route, and requires the `adminUser.read` permission." } }, "/albums": { @@ -827,7 +865,9 @@ ], "tags": [ "Albums" - ] + ], + "x-immich-permission": "album.read", + "description": "This endpoint requires the `album.read` permission." }, "post": { "operationId": "createAlbum", @@ -867,7 +907,9 @@ ], "tags": [ "Albums" - ] + ], + "x-immich-permission": "album.create", + "description": "This endpoint requires the `album.create` permission." } }, "/albums/statistics": { @@ -899,7 +941,9 @@ ], "tags": [ "Albums" - ] + ], + "x-immich-permission": "album.statistics", + "description": "This endpoint requires the `album.statistics` permission." } }, "/albums/{id}": { @@ -934,7 +978,9 @@ ], "tags": [ "Albums" - ] + ], + "x-immich-permission": "album.delete", + "description": "This endpoint requires the `album.delete` permission." }, "get": { "operationId": "getAlbumInfo", @@ -956,6 +1002,14 @@ "type": "string" } }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, { "name": "withoutAssets", "required": false, @@ -990,7 +1044,9 @@ ], "tags": [ "Albums" - ] + ], + "x-immich-permission": "album.read", + "description": "This endpoint requires the `album.read` permission." }, "patch": { "operationId": "updateAlbumInfo", @@ -1040,7 +1096,9 @@ ], "tags": [ "Albums" - ] + ], + "x-immich-permission": "album.update", + "description": "This endpoint requires the `album.update` permission." } }, "/albums/{id}/assets": { @@ -1095,7 +1153,9 @@ ], "tags": [ "Albums" - ] + ], + "x-immich-permission": "albumAsset.delete", + "description": "This endpoint requires the `albumAsset.delete` permission." }, "put": { "operationId": "addAssetsToAlbum", @@ -1116,6 +1176,14 @@ "schema": { "type": "string" } + }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } } ], "requestBody": { @@ -1156,7 +1224,9 @@ ], "tags": [ "Albums" - ] + ], + "x-immich-permission": "albumAsset.create", + "description": "This endpoint requires the `albumAsset.create` permission." } }, "/albums/{id}/user/{userId}": { @@ -1199,7 +1269,9 @@ ], "tags": [ "Albums" - ] + ], + "x-immich-permission": "albumUser.delete", + "description": "This endpoint requires the `albumUser.delete` permission." }, "put": { "operationId": "updateAlbumUser", @@ -1250,7 +1322,9 @@ ], "tags": [ "Albums" - ] + ], + "x-immich-permission": "albumUser.update", + "description": "This endpoint requires the `albumUser.update` permission." } }, "/albums/{id}/users": { @@ -1302,7 +1376,9 @@ ], "tags": [ "Albums" - ] + ], + "x-immich-permission": "albumUser.create", + "description": "This endpoint requires the `albumUser.create` permission." } }, "/api-keys": { @@ -1337,7 +1413,9 @@ ], "tags": [ "API Keys" - ] + ], + "x-immich-permission": "apiKey.read", + "description": "This endpoint requires the `apiKey.read` permission." }, "post": { "operationId": "createApiKey", @@ -1377,7 +1455,9 @@ ], "tags": [ "API Keys" - ] + ], + "x-immich-permission": "apiKey.create", + "description": "This endpoint requires the `apiKey.create` permission." } }, "/api-keys/{id}": { @@ -1412,7 +1492,9 @@ ], "tags": [ "API Keys" - ] + ], + "x-immich-permission": "apiKey.delete", + "description": "This endpoint requires the `apiKey.delete` permission." }, "get": { "operationId": "getApiKey", @@ -1452,7 +1534,9 @@ ], "tags": [ "API Keys" - ] + ], + "x-immich-permission": "apiKey.read", + "description": "This endpoint requires the `apiKey.read` permission." }, "put": { "operationId": "updateApiKey", @@ -1502,7 +1586,9 @@ ], "tags": [ "API Keys" - ] + ], + "x-immich-permission": "apiKey.update", + "description": "This endpoint requires the `apiKey.update` permission." } }, "/assets": { @@ -1537,7 +1623,9 @@ ], "tags": [ "Assets" - ] + ], + "x-immich-permission": "asset.delete", + "description": "This endpoint requires the `asset.delete` permission." }, "post": { "operationId": "uploadAsset", @@ -1550,6 +1638,14 @@ "type": "string" } }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, { "name": "x-immich-checksum", "in": "header", @@ -1596,7 +1692,9 @@ ], "tags": [ "Assets" - ] + ], + "x-immich-permission": "asset.upload", + "description": "This endpoint requires the `asset.upload` permission." }, "put": { "operationId": "updateAssets", @@ -1629,7 +1727,9 @@ ], "tags": [ "Assets" - ] + ], + "x-immich-permission": "asset.update", + "description": "This endpoint requires the `asset.update` permission." } }, "/assets/bulk-upload-check": { @@ -1804,7 +1904,7 @@ "/assets/random": { "get": { "deprecated": true, - "description": "This property was deprecated in v1.116.0", + "description": "This property was deprecated in v1.116.0. This endpoint requires the `asset.read` permission.", "operationId": "getRandom", "parameters": [ { @@ -1849,7 +1949,8 @@ ], "x-immich-lifecycle": { "deprecatedAt": "v1.116.0" - } + }, + "x-immich-permission": "asset.read" } }, "/assets/statistics": { @@ -1906,7 +2007,9 @@ ], "tags": [ "Assets" - ] + ], + "x-immich-permission": "asset.statistics", + "description": "This endpoint requires the `asset.statistics` permission." } }, "/assets/{id}": { @@ -1929,6 +2032,14 @@ "schema": { "type": "string" } + }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } } ], "responses": { @@ -1956,7 +2067,9 @@ ], "tags": [ "Assets" - ] + ], + "x-immich-permission": "asset.read", + "description": "This endpoint requires the `asset.read` permission." }, "put": { "operationId": "updateAsset", @@ -2006,7 +2119,9 @@ ], "tags": [ "Assets" - ] + ], + "x-immich-permission": "asset.update", + "description": "This endpoint requires the `asset.update` permission." } }, "/assets/{id}/original": { @@ -2029,6 +2144,14 @@ "schema": { "type": "string" } + }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } } ], "responses": { @@ -2057,10 +2180,12 @@ ], "tags": [ "Assets" - ] + ], + "x-immich-permission": "asset.download", + "description": "This endpoint requires the `asset.download` permission." }, "put": { - "description": "Replace the asset with new file, without changing its id", + "description": "Replace the asset with new file, without changing its id. This endpoint requires the `asset.replace` permission.", "operationId": "replaceAsset", "parameters": [ { @@ -2079,6 +2204,14 @@ "schema": { "type": "string" } + }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } } ], "requestBody": { @@ -2120,7 +2253,8 @@ ], "x-immich-lifecycle": { "addedAt": "v1.106.0" - } + }, + "x-immich-permission": "asset.replace" } }, "/assets/{id}/thumbnail": { @@ -2151,52 +2285,9 @@ "schema": { "$ref": "#/components/schemas/AssetMediaSize" } - } - ], - "responses": { - "200": { - "content": { - "application/octet-stream": { - "schema": { - "format": "binary", - "type": "string" - } - } - }, - "description": "" - } - }, - "security": [ - { - "bearer": [] }, { - "cookie": [] - }, - { - "api_key": [] - } - ], - "tags": [ - "Assets" - ] - } - }, - "/assets/{id}/video/playback": { - "get": { - "operationId": "playAssetVideo", - "parameters": [ - { - "name": "id", - "required": true, - "in": "path", - "schema": { - "format": "uuid", - "type": "string" - } - }, - { - "name": "key", + "name": "slug", "required": false, "in": "query", "schema": { @@ -2230,7 +2321,70 @@ ], "tags": [ "Assets" - ] + ], + "x-immich-permission": "asset.view", + "description": "This endpoint requires the `asset.view` permission." + } + }, + "/assets/{id}/video/playback": { + "get": { + "operationId": "playAssetVideo", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "format": "uuid", + "type": "string" + } + }, + { + "name": "key", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/octet-stream": { + "schema": { + "format": "binary", + "type": "string" + } + } + }, + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "tags": [ + "Assets" + ], + "x-immich-permission": "asset.view", + "description": "This endpoint requires the `asset.view` permission." } }, "/auth/admin-sign-up": { @@ -2303,7 +2457,9 @@ ], "tags": [ "Authentication" - ] + ], + "x-immich-permission": "auth.changePassword", + "description": "This endpoint requires the `auth.changePassword` permission." } }, "/auth/login": { @@ -2401,7 +2557,9 @@ ], "tags": [ "Authentication" - ] + ], + "x-immich-permission": "pinCode.delete", + "description": "This endpoint requires the `pinCode.delete` permission." }, "post": { "operationId": "setupPinCode", @@ -2434,7 +2592,9 @@ ], "tags": [ "Authentication" - ] + ], + "x-immich-permission": "pinCode.create", + "description": "This endpoint requires the `pinCode.create` permission." }, "put": { "operationId": "changePinCode", @@ -2467,7 +2627,9 @@ ], "tags": [ "Authentication" - ] + ], + "x-immich-permission": "pinCode.update", + "description": "This endpoint requires the `pinCode.update` permission." } }, "/auth/session/lock": { @@ -2605,6 +2767,14 @@ "schema": { "type": "string" } + }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } } ], "requestBody": { @@ -2643,7 +2813,9 @@ ], "tags": [ "Download" - ] + ], + "x-immich-permission": "asset.download", + "description": "This endpoint requires the `asset.download` permission." } }, "/download/info": { @@ -2657,6 +2829,14 @@ "schema": { "type": "string" } + }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } } ], "requestBody": { @@ -2694,7 +2874,9 @@ ], "tags": [ "Download" - ] + ], + "x-immich-permission": "asset.download", + "description": "This endpoint requires the `asset.download` permission." } }, "/duplicates": { @@ -2729,7 +2911,9 @@ ], "tags": [ "Duplicates" - ] + ], + "x-immich-permission": "duplicate.delete", + "description": "This endpoint requires the `duplicate.delete` permission." }, "get": { "operationId": "getAssetDuplicates", @@ -2762,7 +2946,9 @@ ], "tags": [ "Duplicates" - ] + ], + "x-immich-permission": "duplicate.read", + "description": "This endpoint requires the `duplicate.read` permission." } }, "/duplicates/{id}": { @@ -2797,7 +2983,9 @@ ], "tags": [ "Duplicates" - ] + ], + "x-immich-permission": "duplicate.delete", + "description": "This endpoint requires the `duplicate.delete` permission." } }, "/faces": { @@ -2842,7 +3030,9 @@ ], "tags": [ "Faces" - ] + ], + "x-immich-permission": "face.read", + "description": "This endpoint requires the `face.read` permission." }, "post": { "operationId": "createFace", @@ -2875,7 +3065,9 @@ ], "tags": [ "Faces" - ] + ], + "x-immich-permission": "face.create", + "description": "This endpoint requires the `face.create` permission." } }, "/faces/{id}": { @@ -2920,7 +3112,9 @@ ], "tags": [ "Faces" - ] + ], + "x-immich-permission": "face.delete", + "description": "This endpoint requires the `face.delete` permission." }, "put": { "operationId": "reassignFacesById", @@ -2970,7 +3164,9 @@ ], "tags": [ "Faces" - ] + ], + "x-immich-permission": "face.update", + "description": "This endpoint requires the `face.update` permission." } }, "/jobs": { @@ -3002,7 +3198,10 @@ ], "tags": [ "Jobs" - ] + ], + "x-immich-admin-only": true, + "x-immich-permission": "job.read", + "description": "This endpoint is an admin-only route, and requires the `job.read` permission." }, "post": { "operationId": "createJob", @@ -3035,7 +3234,10 @@ ], "tags": [ "Jobs" - ] + ], + "x-immich-admin-only": true, + "x-immich-permission": "job.create", + "description": "This endpoint is an admin-only route, and requires the `job.create` permission." } }, "/jobs/{id}": { @@ -3086,7 +3288,10 @@ ], "tags": [ "Jobs" - ] + ], + "x-immich-admin-only": true, + "x-immich-permission": "job.create", + "description": "This endpoint is an admin-only route, and requires the `job.create` permission." } }, "/libraries": { @@ -3121,7 +3326,10 @@ ], "tags": [ "Libraries" - ] + ], + "x-immich-admin-only": true, + "x-immich-permission": "library.read", + "description": "This endpoint is an admin-only route, and requires the `library.read` permission." }, "post": { "operationId": "createLibrary", @@ -3161,7 +3369,10 @@ ], "tags": [ "Libraries" - ] + ], + "x-immich-admin-only": true, + "x-immich-permission": "library.create", + "description": "This endpoint is an admin-only route, and requires the `library.create` permission." } }, "/libraries/{id}": { @@ -3196,7 +3407,10 @@ ], "tags": [ "Libraries" - ] + ], + "x-immich-admin-only": true, + "x-immich-permission": "library.delete", + "description": "This endpoint is an admin-only route, and requires the `library.delete` permission." }, "get": { "operationId": "getLibrary", @@ -3236,7 +3450,10 @@ ], "tags": [ "Libraries" - ] + ], + "x-immich-admin-only": true, + "x-immich-permission": "library.read", + "description": "This endpoint is an admin-only route, and requires the `library.read` permission." }, "put": { "operationId": "updateLibrary", @@ -3286,7 +3503,10 @@ ], "tags": [ "Libraries" - ] + ], + "x-immich-admin-only": true, + "x-immich-permission": "library.update", + "description": "This endpoint is an admin-only route, and requires the `library.update` permission." } }, "/libraries/{id}/scan": { @@ -3321,7 +3541,10 @@ ], "tags": [ "Libraries" - ] + ], + "x-immich-admin-only": true, + "x-immich-permission": "library.update", + "description": "This endpoint is an admin-only route, and requires the `library.update` permission." } }, "/libraries/{id}/statistics": { @@ -3363,7 +3586,10 @@ ], "tags": [ "Libraries" - ] + ], + "x-immich-admin-only": true, + "x-immich-permission": "library.statistics", + "description": "This endpoint is an admin-only route, and requires the `library.statistics` permission." } }, "/libraries/{id}/validate": { @@ -3415,13 +3641,30 @@ ], "tags": [ "Libraries" - ] + ], + "x-immich-admin-only": true } }, "/map/markers": { "get": { "operationId": "getMapMarkers", "parameters": [ + { + "name": "isArchived", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "isFavorite", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, { "name": "fileCreatedAfter", "required": false, @@ -3440,22 +3683,6 @@ "type": "string" } }, - { - "name": "isArchived", - "required": false, - "in": "query", - "schema": { - "type": "boolean" - } - }, - { - "name": "isFavorite", - "required": false, - "in": "query", - "schema": { - "type": "boolean" - } - }, { "name": "withPartners", "required": false, @@ -3624,7 +3851,9 @@ ], "tags": [ "Memories" - ] + ], + "x-immich-permission": "memory.read", + "description": "This endpoint requires the `memory.read` permission." }, "post": { "operationId": "createMemory", @@ -3664,7 +3893,9 @@ ], "tags": [ "Memories" - ] + ], + "x-immich-permission": "memory.create", + "description": "This endpoint requires the `memory.create` permission." } }, "/memories/statistics": { @@ -3730,7 +3961,9 @@ ], "tags": [ "Memories" - ] + ], + "x-immich-permission": "memory.statistics", + "description": "This endpoint requires the `memory.statistics` permission." } }, "/memories/{id}": { @@ -3765,7 +3998,9 @@ ], "tags": [ "Memories" - ] + ], + "x-immich-permission": "memory.delete", + "description": "This endpoint requires the `memory.delete` permission." }, "get": { "operationId": "getMemory", @@ -3805,7 +4040,9 @@ ], "tags": [ "Memories" - ] + ], + "x-immich-permission": "memory.read", + "description": "This endpoint requires the `memory.read` permission." }, "put": { "operationId": "updateMemory", @@ -3855,7 +4092,9 @@ ], "tags": [ "Memories" - ] + ], + "x-immich-permission": "memory.update", + "description": "This endpoint requires the `memory.update` permission." } }, "/memories/{id}/assets": { @@ -3910,7 +4149,9 @@ ], "tags": [ "Memories" - ] + ], + "x-immich-permission": "memoryAsset.delete", + "description": "This endpoint requires the `memoryAsset.delete` permission." }, "put": { "operationId": "addMemoryAssets", @@ -3963,7 +4204,9 @@ ], "tags": [ "Memories" - ] + ], + "x-immich-permission": "memoryAsset.create", + "description": "This endpoint requires the `memoryAsset.create` permission." } }, "/notifications": { @@ -3998,7 +4241,9 @@ ], "tags": [ "Notifications" - ] + ], + "x-immich-permission": "notification.delete", + "description": "This endpoint requires the `notification.delete` permission." }, "get": { "operationId": "getNotifications", @@ -4065,7 +4310,9 @@ ], "tags": [ "Notifications" - ] + ], + "x-immich-permission": "notification.read", + "description": "This endpoint requires the `notification.read` permission." }, "put": { "operationId": "updateNotifications", @@ -4098,7 +4345,9 @@ ], "tags": [ "Notifications" - ] + ], + "x-immich-permission": "notification.update", + "description": "This endpoint requires the `notification.update` permission." } }, "/notifications/{id}": { @@ -4133,7 +4382,9 @@ ], "tags": [ "Notifications" - ] + ], + "x-immich-permission": "notification.delete", + "description": "This endpoint requires the `notification.delete` permission." }, "get": { "operationId": "getNotification", @@ -4173,7 +4424,9 @@ ], "tags": [ "Notifications" - ] + ], + "x-immich-permission": "notification.read", + "description": "This endpoint requires the `notification.read` permission." }, "put": { "operationId": "updateNotification", @@ -4223,7 +4476,9 @@ ], "tags": [ "Notifications" - ] + ], + "x-immich-permission": "notification.update", + "description": "This endpoint requires the `notification.update` permission." } }, "/oauth/authorize": { @@ -4417,7 +4672,9 @@ ], "tags": [ "Partners" - ] + ], + "x-immich-permission": "partner.read", + "description": "This endpoint requires the `partner.read` permission." } }, "/partners/{id}": { @@ -4452,7 +4709,9 @@ ], "tags": [ "Partners" - ] + ], + "x-immich-permission": "partner.delete", + "description": "This endpoint requires the `partner.delete` permission." }, "post": { "operationId": "createPartner", @@ -4492,7 +4751,9 @@ ], "tags": [ "Partners" - ] + ], + "x-immich-permission": "partner.create", + "description": "This endpoint requires the `partner.create` permission." }, "put": { "operationId": "updatePartner", @@ -4542,7 +4803,9 @@ ], "tags": [ "Partners" - ] + ], + "x-immich-permission": "partner.update", + "description": "This endpoint requires the `partner.update` permission." } }, "/people": { @@ -4577,7 +4840,9 @@ ], "tags": [ "People" - ] + ], + "x-immich-permission": "person.delete", + "description": "This endpoint requires the `person.delete` permission." }, "get": { "operationId": "getAllPeople", @@ -4657,7 +4922,9 @@ ], "tags": [ "People" - ] + ], + "x-immich-permission": "person.read", + "description": "This endpoint requires the `person.read` permission." }, "post": { "operationId": "createPerson", @@ -4697,7 +4964,9 @@ ], "tags": [ "People" - ] + ], + "x-immich-permission": "person.create", + "description": "This endpoint requires the `person.create` permission." }, "put": { "operationId": "updatePeople", @@ -4740,7 +5009,9 @@ ], "tags": [ "People" - ] + ], + "x-immich-permission": "person.update", + "description": "This endpoint requires the `person.update` permission." } }, "/people/{id}": { @@ -4775,7 +5046,9 @@ ], "tags": [ "People" - ] + ], + "x-immich-permission": "person.delete", + "description": "This endpoint requires the `person.delete` permission." }, "get": { "operationId": "getPerson", @@ -4815,7 +5088,9 @@ ], "tags": [ "People" - ] + ], + "x-immich-permission": "person.read", + "description": "This endpoint requires the `person.read` permission." }, "put": { "operationId": "updatePerson", @@ -4865,7 +5140,9 @@ ], "tags": [ "People" - ] + ], + "x-immich-permission": "person.update", + "description": "This endpoint requires the `person.update` permission." } }, "/people/{id}/merge": { @@ -4920,7 +5197,9 @@ ], "tags": [ "People" - ] + ], + "x-immich-permission": "person.merge", + "description": "This endpoint requires the `person.merge` permission." } }, "/people/{id}/reassign": { @@ -4975,7 +5254,9 @@ ], "tags": [ "People" - ] + ], + "x-immich-permission": "person.reassign", + "description": "This endpoint requires the `person.reassign` permission." } }, "/people/{id}/statistics": { @@ -5017,7 +5298,9 @@ ], "tags": [ "People" - ] + ], + "x-immich-permission": "person.statistics", + "description": "This endpoint requires the `person.statistics` permission." } }, "/people/{id}/thumbnail": { @@ -5060,7 +5343,9 @@ ], "tags": [ "People" - ] + ], + "x-immich-permission": "person.read", + "description": "This endpoint requires the `person.read` permission." } }, "/search/cities": { @@ -5095,7 +5380,9 @@ ], "tags": [ "Search" - ] + ], + "x-immich-permission": "asset.read", + "description": "This endpoint requires the `asset.read` permission." } }, "/search/explore": { @@ -5130,7 +5417,328 @@ ], "tags": [ "Search" - ] + ], + "x-immich-permission": "asset.read", + "description": "This endpoint requires the `asset.read` permission." + } + }, + "/search/large-assets": { + "post": { + "operationId": "searchLargeAssets", + "parameters": [ + { + "name": "albumIds", + "required": false, + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + }, + { + "name": "city", + "required": false, + "in": "query", + "schema": { + "nullable": true, + "type": "string" + } + }, + { + "name": "country", + "required": false, + "in": "query", + "schema": { + "nullable": true, + "type": "string" + } + }, + { + "name": "createdAfter", + "required": false, + "in": "query", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "name": "createdBefore", + "required": false, + "in": "query", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "name": "deviceId", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "isEncoded", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "isFavorite", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "isMotion", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "isNotInAlbum", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "isOffline", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "lensModel", + "required": false, + "in": "query", + "schema": { + "nullable": true, + "type": "string" + } + }, + { + "name": "libraryId", + "required": false, + "in": "query", + "schema": { + "format": "uuid", + "nullable": true, + "type": "string" + } + }, + { + "name": "make", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "minFileSize", + "required": false, + "in": "query", + "schema": { + "minimum": 0, + "type": "integer" + } + }, + { + "name": "model", + "required": false, + "in": "query", + "schema": { + "nullable": true, + "type": "string" + } + }, + { + "name": "personIds", + "required": false, + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + }, + { + "name": "rating", + "required": false, + "in": "query", + "schema": { + "minimum": -1, + "maximum": 5, + "type": "number" + } + }, + { + "name": "size", + "required": false, + "in": "query", + "schema": { + "minimum": 1, + "maximum": 1000, + "type": "number" + } + }, + { + "name": "state", + "required": false, + "in": "query", + "schema": { + "nullable": true, + "type": "string" + } + }, + { + "name": "tagIds", + "required": false, + "in": "query", + "schema": { + "nullable": true, + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + }, + { + "name": "takenAfter", + "required": false, + "in": "query", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "name": "takenBefore", + "required": false, + "in": "query", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "name": "trashedAfter", + "required": false, + "in": "query", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "name": "trashedBefore", + "required": false, + "in": "query", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "name": "type", + "required": false, + "in": "query", + "schema": { + "$ref": "#/components/schemas/AssetTypeEnum" + } + }, + { + "name": "updatedAfter", + "required": false, + "in": "query", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "name": "updatedBefore", + "required": false, + "in": "query", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "name": "visibility", + "required": false, + "in": "query", + "schema": { + "$ref": "#/components/schemas/AssetVisibility" + } + }, + { + "name": "withDeleted", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + }, + { + "name": "withExif", + "required": false, + "in": "query", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/AssetResponseDto" + }, + "type": "array" + } + } + }, + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "tags": [ + "Search" + ], + "x-immich-permission": "asset.read", + "description": "This endpoint requires the `asset.read` permission." } }, "/search/metadata": { @@ -5172,7 +5780,9 @@ ], "tags": [ "Search" - ] + ], + "x-immich-permission": "asset.read", + "description": "This endpoint requires the `asset.read` permission." } }, "/search/person": { @@ -5224,7 +5834,9 @@ ], "tags": [ "Search" - ] + ], + "x-immich-permission": "person.read", + "description": "This endpoint requires the `person.read` permission." } }, "/search/places": { @@ -5268,7 +5880,9 @@ ], "tags": [ "Search" - ] + ], + "x-immich-permission": "asset.read", + "description": "This endpoint requires the `asset.read` permission." } }, "/search/random": { @@ -5313,7 +5927,9 @@ ], "tags": [ "Search" - ] + ], + "x-immich-permission": "asset.read", + "description": "This endpoint requires the `asset.read` permission." } }, "/search/smart": { @@ -5355,7 +5971,9 @@ ], "tags": [ "Search" - ] + ], + "x-immich-permission": "asset.read", + "description": "This endpoint requires the `asset.read` permission." } }, "/search/statistics": { @@ -5397,7 +6015,9 @@ ], "tags": [ "Search" - ] + ], + "x-immich-permission": "asset.statistics", + "description": "This endpoint requires the `asset.statistics` permission." } }, "/search/suggestions": { @@ -5482,7 +6102,9 @@ ], "tags": [ "Search" - ] + ], + "x-immich-permission": "asset.read", + "description": "This endpoint requires the `asset.read` permission." } }, "/server/about": { @@ -5514,7 +6136,9 @@ ], "tags": [ "Server" - ] + ], + "x-immich-permission": "server.about", + "description": "This endpoint requires the `server.about` permission." } }, "/server/apk-links": { @@ -5546,7 +6170,9 @@ ], "tags": [ "Server" - ] + ], + "x-immich-permission": "server.apkLinks", + "description": "This endpoint requires the `server.apkLinks` permission." } }, "/server/config": { @@ -5613,7 +6239,10 @@ ], "tags": [ "Server" - ] + ], + "x-immich-admin-only": true, + "x-immich-permission": "serverLicense.delete", + "description": "This endpoint is an admin-only route, and requires the `serverLicense.delete` permission." }, "get": { "operationId": "getServerLicense", @@ -5646,7 +6275,10 @@ ], "tags": [ "Server" - ] + ], + "x-immich-admin-only": true, + "x-immich-permission": "serverLicense.read", + "description": "This endpoint is an admin-only route, and requires the `serverLicense.read` permission." }, "put": { "operationId": "setServerLicense", @@ -5686,7 +6318,10 @@ ], "tags": [ "Server" - ] + ], + "x-immich-admin-only": true, + "x-immich-permission": "serverLicense.update", + "description": "This endpoint is an admin-only route, and requires the `serverLicense.update` permission." } }, "/server/media-types": { @@ -5760,7 +6395,10 @@ ], "tags": [ "Server" - ] + ], + "x-immich-admin-only": true, + "x-immich-permission": "server.statistics", + "description": "This endpoint is an admin-only route, and requires the `server.statistics` permission." } }, "/server/storage": { @@ -5792,7 +6430,9 @@ ], "tags": [ "Server" - ] + ], + "x-immich-permission": "server.storage", + "description": "This endpoint requires the `server.storage` permission." } }, "/server/theme": { @@ -5915,7 +6555,9 @@ ], "tags": [ "Sessions" - ] + ], + "x-immich-permission": "session.delete", + "description": "This endpoint requires the `session.delete` permission." }, "get": { "operationId": "getSessions", @@ -5948,7 +6590,9 @@ ], "tags": [ "Sessions" - ] + ], + "x-immich-permission": "session.read", + "description": "This endpoint requires the `session.read` permission." }, "post": { "operationId": "createSession", @@ -5988,7 +6632,9 @@ ], "tags": [ "Sessions" - ] + ], + "x-immich-permission": "session.create", + "description": "This endpoint requires the `session.create` permission." } }, "/sessions/{id}": { @@ -6023,7 +6669,9 @@ ], "tags": [ "Sessions" - ] + ], + "x-immich-permission": "session.delete", + "description": "This endpoint requires the `session.delete` permission." }, "put": { "operationId": "updateSession", @@ -6073,7 +6721,9 @@ ], "tags": [ "Sessions" - ] + ], + "x-immich-permission": "session.update", + "description": "This endpoint requires the `session.update` permission." } }, "/sessions/{id}/lock": { @@ -6108,7 +6758,9 @@ ], "tags": [ "Sessions" - ] + ], + "x-immich-permission": "session.lock", + "description": "This endpoint requires the `session.lock` permission." } }, "/shared-links": { @@ -6153,7 +6805,9 @@ ], "tags": [ "Shared Links" - ] + ], + "x-immich-permission": "sharedLink.read", + "description": "This endpoint requires the `sharedLink.read` permission." }, "post": { "operationId": "createSharedLink", @@ -6193,21 +6847,15 @@ ], "tags": [ "Shared Links" - ] + ], + "x-immich-permission": "sharedLink.create", + "description": "This endpoint requires the `sharedLink.create` permission." } }, "/shared-links/me": { "get": { "operationId": "getMySharedLink", "parameters": [ - { - "name": "key", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, { "name": "password", "required": false, @@ -6224,6 +6872,22 @@ "schema": { "type": "string" } + }, + { + "name": "key", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } } ], "responses": { @@ -6286,7 +6950,9 @@ ], "tags": [ "Shared Links" - ] + ], + "x-immich-permission": "sharedLink.delete", + "description": "This endpoint requires the `sharedLink.delete` permission." }, "get": { "operationId": "getSharedLinkById", @@ -6326,7 +6992,9 @@ ], "tags": [ "Shared Links" - ] + ], + "x-immich-permission": "sharedLink.read", + "description": "This endpoint requires the `sharedLink.read` permission." }, "patch": { "operationId": "updateSharedLink", @@ -6376,7 +7044,9 @@ ], "tags": [ "Shared Links" - ] + ], + "x-immich-permission": "sharedLink.update", + "description": "This endpoint requires the `sharedLink.update` permission." } }, "/shared-links/{id}/assets": { @@ -6399,6 +7069,14 @@ "schema": { "type": "string" } + }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } } ], "requestBody": { @@ -6460,6 +7138,14 @@ "schema": { "type": "string" } + }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } } ], "requestBody": { @@ -6535,7 +7221,9 @@ ], "tags": [ "Stacks" - ] + ], + "x-immich-permission": "stack.delete", + "description": "This endpoint requires the `stack.delete` permission." }, "get": { "operationId": "searchStacks", @@ -6578,7 +7266,9 @@ ], "tags": [ "Stacks" - ] + ], + "x-immich-permission": "stack.read", + "description": "This endpoint requires the `stack.read` permission." }, "post": { "operationId": "createStack", @@ -6618,7 +7308,9 @@ ], "tags": [ "Stacks" - ] + ], + "x-immich-permission": "stack.create", + "description": "This endpoint requires the `stack.create` permission." } }, "/stacks/{id}": { @@ -6653,7 +7345,9 @@ ], "tags": [ "Stacks" - ] + ], + "x-immich-permission": "stack.delete", + "description": "This endpoint requires the `stack.delete` permission." }, "get": { "operationId": "getStack", @@ -6693,7 +7387,9 @@ ], "tags": [ "Stacks" - ] + ], + "x-immich-permission": "stack.read", + "description": "This endpoint requires the `stack.read` permission." }, "put": { "operationId": "updateStack", @@ -6743,7 +7439,55 @@ ], "tags": [ "Stacks" - ] + ], + "x-immich-permission": "stack.update", + "description": "This endpoint requires the `stack.update` permission." + } + }, + "/stacks/{id}/assets/{assetId}": { + "delete": { + "operationId": "removeAssetFromStack", + "parameters": [ + { + "name": "assetId", + "required": true, + "in": "path", + "schema": { + "format": "uuid", + "type": "string" + } + }, + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "format": "uuid", + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "tags": [ + "Stacks" + ], + "x-immich-permission": "stack.update", + "description": "This endpoint requires the `stack.update` permission." } }, "/sync/ack": { @@ -6778,7 +7522,9 @@ ], "tags": [ "Sync" - ] + ], + "x-immich-permission": "syncCheckpoint.delete", + "description": "This endpoint requires the `syncCheckpoint.delete` permission." }, "get": { "operationId": "getSyncAck", @@ -6811,7 +7557,9 @@ ], "tags": [ "Sync" - ] + ], + "x-immich-permission": "syncCheckpoint.read", + "description": "This endpoint requires the `syncCheckpoint.read` permission." }, "post": { "operationId": "sendSyncAck", @@ -6844,7 +7592,9 @@ ], "tags": [ "Sync" - ] + ], + "x-immich-permission": "syncCheckpoint.update", + "description": "This endpoint requires the `syncCheckpoint.update` permission." } }, "/sync/delta-sync": { @@ -6966,7 +7716,9 @@ ], "tags": [ "Sync" - ] + ], + "x-immich-permission": "sync.stream", + "description": "This endpoint requires the `sync.stream` permission." } }, "/system-config": { @@ -6998,7 +7750,10 @@ ], "tags": [ "System Config" - ] + ], + "x-immich-admin-only": true, + "x-immich-permission": "systemConfig.read", + "description": "This endpoint is an admin-only route, and requires the `systemConfig.read` permission." }, "put": { "operationId": "updateConfig", @@ -7038,7 +7793,10 @@ ], "tags": [ "System Config" - ] + ], + "x-immich-admin-only": true, + "x-immich-permission": "systemConfig.update", + "description": "This endpoint is an admin-only route, and requires the `systemConfig.update` permission." } }, "/system-config/defaults": { @@ -7070,7 +7828,10 @@ ], "tags": [ "System Config" - ] + ], + "x-immich-admin-only": true, + "x-immich-permission": "systemConfig.read", + "description": "This endpoint is an admin-only route, and requires the `systemConfig.read` permission." } }, "/system-config/storage-template-options": { @@ -7102,7 +7863,10 @@ ], "tags": [ "System Config" - ] + ], + "x-immich-admin-only": true, + "x-immich-permission": "systemConfig.read", + "description": "This endpoint is an admin-only route, and requires the `systemConfig.read` permission." } }, "/system-metadata/admin-onboarding": { @@ -7134,7 +7898,10 @@ ], "tags": [ "System Metadata" - ] + ], + "x-immich-admin-only": true, + "x-immich-permission": "systemMetadata.read", + "description": "This endpoint is an admin-only route, and requires the `systemMetadata.read` permission." }, "post": { "operationId": "updateAdminOnboarding", @@ -7167,7 +7934,10 @@ ], "tags": [ "System Metadata" - ] + ], + "x-immich-admin-only": true, + "x-immich-permission": "systemMetadata.update", + "description": "This endpoint is an admin-only route, and requires the `systemMetadata.update` permission." } }, "/system-metadata/reverse-geocoding-state": { @@ -7199,7 +7969,10 @@ ], "tags": [ "System Metadata" - ] + ], + "x-immich-admin-only": true, + "x-immich-permission": "systemMetadata.read", + "description": "This endpoint is an admin-only route, and requires the `systemMetadata.read` permission." } }, "/system-metadata/version-check-state": { @@ -7231,7 +8004,10 @@ ], "tags": [ "System Metadata" - ] + ], + "x-immich-admin-only": true, + "x-immich-permission": "systemMetadata.read", + "description": "This endpoint is an admin-only route, and requires the `systemMetadata.read` permission." } }, "/tags": { @@ -7266,7 +8042,9 @@ ], "tags": [ "Tags" - ] + ], + "x-immich-permission": "tag.read", + "description": "This endpoint requires the `tag.read` permission." }, "post": { "operationId": "createTag", @@ -7306,7 +8084,9 @@ ], "tags": [ "Tags" - ] + ], + "x-immich-permission": "tag.create", + "description": "This endpoint requires the `tag.create` permission." }, "put": { "operationId": "upsertTags", @@ -7349,7 +8129,9 @@ ], "tags": [ "Tags" - ] + ], + "x-immich-permission": "tag.create", + "description": "This endpoint requires the `tag.create` permission." } }, "/tags/assets": { @@ -7391,7 +8173,9 @@ ], "tags": [ "Tags" - ] + ], + "x-immich-permission": "tag.asset", + "description": "This endpoint requires the `tag.asset` permission." } }, "/tags/{id}": { @@ -7426,7 +8210,9 @@ ], "tags": [ "Tags" - ] + ], + "x-immich-permission": "tag.delete", + "description": "This endpoint requires the `tag.delete` permission." }, "get": { "operationId": "getTagById", @@ -7466,7 +8252,9 @@ ], "tags": [ "Tags" - ] + ], + "x-immich-permission": "tag.read", + "description": "This endpoint requires the `tag.read` permission." }, "put": { "operationId": "updateTag", @@ -7516,7 +8304,9 @@ ], "tags": [ "Tags" - ] + ], + "x-immich-permission": "tag.update", + "description": "This endpoint requires the `tag.update` permission." } }, "/tags/{id}/assets": { @@ -7571,7 +8361,9 @@ ], "tags": [ "Tags" - ] + ], + "x-immich-permission": "tag.asset", + "description": "This endpoint requires the `tag.asset` permission." }, "put": { "operationId": "tagAssets", @@ -7624,7 +8416,9 @@ ], "tags": [ "Tags" - ] + ], + "x-immich-permission": "tag.asset", + "description": "This endpoint requires the `tag.asset` permission." } }, "/timeline/bucket": { @@ -7686,6 +8480,14 @@ "type": "string" } }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, { "name": "tagId", "required": false, @@ -7769,7 +8571,9 @@ ], "tags": [ "Timeline" - ] + ], + "x-immich-permission": "asset.read", + "description": "This endpoint requires the `asset.read` permission." } }, "/timeline/buckets": { @@ -7831,6 +8635,14 @@ "type": "string" } }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, { "name": "tagId", "required": false, @@ -7907,7 +8719,9 @@ ], "tags": [ "Timeline" - ] + ], + "x-immich-permission": "asset.read", + "description": "This endpoint requires the `asset.read` permission." } }, "/trash/empty": { @@ -7939,7 +8753,9 @@ ], "tags": [ "Trash" - ] + ], + "x-immich-permission": "asset.delete", + "description": "This endpoint requires the `asset.delete` permission." } }, "/trash/restore": { @@ -7971,7 +8787,9 @@ ], "tags": [ "Trash" - ] + ], + "x-immich-permission": "asset.delete", + "description": "This endpoint requires the `asset.delete` permission." } }, "/trash/restore/assets": { @@ -8013,7 +8831,9 @@ ], "tags": [ "Trash" - ] + ], + "x-immich-permission": "asset.delete", + "description": "This endpoint requires the `asset.delete` permission." } }, "/users": { @@ -8048,7 +8868,9 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "user.read", + "description": "This endpoint requires the `user.read` permission." } }, "/users/me": { @@ -8080,7 +8902,9 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "user.read", + "description": "This endpoint requires the `user.read` permission." }, "put": { "operationId": "updateMyUser", @@ -8120,7 +8944,9 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "user.update", + "description": "This endpoint requires the `user.update` permission." } }, "/users/me/license": { @@ -8145,7 +8971,9 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "userLicense.delete", + "description": "This endpoint requires the `userLicense.delete` permission." }, "get": { "operationId": "getUserLicense", @@ -8175,7 +9003,9 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "userLicense.read", + "description": "This endpoint requires the `userLicense.read` permission." }, "put": { "operationId": "setUserLicense", @@ -8215,7 +9045,9 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "userLicense.update", + "description": "This endpoint requires the `userLicense.update` permission." } }, "/users/me/onboarding": { @@ -8240,7 +9072,9 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "userOnboarding.delete", + "description": "This endpoint requires the `userOnboarding.delete` permission." }, "get": { "operationId": "getUserOnboarding", @@ -8270,7 +9104,9 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "userOnboarding.read", + "description": "This endpoint requires the `userOnboarding.read` permission." }, "put": { "operationId": "setUserOnboarding", @@ -8310,7 +9146,9 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "userOnboarding.update", + "description": "This endpoint requires the `userOnboarding.update` permission." } }, "/users/me/preferences": { @@ -8342,7 +9180,9 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "userPreference.read", + "description": "This endpoint requires the `userPreference.read` permission." }, "put": { "operationId": "updateMyPreferences", @@ -8382,7 +9222,9 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "userPreference.update", + "description": "This endpoint requires the `userPreference.update` permission." } }, "/users/profile-image": { @@ -8407,7 +9249,9 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "userProfileImage.delete", + "description": "This endpoint requires the `userProfileImage.delete` permission." }, "post": { "operationId": "createProfileImage", @@ -8448,7 +9292,9 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "userProfileImage.update", + "description": "This endpoint requires the `userProfileImage.update` permission." } }, "/users/{id}": { @@ -8490,7 +9336,9 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "user.read", + "description": "This endpoint requires the `user.read` permission." } }, "/users/{id}/profile-image": { @@ -8533,7 +9381,9 @@ ], "tags": [ "Users" - ] + ], + "x-immich-permission": "userProfileImage.read", + "description": "This endpoint requires the `userProfileImage.read` permission." } }, "/view/folder": { @@ -8619,7 +9469,7 @@ "info": { "title": "Immich", "description": "Immich API", - "version": "1.135.3", + "version": "1.137.3", "contact": {} }, "tags": [], @@ -11716,25 +12566,35 @@ "asset.read", "asset.update", "asset.delete", + "asset.statistics", "asset.share", "asset.view", "asset.download", "asset.upload", + "asset.replace", "album.create", "album.read", "album.update", "album.delete", "album.statistics", - "album.addAsset", - "album.removeAsset", "album.share", "album.download", + "albumAsset.create", + "albumAsset.delete", + "albumUser.create", + "albumUser.update", + "albumUser.delete", + "auth.changePassword", "authDevice.delete", "archive.read", + "duplicate.read", + "duplicate.delete", "face.create", "face.read", "face.update", "face.delete", + "job.create", + "job.read", "library.create", "library.read", "library.update", @@ -11746,6 +12606,9 @@ "memory.read", "memory.update", "memory.delete", + "memory.statistics", + "memoryAsset.create", + "memoryAsset.delete", "notification.create", "notification.read", "notification.update", @@ -11761,6 +12624,16 @@ "person.statistics", "person.merge", "person.reassign", + "pinCode.create", + "pinCode.update", + "pinCode.delete", + "server.about", + "server.apkLinks", + "server.storage", + "server.statistics", + "serverLicense.read", + "serverLicense.update", + "serverLicense.delete", "session.create", "session.read", "session.update", @@ -11774,6 +12647,10 @@ "stack.read", "stack.update", "stack.delete", + "sync.stream", + "syncCheckpoint.read", + "syncCheckpoint.update", + "syncCheckpoint.delete", "systemConfig.read", "systemConfig.update", "systemMetadata.read", @@ -11783,10 +12660,25 @@ "tag.update", "tag.delete", "tag.asset", - "admin.user.create", - "admin.user.read", - "admin.user.update", - "admin.user.delete" + "user.read", + "user.update", + "userLicense.create", + "userLicense.read", + "userLicense.update", + "userLicense.delete", + "userOnboarding.read", + "userOnboarding.update", + "userOnboarding.delete", + "userPreference.read", + "userPreference.update", + "userProfileImage.create", + "userProfileImage.read", + "userProfileImage.update", + "userProfileImage.delete", + "adminUser.create", + "adminUser.read", + "adminUser.update", + "adminUser.delete" ], "type": "string" }, @@ -12941,6 +13833,7 @@ "type": "array" }, "description": { + "nullable": true, "type": "string" }, "expiresAt": { @@ -12950,12 +13843,17 @@ "type": "string" }, "password": { + "nullable": true, "type": "string" }, "showMetadata": { "default": true, "type": "boolean" }, + "slug": { + "nullable": true, + "type": "string" + }, "type": { "allOf": [ { @@ -12982,6 +13880,7 @@ "type": "boolean" }, "description": { + "nullable": true, "type": "string" }, "expiresAt": { @@ -12990,10 +13889,15 @@ "type": "string" }, "password": { + "nullable": true, "type": "string" }, "showMetadata": { "type": "boolean" + }, + "slug": { + "nullable": true, + "type": "string" } }, "type": "object" @@ -13041,6 +13945,10 @@ "showMetadata": { "type": "boolean" }, + "slug": { + "nullable": true, + "type": "string" + }, "token": { "nullable": true, "type": "string" @@ -13067,6 +13975,7 @@ "key", "password", "showMetadata", + "slug", "type", "userId" ], @@ -13788,6 +14697,65 @@ ], "type": "object" }, + "SyncAssetFaceDeleteV1": { + "properties": { + "assetFaceId": { + "type": "string" + } + }, + "required": [ + "assetFaceId" + ], + "type": "object" + }, + "SyncAssetFaceV1": { + "properties": { + "assetId": { + "type": "string" + }, + "boundingBoxX1": { + "type": "integer" + }, + "boundingBoxX2": { + "type": "integer" + }, + "boundingBoxY1": { + "type": "integer" + }, + "boundingBoxY2": { + "type": "integer" + }, + "id": { + "type": "string" + }, + "imageHeight": { + "type": "integer" + }, + "imageWidth": { + "type": "integer" + }, + "personId": { + "nullable": true, + "type": "string" + }, + "sourceType": { + "type": "string" + } + }, + "required": [ + "assetId", + "boundingBoxX1", + "boundingBoxX2", + "boundingBoxY1", + "boundingBoxY2", + "id", + "imageHeight", + "imageWidth", + "personId", + "sourceType" + ], + "type": "object" + }, "SyncAssetV1": { "properties": { "checksum": { @@ -13818,6 +14786,10 @@ "isFavorite": { "type": "boolean" }, + "libraryId": { + "nullable": true, + "type": "string" + }, "livePhotoVideoId": { "nullable": true, "type": "string" @@ -13864,6 +14836,7 @@ "fileModifiedAt", "id", "isFavorite", + "libraryId", "livePhotoVideoId", "localDateTime", "originalFileName", @@ -13875,8 +14848,79 @@ ], "type": "object" }, + "SyncAuthUserV1": { + "properties": { + "avatarColor": { + "allOf": [ + { + "$ref": "#/components/schemas/UserAvatarColor" + } + ], + "nullable": true + }, + "deletedAt": { + "format": "date-time", + "nullable": true, + "type": "string" + }, + "email": { + "type": "string" + }, + "hasProfileImage": { + "type": "boolean" + }, + "id": { + "type": "string" + }, + "isAdmin": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "oauthId": { + "type": "string" + }, + "pinCode": { + "nullable": true, + "type": "string" + }, + "profileChangedAt": { + "format": "date-time", + "type": "string" + }, + "quotaSizeInBytes": { + "nullable": true, + "type": "integer" + }, + "quotaUsageInBytes": { + "type": "integer" + }, + "storageLabel": { + "nullable": true, + "type": "string" + } + }, + "required": [ + "avatarColor", + "deletedAt", + "email", + "hasProfileImage", + "id", + "isAdmin", + "name", + "oauthId", + "pinCode", + "profileChangedAt", + "quotaSizeInBytes", + "quotaUsageInBytes", + "storageLabel" + ], + "type": "object" + }, "SyncEntityType": { "enum": [ + "AuthUserV1", "UserV1", "UserDeleteV1", "AssetV1", @@ -13912,6 +14956,8 @@ "StackDeleteV1", "PersonV1", "PersonDeleteV1", + "AssetFaceV1", + "AssetFaceDeleteV1", "UserMetadataV1", "UserMetadataDeleteV1", "SyncAckV1", @@ -14109,9 +15155,6 @@ "ownerId": { "type": "string" }, - "thumbnailPath": { - "type": "string" - }, "updatedAt": { "format": "date-time", "type": "string" @@ -14127,7 +15170,6 @@ "isHidden", "name", "ownerId", - "thumbnailPath", "updatedAt" ], "type": "object" @@ -14141,6 +15183,7 @@ "AlbumAssetExifsV1", "AssetsV1", "AssetExifsV1", + "AuthUsersV1", "MemoriesV1", "MemoryToAssetsV1", "PartnersV1", @@ -14150,6 +15193,7 @@ "StacksV1", "UsersV1", "PeopleV1", + "AssetFacesV1", "UserMetadataV1" ], "type": "string" @@ -14229,7 +15273,11 @@ "SyncUserMetadataDeleteV1": { "properties": { "key": { - "type": "string" + "allOf": [ + { + "$ref": "#/components/schemas/UserMetadataKey" + } + ] }, "userId": { "type": "string" @@ -14244,7 +15292,11 @@ "SyncUserMetadataV1": { "properties": { "key": { - "type": "string" + "allOf": [ + { + "$ref": "#/components/schemas/UserMetadataKey" + } + ] }, "userId": { "type": "string" @@ -14262,6 +15314,14 @@ }, "SyncUserV1": { "properties": { + "avatarColor": { + "allOf": [ + { + "$ref": "#/components/schemas/UserAvatarColor" + } + ], + "nullable": true + }, "deletedAt": { "format": "date-time", "nullable": true, @@ -14270,18 +15330,28 @@ "email": { "type": "string" }, + "hasProfileImage": { + "type": "boolean" + }, "id": { "type": "string" }, "name": { "type": "string" + }, + "profileChangedAt": { + "format": "date-time", + "type": "string" } }, "required": [ + "avatarColor", "deletedAt", "email", + "hasProfileImage", "id", - "name" + "name", + "profileChangedAt" ], "type": "object" }, @@ -15949,6 +17019,14 @@ ], "type": "object" }, + "UserMetadataKey": { + "enum": [ + "preferences", + "license", + "onboarding" + ], + "type": "string" + }, "UserPreferencesResponseDto": { "properties": { "albums": { diff --git a/open-api/typescript-sdk/.nvmrc b/open-api/typescript-sdk/.nvmrc index fc37597bcc..7377d130ed 100644 --- a/open-api/typescript-sdk/.nvmrc +++ b/open-api/typescript-sdk/.nvmrc @@ -1 +1 @@ -22.17.0 +22.17.1 diff --git a/open-api/typescript-sdk/package-lock.json b/open-api/typescript-sdk/package-lock.json index ab9f3b1fd7..a9b5c295ca 100644 --- a/open-api/typescript-sdk/package-lock.json +++ b/open-api/typescript-sdk/package-lock.json @@ -1,18 +1,18 @@ { "name": "@immich/sdk", - "version": "1.135.3", + "version": "1.137.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@immich/sdk", - "version": "1.135.3", + "version": "1.137.3", "license": "GNU Affero General Public License version 3", "dependencies": { "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.15.33", + "@types/node": "^22.16.5", "typescript": "^5.3.3" } }, @@ -23,9 +23,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.15.34", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.34.tgz", - "integrity": "sha512-8Y6E5WUupYy1Dd0II32BsWAx5MWdcnRd8L84Oys3veg1YrYtNtzgO4CFhiBg6MDSjk7Ay36HYOnU7/tuOzIzcw==", + "version": "22.16.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.16.5.tgz", + "integrity": "sha512-bJFoMATwIGaxxx8VJPeM8TonI8t579oRvgAuT8zFugJsJZgzqv0Fu8Mhp68iecjzG7cnN3mO2dJQ5uUM2EFrgQ==", "dev": true, "license": "MIT", "dependencies": { diff --git a/open-api/typescript-sdk/package.json b/open-api/typescript-sdk/package.json index f2885f4a7b..f01ec302ce 100644 --- a/open-api/typescript-sdk/package.json +++ b/open-api/typescript-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@immich/sdk", - "version": "1.135.3", + "version": "1.137.3", "description": "Auto-generated TypeScript SDK for the Immich API", "type": "module", "main": "./build/index.js", @@ -19,7 +19,7 @@ "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.15.33", + "@types/node": "^22.16.5", "typescript": "^5.3.3" }, "repository": { @@ -28,6 +28,6 @@ "directory": "open-api/typescript-sdk" }, "volta": { - "node": "22.17.0" + "node": "22.17.1" } } diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index f60fa6dfe8..d26d14aa4a 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -1,6 +1,6 @@ /** * Immich - * 1.135.3 + * 1.137.3 * DO NOT MODIFY - This file has been generated using oazapfts. * See https://www.npmjs.com/package/oazapfts */ @@ -1199,6 +1199,7 @@ export type SharedLinkResponseDto = { key: string; password: string | null; showMetadata: boolean; + slug: string | null; token?: string | null; "type": SharedLinkType; userId: string; @@ -1208,10 +1209,11 @@ export type SharedLinkCreateDto = { allowDownload?: boolean; allowUpload?: boolean; assetIds?: string[]; - description?: string; + description?: string | null; expiresAt?: string | null; - password?: string; + password?: string | null; showMetadata?: boolean; + slug?: string | null; "type": SharedLinkType; }; export type SharedLinkEditDto = { @@ -1221,10 +1223,11 @@ export type SharedLinkEditDto = { Setting this flag and not sending expiryAt is considered as null instead. Clients that can send null values can ignore this. */ changeExpiryTime?: boolean; - description?: string; + description?: string | null; expiresAt?: string | null; - password?: string; + password?: string | null; showMetadata?: boolean; + slug?: string | null; }; export type AssetIdsResponseDto = { assetId: string; @@ -1575,6 +1578,9 @@ export type CreateProfileImageResponseDto = { profileImagePath: string; userId: string; }; +/** + * This endpoint requires the `activity.read` permission. + */ export function getActivities({ albumId, assetId, level, $type, userId }: { albumId: string; assetId?: string; @@ -1595,6 +1601,9 @@ export function getActivities({ albumId, assetId, level, $type, userId }: { ...opts })); } +/** + * This endpoint requires the `activity.create` permission. + */ export function createActivity({ activityCreateDto }: { activityCreateDto: ActivityCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -1607,6 +1616,9 @@ export function createActivity({ activityCreateDto }: { body: activityCreateDto }))); } +/** + * This endpoint requires the `activity.statistics` permission. + */ export function getActivityStatistics({ albumId, assetId }: { albumId: string; assetId?: string; @@ -1621,6 +1633,9 @@ export function getActivityStatistics({ albumId, assetId }: { ...opts })); } +/** + * This endpoint requires the `activity.delete` permission. + */ export function deleteActivity({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -1666,6 +1681,9 @@ export function sendTestEmailAdmin({ systemConfigSmtpDto }: { body: systemConfigSmtpDto }))); } +/** + * This endpoint is an admin-only route, and requires the `adminUser.read` permission. + */ export function searchUsersAdmin({ id, withDeleted }: { id?: string; withDeleted?: boolean; @@ -1680,6 +1698,9 @@ export function searchUsersAdmin({ id, withDeleted }: { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `adminUser.create` permission. + */ export function createUserAdmin({ userAdminCreateDto }: { userAdminCreateDto: UserAdminCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -1692,6 +1713,9 @@ export function createUserAdmin({ userAdminCreateDto }: { body: userAdminCreateDto }))); } +/** + * This endpoint is an admin-only route, and requires the `adminUser.delete` permission. + */ export function deleteUserAdmin({ id, userAdminDeleteDto }: { id: string; userAdminDeleteDto: UserAdminDeleteDto; @@ -1705,6 +1729,9 @@ export function deleteUserAdmin({ id, userAdminDeleteDto }: { body: userAdminDeleteDto }))); } +/** + * This endpoint is an admin-only route, and requires the `adminUser.read` permission. + */ export function getUserAdmin({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -1715,6 +1742,9 @@ export function getUserAdmin({ id }: { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `adminUser.update` permission. + */ export function updateUserAdmin({ id, userAdminUpdateDto }: { id: string; userAdminUpdateDto: UserAdminUpdateDto; @@ -1728,6 +1758,9 @@ export function updateUserAdmin({ id, userAdminUpdateDto }: { body: userAdminUpdateDto }))); } +/** + * This endpoint is an admin-only route, and requires the `adminUser.read` permission. + */ export function getUserPreferencesAdmin({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -1738,6 +1771,9 @@ export function getUserPreferencesAdmin({ id }: { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `adminUser.update` permission. + */ export function updateUserPreferencesAdmin({ id, userPreferencesUpdateDto }: { id: string; userPreferencesUpdateDto: UserPreferencesUpdateDto; @@ -1751,6 +1787,9 @@ export function updateUserPreferencesAdmin({ id, userPreferencesUpdateDto }: { body: userPreferencesUpdateDto }))); } +/** + * This endpoint is an admin-only route, and requires the `adminUser.delete` permission. + */ export function restoreUserAdmin({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -1762,6 +1801,9 @@ export function restoreUserAdmin({ id }: { method: "POST" })); } +/** + * This endpoint is an admin-only route, and requires the `adminUser.read` permission. + */ export function getUserStatisticsAdmin({ id, isFavorite, isTrashed, visibility }: { id: string; isFavorite?: boolean; @@ -1779,6 +1821,9 @@ export function getUserStatisticsAdmin({ id, isFavorite, isTrashed, visibility } ...opts })); } +/** + * This endpoint requires the `album.read` permission. + */ export function getAllAlbums({ assetId, shared }: { assetId?: string; shared?: boolean; @@ -1793,6 +1838,9 @@ export function getAllAlbums({ assetId, shared }: { ...opts })); } +/** + * This endpoint requires the `album.create` permission. + */ export function createAlbum({ createAlbumDto }: { createAlbumDto: CreateAlbumDto; }, opts?: Oazapfts.RequestOpts) { @@ -1805,6 +1853,9 @@ export function createAlbum({ createAlbumDto }: { body: createAlbumDto }))); } +/** + * This endpoint requires the `album.statistics` permission. + */ export function getAlbumStatistics(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -1813,6 +1864,9 @@ export function getAlbumStatistics(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `album.delete` permission. + */ export function deleteAlbum({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -1821,9 +1875,13 @@ export function deleteAlbum({ id }: { method: "DELETE" })); } -export function getAlbumInfo({ id, key, withoutAssets }: { +/** + * This endpoint requires the `album.read` permission. + */ +export function getAlbumInfo({ id, key, slug, withoutAssets }: { id: string; key?: string; + slug?: string; withoutAssets?: boolean; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ @@ -1831,11 +1889,15 @@ export function getAlbumInfo({ id, key, withoutAssets }: { data: AlbumResponseDto; }>(`/albums/${encodeURIComponent(id)}${QS.query(QS.explode({ key, + slug, withoutAssets }))}`, { ...opts })); } +/** + * This endpoint requires the `album.update` permission. + */ export function updateAlbumInfo({ id, updateAlbumDto }: { id: string; updateAlbumDto: UpdateAlbumDto; @@ -1849,6 +1911,9 @@ export function updateAlbumInfo({ id, updateAlbumDto }: { body: updateAlbumDto }))); } +/** + * This endpoint requires the `albumAsset.delete` permission. + */ export function removeAssetFromAlbum({ id, bulkIdsDto }: { id: string; bulkIdsDto: BulkIdsDto; @@ -1862,22 +1927,30 @@ export function removeAssetFromAlbum({ id, bulkIdsDto }: { body: bulkIdsDto }))); } -export function addAssetsToAlbum({ id, key, bulkIdsDto }: { +/** + * This endpoint requires the `albumAsset.create` permission. + */ +export function addAssetsToAlbum({ id, key, slug, bulkIdsDto }: { id: string; key?: string; + slug?: string; bulkIdsDto: BulkIdsDto; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; data: BulkIdResponseDto[]; }>(`/albums/${encodeURIComponent(id)}/assets${QS.query(QS.explode({ - key + key, + slug }))}`, oazapfts.json({ ...opts, method: "PUT", body: bulkIdsDto }))); } +/** + * This endpoint requires the `albumUser.delete` permission. + */ export function removeUserFromAlbum({ id, userId }: { id: string; userId: string; @@ -1887,6 +1960,9 @@ export function removeUserFromAlbum({ id, userId }: { method: "DELETE" })); } +/** + * This endpoint requires the `albumUser.update` permission. + */ export function updateAlbumUser({ id, userId, updateAlbumUserDto }: { id: string; userId: string; @@ -1898,6 +1974,9 @@ export function updateAlbumUser({ id, userId, updateAlbumUserDto }: { body: updateAlbumUserDto }))); } +/** + * This endpoint requires the `albumUser.create` permission. + */ export function addUsersToAlbum({ id, addUsersDto }: { id: string; addUsersDto: AddUsersDto; @@ -1911,6 +1990,9 @@ export function addUsersToAlbum({ id, addUsersDto }: { body: addUsersDto }))); } +/** + * This endpoint requires the `apiKey.read` permission. + */ export function getApiKeys(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -1919,6 +2001,9 @@ export function getApiKeys(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `apiKey.create` permission. + */ export function createApiKey({ apiKeyCreateDto }: { apiKeyCreateDto: ApiKeyCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -1931,6 +2016,9 @@ export function createApiKey({ apiKeyCreateDto }: { body: apiKeyCreateDto }))); } +/** + * This endpoint requires the `apiKey.delete` permission. + */ export function deleteApiKey({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -1939,6 +2027,9 @@ export function deleteApiKey({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `apiKey.read` permission. + */ export function getApiKey({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -1949,6 +2040,9 @@ export function getApiKey({ id }: { ...opts })); } +/** + * This endpoint requires the `apiKey.update` permission. + */ export function updateApiKey({ id, apiKeyUpdateDto }: { id: string; apiKeyUpdateDto: ApiKeyUpdateDto; @@ -1962,6 +2056,9 @@ export function updateApiKey({ id, apiKeyUpdateDto }: { body: apiKeyUpdateDto }))); } +/** + * This endpoint requires the `asset.delete` permission. + */ export function deleteAssets({ assetBulkDeleteDto }: { assetBulkDeleteDto: AssetBulkDeleteDto; }, opts?: Oazapfts.RequestOpts) { @@ -1971,8 +2068,12 @@ export function deleteAssets({ assetBulkDeleteDto }: { body: assetBulkDeleteDto }))); } -export function uploadAsset({ key, xImmichChecksum, assetMediaCreateDto }: { +/** + * This endpoint requires the `asset.upload` permission. + */ +export function uploadAsset({ key, slug, xImmichChecksum, assetMediaCreateDto }: { key?: string; + slug?: string; xImmichChecksum?: string; assetMediaCreateDto: AssetMediaCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -1980,7 +2081,8 @@ export function uploadAsset({ key, xImmichChecksum, assetMediaCreateDto }: { status: 201; data: AssetMediaResponseDto; }>(`/assets${QS.query(QS.explode({ - key + key, + slug }))}`, oazapfts.multipart({ ...opts, method: "POST", @@ -1990,6 +2092,9 @@ export function uploadAsset({ key, xImmichChecksum, assetMediaCreateDto }: { }) }))); } +/** + * This endpoint requires the `asset.update` permission. + */ export function updateAssets({ assetBulkUpdateDto }: { assetBulkUpdateDto: AssetBulkUpdateDto; }, opts?: Oazapfts.RequestOpts) { @@ -2052,7 +2157,7 @@ export function runAssetJobs({ assetJobsDto }: { }))); } /** - * This property was deprecated in v1.116.0 + * This property was deprecated in v1.116.0. This endpoint requires the `asset.read` permission. */ export function getRandom({ count }: { count?: number; @@ -2066,6 +2171,9 @@ export function getRandom({ count }: { ...opts })); } +/** + * This endpoint requires the `asset.statistics` permission. + */ export function getAssetStatistics({ isFavorite, isTrashed, visibility }: { isFavorite?: boolean; isTrashed?: boolean; @@ -2082,19 +2190,27 @@ export function getAssetStatistics({ isFavorite, isTrashed, visibility }: { ...opts })); } -export function getAssetInfo({ id, key }: { +/** + * This endpoint requires the `asset.read` permission. + */ +export function getAssetInfo({ id, key, slug }: { id: string; key?: string; + slug?: string; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; data: AssetResponseDto; }>(`/assets/${encodeURIComponent(id)}${QS.query(QS.explode({ - key + key, + slug }))}`, { ...opts })); } +/** + * This endpoint requires the `asset.update` permission. + */ export function updateAsset({ id, updateAssetDto }: { id: string; updateAssetDto: UpdateAssetDto; @@ -2108,15 +2224,20 @@ export function updateAsset({ id, updateAssetDto }: { body: updateAssetDto }))); } -export function downloadAsset({ id, key }: { +/** + * This endpoint requires the `asset.download` permission. + */ +export function downloadAsset({ id, key, slug }: { id: string; key?: string; + slug?: string; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchBlob<{ status: 200; data: Blob; }>(`/assets/${encodeURIComponent(id)}/original${QS.query(QS.explode({ - key + key, + slug }))}`, { ...opts })); @@ -2124,46 +2245,58 @@ export function downloadAsset({ id, key }: { /** * replaceAsset */ -export function replaceAsset({ id, key, assetMediaReplaceDto }: { +export function replaceAsset({ id, key, slug, assetMediaReplaceDto }: { id: string; key?: string; + slug?: string; assetMediaReplaceDto: AssetMediaReplaceDto; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; data: AssetMediaResponseDto; }>(`/assets/${encodeURIComponent(id)}/original${QS.query(QS.explode({ - key + key, + slug }))}`, oazapfts.multipart({ ...opts, method: "PUT", body: assetMediaReplaceDto }))); } -export function viewAsset({ id, key, size }: { +/** + * This endpoint requires the `asset.view` permission. + */ +export function viewAsset({ id, key, size, slug }: { id: string; key?: string; size?: AssetMediaSize; + slug?: string; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchBlob<{ status: 200; data: Blob; }>(`/assets/${encodeURIComponent(id)}/thumbnail${QS.query(QS.explode({ key, - size + size, + slug }))}`, { ...opts })); } -export function playAssetVideo({ id, key }: { +/** + * This endpoint requires the `asset.view` permission. + */ +export function playAssetVideo({ id, key, slug }: { id: string; key?: string; + slug?: string; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchBlob<{ status: 200; data: Blob; }>(`/assets/${encodeURIComponent(id)}/video/playback${QS.query(QS.explode({ - key + key, + slug }))}`, { ...opts })); @@ -2180,6 +2313,9 @@ export function signUpAdmin({ signUpDto }: { body: signUpDto }))); } +/** + * This endpoint requires the `auth.changePassword` permission. + */ export function changePassword({ changePasswordDto }: { changePasswordDto: ChangePasswordDto; }, opts?: Oazapfts.RequestOpts) { @@ -2213,6 +2349,9 @@ export function logout(opts?: Oazapfts.RequestOpts) { method: "POST" })); } +/** + * This endpoint requires the `pinCode.delete` permission. + */ export function resetPinCode({ pinCodeResetDto }: { pinCodeResetDto: PinCodeResetDto; }, opts?: Oazapfts.RequestOpts) { @@ -2222,6 +2361,9 @@ export function resetPinCode({ pinCodeResetDto }: { body: pinCodeResetDto }))); } +/** + * This endpoint requires the `pinCode.create` permission. + */ export function setupPinCode({ pinCodeSetupDto }: { pinCodeSetupDto: PinCodeSetupDto; }, opts?: Oazapfts.RequestOpts) { @@ -2231,6 +2373,9 @@ export function setupPinCode({ pinCodeSetupDto }: { body: pinCodeSetupDto }))); } +/** + * This endpoint requires the `pinCode.update` permission. + */ export function changePinCode({ pinCodeChangeDto }: { pinCodeChangeDto: PinCodeChangeDto; }, opts?: Oazapfts.RequestOpts) { @@ -2272,36 +2417,49 @@ export function validateAccessToken(opts?: Oazapfts.RequestOpts) { method: "POST" })); } -export function downloadArchive({ key, assetIdsDto }: { +/** + * This endpoint requires the `asset.download` permission. + */ +export function downloadArchive({ key, slug, assetIdsDto }: { key?: string; + slug?: string; assetIdsDto: AssetIdsDto; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchBlob<{ status: 200; data: Blob; }>(`/download/archive${QS.query(QS.explode({ - key + key, + slug }))}`, oazapfts.json({ ...opts, method: "POST", body: assetIdsDto }))); } -export function getDownloadInfo({ key, downloadInfoDto }: { +/** + * This endpoint requires the `asset.download` permission. + */ +export function getDownloadInfo({ key, slug, downloadInfoDto }: { key?: string; + slug?: string; downloadInfoDto: DownloadInfoDto; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 201; data: DownloadResponseDto; }>(`/download/info${QS.query(QS.explode({ - key + key, + slug }))}`, oazapfts.json({ ...opts, method: "POST", body: downloadInfoDto }))); } +/** + * This endpoint requires the `duplicate.delete` permission. + */ export function deleteDuplicates({ bulkIdsDto }: { bulkIdsDto: BulkIdsDto; }, opts?: Oazapfts.RequestOpts) { @@ -2311,6 +2469,9 @@ export function deleteDuplicates({ bulkIdsDto }: { body: bulkIdsDto }))); } +/** + * This endpoint requires the `duplicate.read` permission. + */ export function getAssetDuplicates(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -2319,6 +2480,9 @@ export function getAssetDuplicates(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `duplicate.delete` permission. + */ export function deleteDuplicate({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2327,6 +2491,9 @@ export function deleteDuplicate({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `face.read` permission. + */ export function getFaces({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2339,6 +2506,9 @@ export function getFaces({ id }: { ...opts })); } +/** + * This endpoint requires the `face.create` permission. + */ export function createFace({ assetFaceCreateDto }: { assetFaceCreateDto: AssetFaceCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -2348,6 +2518,9 @@ export function createFace({ assetFaceCreateDto }: { body: assetFaceCreateDto }))); } +/** + * This endpoint requires the `face.delete` permission. + */ export function deleteFace({ id, assetFaceDeleteDto }: { id: string; assetFaceDeleteDto: AssetFaceDeleteDto; @@ -2358,6 +2531,9 @@ export function deleteFace({ id, assetFaceDeleteDto }: { body: assetFaceDeleteDto }))); } +/** + * This endpoint requires the `face.update` permission. + */ export function reassignFacesById({ id, faceDto }: { id: string; faceDto: FaceDto; @@ -2371,6 +2547,9 @@ export function reassignFacesById({ id, faceDto }: { body: faceDto }))); } +/** + * This endpoint is an admin-only route, and requires the `job.read` permission. + */ export function getAllJobsStatus(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -2379,6 +2558,9 @@ export function getAllJobsStatus(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `job.create` permission. + */ export function createJob({ jobCreateDto }: { jobCreateDto: JobCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -2388,6 +2570,9 @@ export function createJob({ jobCreateDto }: { body: jobCreateDto }))); } +/** + * This endpoint is an admin-only route, and requires the `job.create` permission. + */ export function sendJobCommand({ id, jobCommandDto }: { id: JobName; jobCommandDto: JobCommandDto; @@ -2401,6 +2586,9 @@ export function sendJobCommand({ id, jobCommandDto }: { body: jobCommandDto }))); } +/** + * This endpoint is an admin-only route, and requires the `library.read` permission. + */ export function getAllLibraries(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -2409,6 +2597,9 @@ export function getAllLibraries(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `library.create` permission. + */ export function createLibrary({ createLibraryDto }: { createLibraryDto: CreateLibraryDto; }, opts?: Oazapfts.RequestOpts) { @@ -2421,6 +2612,9 @@ export function createLibrary({ createLibraryDto }: { body: createLibraryDto }))); } +/** + * This endpoint is an admin-only route, and requires the `library.delete` permission. + */ export function deleteLibrary({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2429,6 +2623,9 @@ export function deleteLibrary({ id }: { method: "DELETE" })); } +/** + * This endpoint is an admin-only route, and requires the `library.read` permission. + */ export function getLibrary({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2439,6 +2636,9 @@ export function getLibrary({ id }: { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `library.update` permission. + */ export function updateLibrary({ id, updateLibraryDto }: { id: string; updateLibraryDto: UpdateLibraryDto; @@ -2452,6 +2652,9 @@ export function updateLibrary({ id, updateLibraryDto }: { body: updateLibraryDto }))); } +/** + * This endpoint is an admin-only route, and requires the `library.update` permission. + */ export function scanLibrary({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2460,6 +2663,9 @@ export function scanLibrary({ id }: { method: "POST" })); } +/** + * This endpoint is an admin-only route, and requires the `library.statistics` permission. + */ export function getLibraryStatistics({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2483,11 +2689,11 @@ export function validate({ id, validateLibraryDto }: { body: validateLibraryDto }))); } -export function getMapMarkers({ fileCreatedAfter, fileCreatedBefore, isArchived, isFavorite, withPartners, withSharedAlbums }: { - fileCreatedAfter?: string; - fileCreatedBefore?: string; +export function getMapMarkers({ isArchived, isFavorite, fileCreatedAfter, fileCreatedBefore, withPartners, withSharedAlbums }: { isArchived?: boolean; isFavorite?: boolean; + fileCreatedAfter?: string; + fileCreatedBefore?: string; withPartners?: boolean; withSharedAlbums?: boolean; }, opts?: Oazapfts.RequestOpts) { @@ -2495,10 +2701,10 @@ export function getMapMarkers({ fileCreatedAfter, fileCreatedBefore, isArchived, status: 200; data: MapMarkerResponseDto[]; }>(`/map/markers${QS.query(QS.explode({ - fileCreatedAfter, - fileCreatedBefore, isArchived, isFavorite, + fileCreatedAfter, + fileCreatedBefore, withPartners, withSharedAlbums }))}`, { @@ -2519,6 +2725,9 @@ export function reverseGeocode({ lat, lon }: { ...opts })); } +/** + * This endpoint requires the `memory.read` permission. + */ export function searchMemories({ $for, isSaved, isTrashed, $type }: { $for?: string; isSaved?: boolean; @@ -2537,6 +2746,9 @@ export function searchMemories({ $for, isSaved, isTrashed, $type }: { ...opts })); } +/** + * This endpoint requires the `memory.create` permission. + */ export function createMemory({ memoryCreateDto }: { memoryCreateDto: MemoryCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -2549,6 +2761,9 @@ export function createMemory({ memoryCreateDto }: { body: memoryCreateDto }))); } +/** + * This endpoint requires the `memory.statistics` permission. + */ export function memoriesStatistics({ $for, isSaved, isTrashed, $type }: { $for?: string; isSaved?: boolean; @@ -2567,6 +2782,9 @@ export function memoriesStatistics({ $for, isSaved, isTrashed, $type }: { ...opts })); } +/** + * This endpoint requires the `memory.delete` permission. + */ export function deleteMemory({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2575,6 +2793,9 @@ export function deleteMemory({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `memory.read` permission. + */ export function getMemory({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2585,6 +2806,9 @@ export function getMemory({ id }: { ...opts })); } +/** + * This endpoint requires the `memory.update` permission. + */ export function updateMemory({ id, memoryUpdateDto }: { id: string; memoryUpdateDto: MemoryUpdateDto; @@ -2598,6 +2822,9 @@ export function updateMemory({ id, memoryUpdateDto }: { body: memoryUpdateDto }))); } +/** + * This endpoint requires the `memoryAsset.delete` permission. + */ export function removeMemoryAssets({ id, bulkIdsDto }: { id: string; bulkIdsDto: BulkIdsDto; @@ -2611,6 +2838,9 @@ export function removeMemoryAssets({ id, bulkIdsDto }: { body: bulkIdsDto }))); } +/** + * This endpoint requires the `memoryAsset.create` permission. + */ export function addMemoryAssets({ id, bulkIdsDto }: { id: string; bulkIdsDto: BulkIdsDto; @@ -2624,6 +2854,9 @@ export function addMemoryAssets({ id, bulkIdsDto }: { body: bulkIdsDto }))); } +/** + * This endpoint requires the `notification.delete` permission. + */ export function deleteNotifications({ notificationDeleteAllDto }: { notificationDeleteAllDto: NotificationDeleteAllDto; }, opts?: Oazapfts.RequestOpts) { @@ -2633,6 +2866,9 @@ export function deleteNotifications({ notificationDeleteAllDto }: { body: notificationDeleteAllDto }))); } +/** + * This endpoint requires the `notification.read` permission. + */ export function getNotifications({ id, level, $type, unread }: { id?: string; level?: NotificationLevel; @@ -2651,6 +2887,9 @@ export function getNotifications({ id, level, $type, unread }: { ...opts })); } +/** + * This endpoint requires the `notification.update` permission. + */ export function updateNotifications({ notificationUpdateAllDto }: { notificationUpdateAllDto: NotificationUpdateAllDto; }, opts?: Oazapfts.RequestOpts) { @@ -2660,6 +2899,9 @@ export function updateNotifications({ notificationUpdateAllDto }: { body: notificationUpdateAllDto }))); } +/** + * This endpoint requires the `notification.delete` permission. + */ export function deleteNotification({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2668,6 +2910,9 @@ export function deleteNotification({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `notification.read` permission. + */ export function getNotification({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2678,6 +2923,9 @@ export function getNotification({ id }: { ...opts })); } +/** + * This endpoint requires the `notification.update` permission. + */ export function updateNotification({ id, notificationUpdateDto }: { id: string; notificationUpdateDto: NotificationUpdateDto; @@ -2741,6 +2989,9 @@ export function unlinkOAuthAccount(opts?: Oazapfts.RequestOpts) { method: "POST" })); } +/** + * This endpoint requires the `partner.read` permission. + */ export function getPartners({ direction }: { direction: PartnerDirection; }, opts?: Oazapfts.RequestOpts) { @@ -2753,6 +3004,9 @@ export function getPartners({ direction }: { ...opts })); } +/** + * This endpoint requires the `partner.delete` permission. + */ export function removePartner({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2761,6 +3015,9 @@ export function removePartner({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `partner.create` permission. + */ export function createPartner({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2772,6 +3029,9 @@ export function createPartner({ id }: { method: "POST" })); } +/** + * This endpoint requires the `partner.update` permission. + */ export function updatePartner({ id, updatePartnerDto }: { id: string; updatePartnerDto: UpdatePartnerDto; @@ -2785,6 +3045,9 @@ export function updatePartner({ id, updatePartnerDto }: { body: updatePartnerDto }))); } +/** + * This endpoint requires the `person.delete` permission. + */ export function deletePeople({ bulkIdsDto }: { bulkIdsDto: BulkIdsDto; }, opts?: Oazapfts.RequestOpts) { @@ -2794,6 +3057,9 @@ export function deletePeople({ bulkIdsDto }: { body: bulkIdsDto }))); } +/** + * This endpoint requires the `person.read` permission. + */ export function getAllPeople({ closestAssetId, closestPersonId, page, size, withHidden }: { closestAssetId?: string; closestPersonId?: string; @@ -2814,6 +3080,9 @@ export function getAllPeople({ closestAssetId, closestPersonId, page, size, with ...opts })); } +/** + * This endpoint requires the `person.create` permission. + */ export function createPerson({ personCreateDto }: { personCreateDto: PersonCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -2826,6 +3095,9 @@ export function createPerson({ personCreateDto }: { body: personCreateDto }))); } +/** + * This endpoint requires the `person.update` permission. + */ export function updatePeople({ peopleUpdateDto }: { peopleUpdateDto: PeopleUpdateDto; }, opts?: Oazapfts.RequestOpts) { @@ -2838,6 +3110,9 @@ export function updatePeople({ peopleUpdateDto }: { body: peopleUpdateDto }))); } +/** + * This endpoint requires the `person.delete` permission. + */ export function deletePerson({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2846,6 +3121,9 @@ export function deletePerson({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `person.read` permission. + */ export function getPerson({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2856,6 +3134,9 @@ export function getPerson({ id }: { ...opts })); } +/** + * This endpoint requires the `person.update` permission. + */ export function updatePerson({ id, personUpdateDto }: { id: string; personUpdateDto: PersonUpdateDto; @@ -2869,6 +3150,9 @@ export function updatePerson({ id, personUpdateDto }: { body: personUpdateDto }))); } +/** + * This endpoint requires the `person.merge` permission. + */ export function mergePerson({ id, mergePersonDto }: { id: string; mergePersonDto: MergePersonDto; @@ -2882,6 +3166,9 @@ export function mergePerson({ id, mergePersonDto }: { body: mergePersonDto }))); } +/** + * This endpoint requires the `person.reassign` permission. + */ export function reassignFaces({ id, assetFaceUpdateDto }: { id: string; assetFaceUpdateDto: AssetFaceUpdateDto; @@ -2895,6 +3182,9 @@ export function reassignFaces({ id, assetFaceUpdateDto }: { body: assetFaceUpdateDto }))); } +/** + * This endpoint requires the `person.statistics` permission. + */ export function getPersonStatistics({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2905,6 +3195,9 @@ export function getPersonStatistics({ id }: { ...opts })); } +/** + * This endpoint requires the `person.read` permission. + */ export function getPersonThumbnail({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -2915,6 +3208,9 @@ export function getPersonThumbnail({ id }: { ...opts })); } +/** + * This endpoint requires the `asset.read` permission. + */ export function getAssetsByCity(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -2923,6 +3219,9 @@ export function getAssetsByCity(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `asset.read` permission. + */ export function getExploreData(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -2931,6 +3230,85 @@ export function getExploreData(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `asset.read` permission. + */ +export function searchLargeAssets({ albumIds, city, country, createdAfter, createdBefore, deviceId, isEncoded, isFavorite, isMotion, isNotInAlbum, isOffline, lensModel, libraryId, make, minFileSize, model, personIds, rating, size, state, tagIds, takenAfter, takenBefore, trashedAfter, trashedBefore, $type, updatedAfter, updatedBefore, visibility, withDeleted, withExif }: { + albumIds?: string[]; + city?: string | null; + country?: string | null; + createdAfter?: string; + createdBefore?: string; + deviceId?: string; + isEncoded?: boolean; + isFavorite?: boolean; + isMotion?: boolean; + isNotInAlbum?: boolean; + isOffline?: boolean; + lensModel?: string | null; + libraryId?: string | null; + make?: string; + minFileSize?: number; + model?: string | null; + personIds?: string[]; + rating?: number; + size?: number; + state?: string | null; + tagIds?: string[] | null; + takenAfter?: string; + takenBefore?: string; + trashedAfter?: string; + trashedBefore?: string; + $type?: AssetTypeEnum; + updatedAfter?: string; + updatedBefore?: string; + visibility?: AssetVisibility; + withDeleted?: boolean; + withExif?: boolean; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: AssetResponseDto[]; + }>(`/search/large-assets${QS.query(QS.explode({ + albumIds, + city, + country, + createdAfter, + createdBefore, + deviceId, + isEncoded, + isFavorite, + isMotion, + isNotInAlbum, + isOffline, + lensModel, + libraryId, + make, + minFileSize, + model, + personIds, + rating, + size, + state, + tagIds, + takenAfter, + takenBefore, + trashedAfter, + trashedBefore, + "type": $type, + updatedAfter, + updatedBefore, + visibility, + withDeleted, + withExif + }))}`, { + ...opts, + method: "POST" + })); +} +/** + * This endpoint requires the `asset.read` permission. + */ export function searchAssets({ metadataSearchDto }: { metadataSearchDto: MetadataSearchDto; }, opts?: Oazapfts.RequestOpts) { @@ -2943,6 +3321,9 @@ export function searchAssets({ metadataSearchDto }: { body: metadataSearchDto }))); } +/** + * This endpoint requires the `person.read` permission. + */ export function searchPerson({ name, withHidden }: { name: string; withHidden?: boolean; @@ -2957,6 +3338,9 @@ export function searchPerson({ name, withHidden }: { ...opts })); } +/** + * This endpoint requires the `asset.read` permission. + */ export function searchPlaces({ name }: { name: string; }, opts?: Oazapfts.RequestOpts) { @@ -2969,6 +3353,9 @@ export function searchPlaces({ name }: { ...opts })); } +/** + * This endpoint requires the `asset.read` permission. + */ export function searchRandom({ randomSearchDto }: { randomSearchDto: RandomSearchDto; }, opts?: Oazapfts.RequestOpts) { @@ -2981,6 +3368,9 @@ export function searchRandom({ randomSearchDto }: { body: randomSearchDto }))); } +/** + * This endpoint requires the `asset.read` permission. + */ export function searchSmart({ smartSearchDto }: { smartSearchDto: SmartSearchDto; }, opts?: Oazapfts.RequestOpts) { @@ -2993,6 +3383,9 @@ export function searchSmart({ smartSearchDto }: { body: smartSearchDto }))); } +/** + * This endpoint requires the `asset.statistics` permission. + */ export function searchAssetStatistics({ statisticsSearchDto }: { statisticsSearchDto: StatisticsSearchDto; }, opts?: Oazapfts.RequestOpts) { @@ -3005,6 +3398,9 @@ export function searchAssetStatistics({ statisticsSearchDto }: { body: statisticsSearchDto }))); } +/** + * This endpoint requires the `asset.read` permission. + */ export function getSearchSuggestions({ country, includeNull, make, model, state, $type }: { country?: string; includeNull?: boolean; @@ -3027,6 +3423,9 @@ export function getSearchSuggestions({ country, includeNull, make, model, state, ...opts })); } +/** + * This endpoint requires the `server.about` permission. + */ export function getAboutInfo(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3035,6 +3434,9 @@ export function getAboutInfo(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `server.apkLinks` permission. + */ export function getApkLinks(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3059,12 +3461,18 @@ export function getServerFeatures(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `serverLicense.delete` permission. + */ export function deleteServerLicense(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchText("/server/license", { ...opts, method: "DELETE" })); } +/** + * This endpoint is an admin-only route, and requires the `serverLicense.read` permission. + */ export function getServerLicense(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3075,6 +3483,9 @@ export function getServerLicense(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `serverLicense.update` permission. + */ export function setServerLicense({ licenseKeyDto }: { licenseKeyDto: LicenseKeyDto; }, opts?: Oazapfts.RequestOpts) { @@ -3103,6 +3514,9 @@ export function pingServer(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `server.statistics` permission. + */ export function getServerStatistics(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3111,6 +3525,9 @@ export function getServerStatistics(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `server.storage` permission. + */ export function getStorage(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3151,12 +3568,18 @@ export function getVersionHistory(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `session.delete` permission. + */ export function deleteAllSessions(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchText("/sessions", { ...opts, method: "DELETE" })); } +/** + * This endpoint requires the `session.read` permission. + */ export function getSessions(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3165,6 +3588,9 @@ export function getSessions(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `session.create` permission. + */ export function createSession({ sessionCreateDto }: { sessionCreateDto: SessionCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -3177,6 +3603,9 @@ export function createSession({ sessionCreateDto }: { body: sessionCreateDto }))); } +/** + * This endpoint requires the `session.delete` permission. + */ export function deleteSession({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -3185,6 +3614,9 @@ export function deleteSession({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `session.update` permission. + */ export function updateSession({ id, sessionUpdateDto }: { id: string; sessionUpdateDto: SessionUpdateDto; @@ -3198,6 +3630,9 @@ export function updateSession({ id, sessionUpdateDto }: { body: sessionUpdateDto }))); } +/** + * This endpoint requires the `session.lock` permission. + */ export function lockSession({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -3206,6 +3641,9 @@ export function lockSession({ id }: { method: "POST" })); } +/** + * This endpoint requires the `sharedLink.read` permission. + */ export function getAllSharedLinks({ albumId }: { albumId?: string; }, opts?: Oazapfts.RequestOpts) { @@ -3218,6 +3656,9 @@ export function getAllSharedLinks({ albumId }: { ...opts })); } +/** + * This endpoint requires the `sharedLink.create` permission. + */ export function createSharedLink({ sharedLinkCreateDto }: { sharedLinkCreateDto: SharedLinkCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -3230,22 +3671,27 @@ export function createSharedLink({ sharedLinkCreateDto }: { body: sharedLinkCreateDto }))); } -export function getMySharedLink({ key, password, token }: { - key?: string; +export function getMySharedLink({ password, token, key, slug }: { password?: string; token?: string; + key?: string; + slug?: string; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; data: SharedLinkResponseDto; }>(`/shared-links/me${QS.query(QS.explode({ - key, password, - token + token, + key, + slug }))}`, { ...opts })); } +/** + * This endpoint requires the `sharedLink.delete` permission. + */ export function removeSharedLink({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -3254,6 +3700,9 @@ export function removeSharedLink({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `sharedLink.read` permission. + */ export function getSharedLinkById({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -3264,6 +3713,9 @@ export function getSharedLinkById({ id }: { ...opts })); } +/** + * This endpoint requires the `sharedLink.update` permission. + */ export function updateSharedLink({ id, sharedLinkEditDto }: { id: string; sharedLinkEditDto: SharedLinkEditDto; @@ -3277,38 +3729,45 @@ export function updateSharedLink({ id, sharedLinkEditDto }: { body: sharedLinkEditDto }))); } -export function removeSharedLinkAssets({ id, key, assetIdsDto }: { +export function removeSharedLinkAssets({ id, key, slug, assetIdsDto }: { id: string; key?: string; + slug?: string; assetIdsDto: AssetIdsDto; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; data: AssetIdsResponseDto[]; }>(`/shared-links/${encodeURIComponent(id)}/assets${QS.query(QS.explode({ - key + key, + slug }))}`, oazapfts.json({ ...opts, method: "DELETE", body: assetIdsDto }))); } -export function addSharedLinkAssets({ id, key, assetIdsDto }: { +export function addSharedLinkAssets({ id, key, slug, assetIdsDto }: { id: string; key?: string; + slug?: string; assetIdsDto: AssetIdsDto; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; data: AssetIdsResponseDto[]; }>(`/shared-links/${encodeURIComponent(id)}/assets${QS.query(QS.explode({ - key + key, + slug }))}`, oazapfts.json({ ...opts, method: "PUT", body: assetIdsDto }))); } +/** + * This endpoint requires the `stack.delete` permission. + */ export function deleteStacks({ bulkIdsDto }: { bulkIdsDto: BulkIdsDto; }, opts?: Oazapfts.RequestOpts) { @@ -3318,6 +3777,9 @@ export function deleteStacks({ bulkIdsDto }: { body: bulkIdsDto }))); } +/** + * This endpoint requires the `stack.read` permission. + */ export function searchStacks({ primaryAssetId }: { primaryAssetId?: string; }, opts?: Oazapfts.RequestOpts) { @@ -3330,6 +3792,9 @@ export function searchStacks({ primaryAssetId }: { ...opts })); } +/** + * This endpoint requires the `stack.create` permission. + */ export function createStack({ stackCreateDto }: { stackCreateDto: StackCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -3342,6 +3807,9 @@ export function createStack({ stackCreateDto }: { body: stackCreateDto }))); } +/** + * This endpoint requires the `stack.delete` permission. + */ export function deleteStack({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -3350,6 +3818,9 @@ export function deleteStack({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `stack.read` permission. + */ export function getStack({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -3360,6 +3831,9 @@ export function getStack({ id }: { ...opts })); } +/** + * This endpoint requires the `stack.update` permission. + */ export function updateStack({ id, stackUpdateDto }: { id: string; stackUpdateDto: StackUpdateDto; @@ -3373,6 +3847,21 @@ export function updateStack({ id, stackUpdateDto }: { body: stackUpdateDto }))); } +/** + * This endpoint requires the `stack.update` permission. + */ +export function removeAssetFromStack({ assetId, id }: { + assetId: string; + id: string; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchText(`/stacks/${encodeURIComponent(id)}/assets/${encodeURIComponent(assetId)}`, { + ...opts, + method: "DELETE" + })); +} +/** + * This endpoint requires the `syncCheckpoint.delete` permission. + */ export function deleteSyncAck({ syncAckDeleteDto }: { syncAckDeleteDto: SyncAckDeleteDto; }, opts?: Oazapfts.RequestOpts) { @@ -3382,6 +3871,9 @@ export function deleteSyncAck({ syncAckDeleteDto }: { body: syncAckDeleteDto }))); } +/** + * This endpoint requires the `syncCheckpoint.read` permission. + */ export function getSyncAck(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3390,6 +3882,9 @@ export function getSyncAck(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `syncCheckpoint.update` permission. + */ export function sendSyncAck({ syncAckSetDto }: { syncAckSetDto: SyncAckSetDto; }, opts?: Oazapfts.RequestOpts) { @@ -3423,6 +3918,9 @@ export function getFullSyncForUser({ assetFullSyncDto }: { body: assetFullSyncDto }))); } +/** + * This endpoint requires the `sync.stream` permission. + */ export function getSyncStream({ syncStreamDto }: { syncStreamDto: SyncStreamDto; }, opts?: Oazapfts.RequestOpts) { @@ -3432,6 +3930,9 @@ export function getSyncStream({ syncStreamDto }: { body: syncStreamDto }))); } +/** + * This endpoint is an admin-only route, and requires the `systemConfig.read` permission. + */ export function getConfig(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3440,6 +3941,9 @@ export function getConfig(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `systemConfig.update` permission. + */ export function updateConfig({ systemConfigDto }: { systemConfigDto: SystemConfigDto; }, opts?: Oazapfts.RequestOpts) { @@ -3452,6 +3956,9 @@ export function updateConfig({ systemConfigDto }: { body: systemConfigDto }))); } +/** + * This endpoint is an admin-only route, and requires the `systemConfig.read` permission. + */ export function getConfigDefaults(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3460,6 +3967,9 @@ export function getConfigDefaults(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `systemConfig.read` permission. + */ export function getStorageTemplateOptions(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3468,6 +3978,9 @@ export function getStorageTemplateOptions(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `systemMetadata.read` permission. + */ export function getAdminOnboarding(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3476,6 +3989,9 @@ export function getAdminOnboarding(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `systemMetadata.update` permission. + */ export function updateAdminOnboarding({ adminOnboardingUpdateDto }: { adminOnboardingUpdateDto: AdminOnboardingUpdateDto; }, opts?: Oazapfts.RequestOpts) { @@ -3485,6 +4001,9 @@ export function updateAdminOnboarding({ adminOnboardingUpdateDto }: { body: adminOnboardingUpdateDto }))); } +/** + * This endpoint is an admin-only route, and requires the `systemMetadata.read` permission. + */ export function getReverseGeocodingState(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3493,6 +4012,9 @@ export function getReverseGeocodingState(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint is an admin-only route, and requires the `systemMetadata.read` permission. + */ export function getVersionCheckState(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3501,6 +4023,9 @@ export function getVersionCheckState(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `tag.read` permission. + */ export function getAllTags(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3509,6 +4034,9 @@ export function getAllTags(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `tag.create` permission. + */ export function createTag({ tagCreateDto }: { tagCreateDto: TagCreateDto; }, opts?: Oazapfts.RequestOpts) { @@ -3521,6 +4049,9 @@ export function createTag({ tagCreateDto }: { body: tagCreateDto }))); } +/** + * This endpoint requires the `tag.create` permission. + */ export function upsertTags({ tagUpsertDto }: { tagUpsertDto: TagUpsertDto; }, opts?: Oazapfts.RequestOpts) { @@ -3533,6 +4064,9 @@ export function upsertTags({ tagUpsertDto }: { body: tagUpsertDto }))); } +/** + * This endpoint requires the `tag.asset` permission. + */ export function bulkTagAssets({ tagBulkAssetsDto }: { tagBulkAssetsDto: TagBulkAssetsDto; }, opts?: Oazapfts.RequestOpts) { @@ -3545,6 +4079,9 @@ export function bulkTagAssets({ tagBulkAssetsDto }: { body: tagBulkAssetsDto }))); } +/** + * This endpoint requires the `tag.delete` permission. + */ export function deleteTag({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -3553,6 +4090,9 @@ export function deleteTag({ id }: { method: "DELETE" })); } +/** + * This endpoint requires the `tag.read` permission. + */ export function getTagById({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -3563,6 +4103,9 @@ export function getTagById({ id }: { ...opts })); } +/** + * This endpoint requires the `tag.update` permission. + */ export function updateTag({ id, tagUpdateDto }: { id: string; tagUpdateDto: TagUpdateDto; @@ -3576,6 +4119,9 @@ export function updateTag({ id, tagUpdateDto }: { body: tagUpdateDto }))); } +/** + * This endpoint requires the `tag.asset` permission. + */ export function untagAssets({ id, bulkIdsDto }: { id: string; bulkIdsDto: BulkIdsDto; @@ -3589,6 +4135,9 @@ export function untagAssets({ id, bulkIdsDto }: { body: bulkIdsDto }))); } +/** + * This endpoint requires the `tag.asset` permission. + */ export function tagAssets({ id, bulkIdsDto }: { id: string; bulkIdsDto: BulkIdsDto; @@ -3602,13 +4151,17 @@ export function tagAssets({ id, bulkIdsDto }: { body: bulkIdsDto }))); } -export function getTimeBucket({ albumId, isFavorite, isTrashed, key, order, personId, tagId, timeBucket, userId, visibility, withPartners, withStacked }: { +/** + * This endpoint requires the `asset.read` permission. + */ +export function getTimeBucket({ albumId, isFavorite, isTrashed, key, order, personId, slug, tagId, timeBucket, userId, visibility, withPartners, withStacked }: { albumId?: string; isFavorite?: boolean; isTrashed?: boolean; key?: string; order?: AssetOrder; personId?: string; + slug?: string; tagId?: string; timeBucket: string; userId?: string; @@ -3626,6 +4179,7 @@ export function getTimeBucket({ albumId, isFavorite, isTrashed, key, order, pers key, order, personId, + slug, tagId, timeBucket, userId, @@ -3636,13 +4190,17 @@ export function getTimeBucket({ albumId, isFavorite, isTrashed, key, order, pers ...opts })); } -export function getTimeBuckets({ albumId, isFavorite, isTrashed, key, order, personId, tagId, userId, visibility, withPartners, withStacked }: { +/** + * This endpoint requires the `asset.read` permission. + */ +export function getTimeBuckets({ albumId, isFavorite, isTrashed, key, order, personId, slug, tagId, userId, visibility, withPartners, withStacked }: { albumId?: string; isFavorite?: boolean; isTrashed?: boolean; key?: string; order?: AssetOrder; personId?: string; + slug?: string; tagId?: string; userId?: string; visibility?: AssetVisibility; @@ -3659,6 +4217,7 @@ export function getTimeBuckets({ albumId, isFavorite, isTrashed, key, order, per key, order, personId, + slug, tagId, userId, visibility, @@ -3668,6 +4227,9 @@ export function getTimeBuckets({ albumId, isFavorite, isTrashed, key, order, per ...opts })); } +/** + * This endpoint requires the `asset.delete` permission. + */ export function emptyTrash(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3677,6 +4239,9 @@ export function emptyTrash(opts?: Oazapfts.RequestOpts) { method: "POST" })); } +/** + * This endpoint requires the `asset.delete` permission. + */ export function restoreTrash(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3686,6 +4251,9 @@ export function restoreTrash(opts?: Oazapfts.RequestOpts) { method: "POST" })); } +/** + * This endpoint requires the `asset.delete` permission. + */ export function restoreAssets({ bulkIdsDto }: { bulkIdsDto: BulkIdsDto; }, opts?: Oazapfts.RequestOpts) { @@ -3698,6 +4266,9 @@ export function restoreAssets({ bulkIdsDto }: { body: bulkIdsDto }))); } +/** + * This endpoint requires the `user.read` permission. + */ export function searchUsers(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3706,6 +4277,9 @@ export function searchUsers(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `user.read` permission. + */ export function getMyUser(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3714,6 +4288,9 @@ export function getMyUser(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `user.update` permission. + */ export function updateMyUser({ userUpdateMeDto }: { userUpdateMeDto: UserUpdateMeDto; }, opts?: Oazapfts.RequestOpts) { @@ -3726,12 +4303,18 @@ export function updateMyUser({ userUpdateMeDto }: { body: userUpdateMeDto }))); } +/** + * This endpoint requires the `userLicense.delete` permission. + */ export function deleteUserLicense(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchText("/users/me/license", { ...opts, method: "DELETE" })); } +/** + * This endpoint requires the `userLicense.read` permission. + */ export function getUserLicense(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3740,6 +4323,9 @@ export function getUserLicense(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `userLicense.update` permission. + */ export function setUserLicense({ licenseKeyDto }: { licenseKeyDto: LicenseKeyDto; }, opts?: Oazapfts.RequestOpts) { @@ -3752,12 +4338,18 @@ export function setUserLicense({ licenseKeyDto }: { body: licenseKeyDto }))); } +/** + * This endpoint requires the `userOnboarding.delete` permission. + */ export function deleteUserOnboarding(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchText("/users/me/onboarding", { ...opts, method: "DELETE" })); } +/** + * This endpoint requires the `userOnboarding.read` permission. + */ export function getUserOnboarding(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3766,6 +4358,9 @@ export function getUserOnboarding(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `userOnboarding.update` permission. + */ export function setUserOnboarding({ onboardingDto }: { onboardingDto: OnboardingDto; }, opts?: Oazapfts.RequestOpts) { @@ -3778,6 +4373,9 @@ export function setUserOnboarding({ onboardingDto }: { body: onboardingDto }))); } +/** + * This endpoint requires the `userPreference.read` permission. + */ export function getMyPreferences(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3786,6 +4384,9 @@ export function getMyPreferences(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This endpoint requires the `userPreference.update` permission. + */ export function updateMyPreferences({ userPreferencesUpdateDto }: { userPreferencesUpdateDto: UserPreferencesUpdateDto; }, opts?: Oazapfts.RequestOpts) { @@ -3798,12 +4399,18 @@ export function updateMyPreferences({ userPreferencesUpdateDto }: { body: userPreferencesUpdateDto }))); } +/** + * This endpoint requires the `userProfileImage.delete` permission. + */ export function deleteProfileImage(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchText("/users/profile-image", { ...opts, method: "DELETE" })); } +/** + * This endpoint requires the `userProfileImage.update` permission. + */ export function createProfileImage({ createProfileImageDto }: { createProfileImageDto: CreateProfileImageDto; }, opts?: Oazapfts.RequestOpts) { @@ -3816,6 +4423,9 @@ export function createProfileImage({ createProfileImageDto }: { body: createProfileImageDto }))); } +/** + * This endpoint requires the `user.read` permission. + */ export function getUser({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -3826,6 +4436,9 @@ export function getUser({ id }: { ...opts })); } +/** + * This endpoint requires the `userProfileImage.read` permission. + */ export function getProfileImage({ id }: { id: string; }, opts?: Oazapfts.RequestOpts) { @@ -3938,25 +4551,35 @@ export enum Permission { AssetRead = "asset.read", AssetUpdate = "asset.update", AssetDelete = "asset.delete", + AssetStatistics = "asset.statistics", AssetShare = "asset.share", AssetView = "asset.view", AssetDownload = "asset.download", AssetUpload = "asset.upload", + AssetReplace = "asset.replace", AlbumCreate = "album.create", AlbumRead = "album.read", AlbumUpdate = "album.update", AlbumDelete = "album.delete", AlbumStatistics = "album.statistics", - AlbumAddAsset = "album.addAsset", - AlbumRemoveAsset = "album.removeAsset", AlbumShare = "album.share", AlbumDownload = "album.download", + AlbumAssetCreate = "albumAsset.create", + AlbumAssetDelete = "albumAsset.delete", + AlbumUserCreate = "albumUser.create", + AlbumUserUpdate = "albumUser.update", + AlbumUserDelete = "albumUser.delete", + AuthChangePassword = "auth.changePassword", AuthDeviceDelete = "authDevice.delete", ArchiveRead = "archive.read", + DuplicateRead = "duplicate.read", + DuplicateDelete = "duplicate.delete", FaceCreate = "face.create", FaceRead = "face.read", FaceUpdate = "face.update", FaceDelete = "face.delete", + JobCreate = "job.create", + JobRead = "job.read", LibraryCreate = "library.create", LibraryRead = "library.read", LibraryUpdate = "library.update", @@ -3968,6 +4591,9 @@ export enum Permission { MemoryRead = "memory.read", MemoryUpdate = "memory.update", MemoryDelete = "memory.delete", + MemoryStatistics = "memory.statistics", + MemoryAssetCreate = "memoryAsset.create", + MemoryAssetDelete = "memoryAsset.delete", NotificationCreate = "notification.create", NotificationRead = "notification.read", NotificationUpdate = "notification.update", @@ -3983,6 +4609,16 @@ export enum Permission { PersonStatistics = "person.statistics", PersonMerge = "person.merge", PersonReassign = "person.reassign", + PinCodeCreate = "pinCode.create", + PinCodeUpdate = "pinCode.update", + PinCodeDelete = "pinCode.delete", + ServerAbout = "server.about", + ServerApkLinks = "server.apkLinks", + ServerStorage = "server.storage", + ServerStatistics = "server.statistics", + ServerLicenseRead = "serverLicense.read", + ServerLicenseUpdate = "serverLicense.update", + ServerLicenseDelete = "serverLicense.delete", SessionCreate = "session.create", SessionRead = "session.read", SessionUpdate = "session.update", @@ -3996,6 +4632,10 @@ export enum Permission { StackRead = "stack.read", StackUpdate = "stack.update", StackDelete = "stack.delete", + SyncStream = "sync.stream", + SyncCheckpointRead = "syncCheckpoint.read", + SyncCheckpointUpdate = "syncCheckpoint.update", + SyncCheckpointDelete = "syncCheckpoint.delete", SystemConfigRead = "systemConfig.read", SystemConfigUpdate = "systemConfig.update", SystemMetadataRead = "systemMetadata.read", @@ -4005,10 +4645,25 @@ export enum Permission { TagUpdate = "tag.update", TagDelete = "tag.delete", TagAsset = "tag.asset", - AdminUserCreate = "admin.user.create", - AdminUserRead = "admin.user.read", - AdminUserUpdate = "admin.user.update", - AdminUserDelete = "admin.user.delete" + UserRead = "user.read", + UserUpdate = "user.update", + UserLicenseCreate = "userLicense.create", + UserLicenseRead = "userLicense.read", + UserLicenseUpdate = "userLicense.update", + UserLicenseDelete = "userLicense.delete", + UserOnboardingRead = "userOnboarding.read", + UserOnboardingUpdate = "userOnboarding.update", + UserOnboardingDelete = "userOnboarding.delete", + UserPreferenceRead = "userPreference.read", + UserPreferenceUpdate = "userPreference.update", + UserProfileImageCreate = "userProfileImage.create", + UserProfileImageRead = "userProfileImage.read", + UserProfileImageUpdate = "userProfileImage.update", + UserProfileImageDelete = "userProfileImage.delete", + AdminUserCreate = "adminUser.create", + AdminUserRead = "adminUser.read", + AdminUserUpdate = "adminUser.update", + AdminUserDelete = "adminUser.delete" } export enum AssetMediaStatus { Created = "created", @@ -4090,6 +4745,7 @@ export enum Error2 { NotFound = "not_found" } export enum SyncEntityType { + AuthUserV1 = "AuthUserV1", UserV1 = "UserV1", UserDeleteV1 = "UserDeleteV1", AssetV1 = "AssetV1", @@ -4125,6 +4781,8 @@ export enum SyncEntityType { StackDeleteV1 = "StackDeleteV1", PersonV1 = "PersonV1", PersonDeleteV1 = "PersonDeleteV1", + AssetFaceV1 = "AssetFaceV1", + AssetFaceDeleteV1 = "AssetFaceDeleteV1", UserMetadataV1 = "UserMetadataV1", UserMetadataDeleteV1 = "UserMetadataDeleteV1", SyncAckV1 = "SyncAckV1", @@ -4138,6 +4796,7 @@ export enum SyncRequestType { AlbumAssetExifsV1 = "AlbumAssetExifsV1", AssetsV1 = "AssetsV1", AssetExifsV1 = "AssetExifsV1", + AuthUsersV1 = "AuthUsersV1", MemoriesV1 = "MemoriesV1", MemoryToAssetsV1 = "MemoryToAssetsV1", PartnersV1 = "PartnersV1", @@ -4147,6 +4806,7 @@ export enum SyncRequestType { StacksV1 = "StacksV1", UsersV1 = "UsersV1", PeopleV1 = "PeopleV1", + AssetFacesV1 = "AssetFacesV1", UserMetadataV1 = "UserMetadataV1" } export enum TranscodeHWAccel { diff --git a/server/.nvmrc b/server/.nvmrc index fc37597bcc..7377d130ed 100644 --- a/server/.nvmrc +++ b/server/.nvmrc @@ -1 +1 @@ -22.17.0 +22.17.1 diff --git a/server/Dockerfile b/server/Dockerfile index 0b3b864c95..c922c5b291 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -2,25 +2,26 @@ FROM ghcr.io/immich-app/base-server-dev:202507162011@sha256:85d4230c2208646bd6c528db41b2213d780b11b7a311397ca6a2aaba7cf697c8 AS dev WORKDIR /usr/src/app -COPY server/package.json server/package-lock.json ./ +COPY ./server/package* ./server/ +WORKDIR /usr/src/app/server RUN npm ci && \ - # exiftool-vendored.pl, sharp-linux-x64 and sharp-linux-arm64 are the only ones we need - # they're marked as optional dependencies, so we need to copy them manually after pruning - rm -rf node_modules/@img/sharp-libvips* && \ - rm -rf node_modules/@img/sharp-linuxmusl-x64 -ENV PATH="${PATH}:/usr/src/app/bin" \ - IMMICH_ENV=development \ - NVIDIA_DRIVER_CAPABILITIES=all \ - NVIDIA_VISIBLE_DEVICES=all -ENTRYPOINT ["tini", "--", "/bin/sh"] + # exiftool-vendored.pl, sharp-linux-x64 and sharp-linux-arm64 are the only ones we need + # they're marked as optional dependencies, so we need to copy them manually after pruning + rm -rf node_modules/@img/sharp-libvips* && \ + rm -rf node_modules/@img/sharp-linuxmusl-x64 +ENV PATH="${PATH}:/usr/src/app/server/bin" \ + IMMICH_ENV=development \ + NVIDIA_DRIVER_CAPABILITIES=all \ + NVIDIA_VISIBLE_DEVICES=all +ENTRYPOINT ["tini", "--", "/bin/bash", "-c"] FROM dev AS dev-container-server RUN rm -rf /usr/src/app RUN apt-get update && \ - apt-get install sudo inetutils-ping openjdk-11-jre-headless \ - vim nano \ - -y --no-install-recommends --fix-missing + apt-get install sudo inetutils-ping openjdk-11-jre-headless \ + vim nano \ + -y --no-install-recommends --fix-missing RUN usermod -aG sudo node RUN echo "node ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers @@ -38,19 +39,19 @@ FROM dev-container-server AS dev-container-mobile USER root # Enable multiarch for arm64 if necessary RUN if [ "$(dpkg --print-architecture)" = "arm64" ]; then \ - dpkg --add-architecture amd64 && \ - apt-get update && \ - apt-get install -y --no-install-recommends \ - qemu-user-static \ - libc6:amd64 \ - libstdc++6:amd64 \ - libgcc1:amd64; \ - fi + dpkg --add-architecture amd64 && \ + apt-get update && \ + apt-get install -y --no-install-recommends \ + qemu-user-static \ + libc6:amd64 \ + libstdc++6:amd64 \ + libgcc1:amd64; \ + fi # Flutter SDK # https://flutter.dev/docs/development/tools/sdk/releases?tab=linux ENV FLUTTER_CHANNEL="stable" -ENV FLUTTER_VERSION="3.32.6" +ENV FLUTTER_VERSION="3.32.8" ENV FLUTTER_HOME=/flutter ENV PATH=${PATH}:${FLUTTER_HOME}/bin @@ -77,45 +78,42 @@ FROM dev AS prod COPY server . RUN npm run build RUN npm prune --omit=dev --omit=optional -COPY --from=dev /usr/src/app/node_modules/@img ./node_modules/@img -COPY --from=dev /usr/src/app/node_modules/exiftool-vendored.pl ./node_modules/exiftool-vendored.pl +COPY --from=dev /usr/src/app/server/node_modules/@img ./node_modules/@img +COPY --from=dev /usr/src/app/server/node_modules/exiftool-vendored.pl ./node_modules/exiftool-vendored.pl # web build FROM node:22.16.0-alpine3.20@sha256:2289fb1fba0f4633b08ec47b94a89c7e20b829fc5679f9b7b298eaa2f1ed8b7e AS web -WORKDIR /usr/src/open-api/typescript-sdk -COPY open-api/typescript-sdk/package*.json open-api/typescript-sdk/tsconfig*.json ./ -RUN npm ci -COPY open-api/typescript-sdk/ ./ -RUN npm run build - WORKDIR /usr/src/app -COPY web/package*.json web/svelte.config.js ./ -RUN npm ci -COPY web ./ -COPY i18n ../i18n -RUN npm run build +COPY ./web ./web/ +COPY ./i18n ./i18n/ +COPY ./open-api/typescript-sdk ./open-api/typescript-sdk/ +WORKDIR /usr/src/app/open-api/typescript-sdk +RUN npm ci && npm run build + +WORKDIR /usr/src/app/web +RUN npm ci && npm run build # prod build FROM ghcr.io/immich-app/base-server-prod:202507162011@sha256:636f3ddb6106628ef851d51c23f3fa2c6e4829390cc315b27b38c288c82b23a7 WORKDIR /usr/src/app ENV NODE_ENV=production \ - NVIDIA_DRIVER_CAPABILITIES=all \ - NVIDIA_VISIBLE_DEVICES=all -COPY --from=prod /usr/src/app/node_modules ./node_modules -COPY --from=prod /usr/src/app/dist ./dist -COPY --from=prod /usr/src/app/bin ./bin -COPY --from=web /usr/src/app/build /build/www -COPY server/resources resources -COPY server/package.json server/package-lock.json ./ -COPY server/start*.sh ./ -COPY "docker/scripts/get-cpus.sh" ./ -RUN npm install -g @immich/cli && npm cache clean --force + NVIDIA_DRIVER_CAPABILITIES=all \ + NVIDIA_VISIBLE_DEVICES=all + +COPY --from=prod /usr/src/app/server/node_modules ./server/node_modules +COPY --from=prod /usr/src/app/server/dist ./server/dist +COPY --from=prod /usr/src/app/server/bin ./server/bin +COPY --from=web /usr/src/app/web/build /build/www +COPY ./server/resources ./server/resources +COPY ./server/package.json server/package-lock.json ./ COPY LICENSE /licenses/LICENSE.txt COPY LICENSE /LICENSE -ENV PATH="${PATH}:/usr/src/app/bin" + +RUN npm install -g @immich/cli && npm cache clean --force +ENV PATH="${PATH}:/usr/src/app/server/bin" ARG BUILD_ID ARG BUILD_IMAGE @@ -132,9 +130,9 @@ ENV IMMICH_SOURCE_REF=${BUILD_SOURCE_REF} ENV IMMICH_SOURCE_COMMIT=${BUILD_SOURCE_COMMIT} ENV IMMICH_SOURCE_URL=https://github.com/immich-app/immich/commit/${BUILD_SOURCE_COMMIT} -VOLUME /usr/src/app/upload +VOLUME /data EXPOSE 2283 -ENTRYPOINT ["tini", "--", "/bin/bash"] +ENTRYPOINT ["tini", "--", "/bin/bash", "-c"] CMD ["start.sh"] HEALTHCHECK CMD immich-healthcheck diff --git a/docker/scripts/get-cpus.sh b/server/bin/get-cpus.sh similarity index 100% rename from docker/scripts/get-cpus.sh rename to server/bin/get-cpus.sh diff --git a/server/bin/immich-admin b/server/bin/immich-admin index 30fd33a20a..0465a362b8 100755 --- a/server/bin/immich-admin +++ b/server/bin/immich-admin @@ -1,3 +1,3 @@ #!/usr/bin/env sh -/usr/src/app/start.sh immich-admin "$@" +start.sh immich-admin "$@" diff --git a/server/bin/immich-dev b/server/bin/immich-dev index 177455d037..e861c0ee06 100755 --- a/server/bin/immich-dev +++ b/server/bin/immich-dev @@ -1,3 +1,9 @@ #!/usr/bin/env bash -node /usr/src/app/node_modules/.bin/nest start --debug "0.0.0.0:9230" --watch -- "$@" +if [ "$IMMICH_ENV" != "development" ]; then + echo "This command can only be run in development environments" + exit 1 +fi + +cd /usr/src/app/server || exit 1 +npm exec nest -- start --debug "0.0.0.0:9230" --watch -- "$@" diff --git a/server/bin/start.sh b/server/bin/start.sh new file mode 100755 index 0000000000..10f897dd8e --- /dev/null +++ b/server/bin/start.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +echo "Initializing Immich $IMMICH_SOURCE_REF" + +lib_path="/usr/lib/$(arch)-linux-gnu/libmimalloc.so.2" +if [ -f "$lib_path" ]; then + export LD_PRELOAD="$lib_path" +else + echo "skipping libmimalloc - path not found $lib_path" +fi +export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/lib/jellyfin-ffmpeg/lib" +SERVER_HOME=/usr/src/app/server + +read_file_and_export() { + fname="${!1}" + if [[ -z $fname ]] && [[ -e "$CREDENTIALS_DIRECTORY/$2" ]]; then + fname="${CREDENTIALS_DIRECTORY}/$2" + fi + if [[ -n $fname ]]; then + content="$(< "$fname")" + export "$2"="${content}" + unset "$1" + fi +} +read_file_and_export "DB_URL_FILE" "DB_URL" +read_file_and_export "DB_HOSTNAME_FILE" "DB_HOSTNAME" +read_file_and_export "DB_DATABASE_NAME_FILE" "DB_DATABASE_NAME" +read_file_and_export "DB_USERNAME_FILE" "DB_USERNAME" +read_file_and_export "DB_PASSWORD_FILE" "DB_PASSWORD" +read_file_and_export "REDIS_PASSWORD_FILE" "REDIS_PASSWORD" + +if CPU_CORES="${CPU_CORES:=$(get-cpus.sh 2>/dev/null)}"; then + echo "Detected CPU Cores: $CPU_CORES" + if [ "$CPU_CORES" -gt 4 ]; then + export UV_THREADPOOL_SIZE=$CPU_CORES + fi +else + echo "skipping get-cpus.sh - not found in PATH or failed: using default UV_THREADPOOL_SIZE" +fi + +if [ -f "${SERVER_HOME}/dist/main.js" ]; then + exec node "${SERVER_HOME}/dist/main.js" "$@" +else + echo "Error: ${SERVER_HOME}/dist/main.js not found" + if [ "$IMMICH_ENV" = "development" ]; then + echo "You may need to build the server first." + fi + exit 1 +fi diff --git a/server/package-lock.json b/server/package-lock.json index 11be49ecb6..ed2dc2e14f 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -1,12 +1,12 @@ { "name": "immich", - "version": "1.135.3", + "version": "1.137.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich", - "version": "1.135.3", + "version": "1.137.3", "license": "GNU Affero General Public License version 3", "dependencies": { "@nestjs/bullmq": "^11.0.1", @@ -30,7 +30,7 @@ "@opentelemetry/sdk-metrics": "^2.0.1", "@opentelemetry/sdk-node": "^0.203.0", "@opentelemetry/semantic-conventions": "^1.34.0", - "@react-email/components": "^0.2.0", + "@react-email/components": "^0.3.0", "@react-email/render": "^1.1.2", "@socket.io/redis-adapter": "^8.3.0", "archiver": "^7.0.0", @@ -54,7 +54,7 @@ "i18n-iso-countries": "^7.6.0", "ioredis": "^5.3.2", "js-yaml": "^4.1.0", - "kysely": "^0.28.0", + "kysely": "0.28.2", "kysely-postgres-js": "^2.0.0", "lodash": "^4.17.21", "luxon": "^3.4.2", @@ -108,8 +108,8 @@ "@types/lodash": "^4.14.197", "@types/luxon": "^3.6.2", "@types/mock-fs": "^4.13.1", - "@types/multer": "^1.4.7", - "@types/node": "^22.15.33", + "@types/multer": "^2.0.0", + "@types/node": "^22.16.5", "@types/nodemailer": "^6.4.14", "@types/picomatch": "^4.0.0", "@types/pngjs": "^6.0.5", @@ -122,7 +122,7 @@ "@vitest/coverage-v8": "^3.0.0", "canvas": "^3.1.0", "eslint": "^9.14.0", - "eslint-config-prettier": "^10.0.0", + "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-unicorn": "^59.0.0", "globals": "^16.0.0", @@ -297,6 +297,19 @@ "dev": true, "license": "MIT" }, + "node_modules/@angular-devkit/schematics-cli/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@angular-devkit/schematics-cli/node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -369,6 +382,19 @@ "dev": true, "license": "MIT" }, + "node_modules/@angular-devkit/schematics/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@angular-devkit/schematics/node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -389,22 +415,6 @@ "node": ">= 8" } }, - "node_modules/@asamuzakjp/css-color": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz", - "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@csstools/css-calc": "^2.1.3", - "@csstools/css-color-parser": "^3.0.9", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "lru-cache": "^10.4.3" - } - }, "node_modules/@babel/code-frame": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", @@ -556,131 +566,6 @@ "node": ">=0.1.90" } }, - "node_modules/@csstools/color-helpers": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.2.tgz", - "integrity": "sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "optional": true, - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@csstools/css-calc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", - "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-color-parser": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.10.tgz", - "integrity": "sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@csstools/color-helpers": "^5.0.2", - "@csstools/css-calc": "^2.1.4" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-parser-algorithms": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", - "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-tokenizer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", - "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=18" - } - }, "node_modules/@emnapi/runtime": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz", @@ -1159,9 +1044,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", - "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", + "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1209,9 +1094,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.30.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.30.1.tgz", - "integrity": "sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg==", + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", + "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", "dev": true, "license": "MIT", "engines": { @@ -1245,19 +1130,6 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.0.tgz", - "integrity": "sha512-b7ePw78tEWWkpgZCDYkbqDOP8dmM6qe+AOC6iuJqlq1R/0ahMAeH3qynpnqKFGkMltrp44ohV4ubGyvLX28tzw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, "node_modules/@golevelup/nestjs-discovery": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/@golevelup/nestjs-discovery/-/nestjs-discovery-4.0.3.tgz", @@ -2736,6 +2608,19 @@ "dev": true, "license": "MIT" }, + "node_modules/@nestjs/cli/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@nestjs/cli/node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -2757,9 +2642,9 @@ } }, "node_modules/@nestjs/common": { - "version": "11.1.3", - "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.3.tgz", - "integrity": "sha512-ogEK+GriWodIwCw6buQ1rpcH4Kx+G7YQ9EwuPySI3rS05pSdtQ++UhucjusSI9apNidv+QURBztJkRecwwJQXg==", + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.5.tgz", + "integrity": "sha512-DQpWdr3ShO0BHWkHl3I4W/jR6R3pDtxyBlmrpTuZF+PXxQyBXNvsUne0Wyo6QHPEDi+pAz9XchBFoKbqOhcdTg==", "license": "MIT", "dependencies": { "file-type": "21.0.0", @@ -2788,9 +2673,9 @@ } }, "node_modules/@nestjs/core": { - "version": "11.1.3", - "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.3.tgz", - "integrity": "sha512-5lTni0TCh8x7bXETRD57pQFnKnEg1T6M+VLE7wAmyQRIecKQU+2inRGZD+A4v2DC1I04eA0WffP0GKLxjOKlzw==", + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.5.tgz", + "integrity": "sha512-Qr25MEY9t8VsMETy7eXQ0cNXqu0lzuFrrTr+f+1G57ABCtV5Pogm7n9bF71OU2bnkDD32Bi4hQLeFR90cku3Tw==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -2862,14 +2747,14 @@ } }, "node_modules/@nestjs/platform-express": { - "version": "11.1.3", - "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.3.tgz", - "integrity": "sha512-hEDNMlaPiBO72fxxX/CuRQL3MEhKRc/sIYGVoXjrnw6hTxZdezvvM6A95UaLsYknfmcZZa/CdG1SMBZOu9agHQ==", + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.5.tgz", + "integrity": "sha512-OsoiUBY9Shs5IG3uvDIt9/IDfY5OlvWBESuB/K4Eun8xILw1EK5d5qMfC3d2sIJ+kA3l+kBR1d/RuzH7VprLIg==", "license": "MIT", "dependencies": { "cors": "2.8.5", "express": "5.1.0", - "multer": "2.0.1", + "multer": "2.0.2", "path-to-regexp": "8.2.0", "tslib": "2.8.1" }, @@ -2882,71 +2767,10 @@ "@nestjs/core": "^11.0.0" } }, - "node_modules/@nestjs/platform-express/node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@nestjs/platform-express/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@nestjs/platform-express/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@nestjs/platform-express/node_modules/multer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.1.tgz", - "integrity": "sha512-Ug8bXeTIUlxurg8xLTEskKShvcKDZALo1THEX5E41pYCD2sCVub5/kIRIGqWNoqV6szyLyQKV6mD4QUrWE5GCQ==", - "license": "MIT", - "dependencies": { - "append-field": "^1.0.0", - "busboy": "^1.6.0", - "concat-stream": "^2.0.0", - "mkdirp": "^0.5.6", - "object-assign": "^4.1.1", - "type-is": "^1.6.18", - "xtend": "^4.0.2" - }, - "engines": { - "node": ">= 10.16.0" - } - }, - "node_modules/@nestjs/platform-express/node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/@nestjs/platform-socket.io": { - "version": "11.1.3", - "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-11.1.3.tgz", - "integrity": "sha512-jQ+ccprmh3kKolBp+bb97zoaS3vKaiyeNqyctGqV4CSG8P6mXSaaUObWxAsw6Jdgn5YQAVEBWJ6FhvF4s6QZbg==", + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-11.1.5.tgz", + "integrity": "sha512-DY3zNY+BjbrYpV/t8HL8ptrusrWK8J0cfkfY1iZsfCd+0/1+j8IKno+QMLkerNQAZ7/Frh5tkaKHVwWk18TkMw==", "license": "MIT", "dependencies": { "socket.io": "4.8.1", @@ -3063,6 +2887,19 @@ "dev": true, "license": "MIT" }, + "node_modules/@nestjs/schematics/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@nestjs/schematics/node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -3117,9 +2954,9 @@ } }, "node_modules/@nestjs/testing": { - "version": "11.1.3", - "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-11.1.3.tgz", - "integrity": "sha512-CeXG6/eEqgFIkPkmU00y18Dd3DLOIDFhPItzJK1SWckKo6IhcnfoRJzGx75bmuvUMjb51j6An96S/+MJ2ty9jA==", + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-11.1.5.tgz", + "integrity": "sha512-ZYRYF750SefmuIo7ZqPlHDcin1OHh6My0OkOfGEFjrD9mJ0vMVIpwMTOOkpzCfCcpqUuxeHBuecpiIn+NLrQbQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3145,9 +2982,9 @@ } }, "node_modules/@nestjs/websockets": { - "version": "11.1.3", - "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-11.1.3.tgz", - "integrity": "sha512-IjhWKfRf0D247JxYIEs8USblJJbcxUsKJpzbCPaZ7TrVy4LrpG3IRQDlSTOw599TRIYP5ixyH9C0+v5DyaI9uA==", + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-11.1.5.tgz", + "integrity": "sha512-mAM11HwyS7aeSUbXdOqHbNCRoHwB0OOb+cmx5sgxvszhdG0Y6bwR60nKA4+EXL9xUEeWoxmbfLmSHlTSIJ9GKA==", "license": "MIT", "dependencies": { "iterare": "1.2.1", @@ -5838,9 +5675,9 @@ } }, "node_modules/@opentelemetry/semantic-conventions": { - "version": "1.34.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.34.0.tgz", - "integrity": "sha512-aKcOkyrorBGlajjRdVoJWHTxfxO1vCNHLJVlSDaRHDIdjU+pX8IYQPvPDkYiujKLbRnWU+1TBwEt0QRgSm4SGA==", + "version": "1.36.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.36.0.tgz", + "integrity": "sha512-TtxJSRD8Ohxp6bKkhrm27JRHAxPczQA7idtcTOMYI+wQRRrfgqxHv1cFbCApcSnNjtXkmzFozn6jQtFrOmbjPQ==", "license": "Apache-2.0", "engines": { "node": ">=14" @@ -6031,9 +5868,9 @@ } }, "node_modules/@react-email/components": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@react-email/components/-/components-0.2.0.tgz", - "integrity": "sha512-y45D+oYDgvL1fuFnauwUk8MwT54l0hWwnUAzzP0bVuwhsmVJFelKOGGMCRch0pcgyINilVlAEk0Xjtcu0Su4cw==", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@react-email/components/-/components-0.3.2.tgz", + "integrity": "sha512-nVbo0KtBdZbj19lvfFpe0ZhjKPh6LE229+NyQLuTDt6dfaLzNRpSu/rHP+jlvdWBAk93slsoGyWDRldbqklpaA==", "license": "MIT", "dependencies": { "@react-email/body": "0.0.11", @@ -6054,7 +5891,7 @@ "@react-email/render": "1.1.3", "@react-email/row": "0.0.12", "@react-email/section": "0.0.16", - "@react-email/tailwind": "1.1.0", + "@react-email/tailwind": "1.2.2", "@react-email/text": "0.1.5" }, "engines": { @@ -6227,9 +6064,9 @@ } }, "node_modules/@react-email/tailwind": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@react-email/tailwind/-/tailwind-1.1.0.tgz", - "integrity": "sha512-m4sh5d1c8P9TPA6Ea8qHrboE5s9PmRQREIreYMn1l5ca0pCV/UBEY15e1RgoaseAzy2cy+gwI+nKhMwqUJsD1g==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@react-email/tailwind/-/tailwind-1.2.2.tgz", + "integrity": "sha512-heO9Khaqxm6Ulm6p7HQ9h01oiiLRrZuuEQuYds/O7Iyp3c58sMVHZGIxiRXO/kSs857NZQycpjewEVKF3jhNTw==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -6627,9 +6464,9 @@ "license": "MIT" }, "node_modules/@swc/core": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.12.11.tgz", - "integrity": "sha512-P3GM+0lqjFctcp5HhR9mOcvLSX3SptI9L1aux0Fuvgt8oH4f92rCUrkodAa0U2ktmdjcyIiG37xg2mb/dSCYSA==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.13.2.tgz", + "integrity": "sha512-YWqn+0IKXDhqVLKoac4v2tV6hJqB/wOh8/Br8zjqeqBkKa77Qb0Kw2i7LOFzjFNZbZaPH6AlMGlBwNrxaauaAg==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", @@ -6645,16 +6482,16 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.12.11", - "@swc/core-darwin-x64": "1.12.11", - "@swc/core-linux-arm-gnueabihf": "1.12.11", - "@swc/core-linux-arm64-gnu": "1.12.11", - "@swc/core-linux-arm64-musl": "1.12.11", - "@swc/core-linux-x64-gnu": "1.12.11", - "@swc/core-linux-x64-musl": "1.12.11", - "@swc/core-win32-arm64-msvc": "1.12.11", - "@swc/core-win32-ia32-msvc": "1.12.11", - "@swc/core-win32-x64-msvc": "1.12.11" + "@swc/core-darwin-arm64": "1.13.2", + "@swc/core-darwin-x64": "1.13.2", + "@swc/core-linux-arm-gnueabihf": "1.13.2", + "@swc/core-linux-arm64-gnu": "1.13.2", + "@swc/core-linux-arm64-musl": "1.13.2", + "@swc/core-linux-x64-gnu": "1.13.2", + "@swc/core-linux-x64-musl": "1.13.2", + "@swc/core-win32-arm64-msvc": "1.13.2", + "@swc/core-win32-ia32-msvc": "1.13.2", + "@swc/core-win32-x64-msvc": "1.13.2" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" @@ -6666,9 +6503,9 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.12.11.tgz", - "integrity": "sha512-J19Jj9Y5x/N0loExH7W0OI9OwwoVyxutDdkyq1o/kgXyBqmmzV7Y/Q9QekI2Fm/qc5mNeAdP7aj4boY4AY/JPw==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.13.2.tgz", + "integrity": "sha512-44p7ivuLSGFJ15Vly4ivLJjg3ARo4879LtEBAabcHhSZygpmkP8eyjyWxrH3OxkY1eRZSIJe8yRZPFw4kPXFPw==", "cpu": [ "arm64" ], @@ -6683,9 +6520,9 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.12.11.tgz", - "integrity": "sha512-PTuUQrfStQ6cjW+uprGO2lpQHy84/l0v+GqRqq8s/jdK55rFRjMfCeyf6FAR0l6saO5oNOQl+zWR1aNpj8pMQw==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.13.2.tgz", + "integrity": "sha512-Lb9EZi7X2XDAVmuUlBm2UvVAgSCbD3qKqDCxSI4jEOddzVOpNCnyZ/xEampdngUIyDDhhJLYU9duC+Mcsv5Y+A==", "cpu": [ "x64" ], @@ -6700,9 +6537,9 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.12.11.tgz", - "integrity": "sha512-poxBq152HsupOtnZilenvHmxZ9a8SRj4LtfxUnkMDNOGrZR9oxbQNwEzNKfi3RXEcXz+P8c0Rai1ubBazXv8oQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.13.2.tgz", + "integrity": "sha512-9TDe/92ee1x57x+0OqL1huG4BeljVx0nWW4QOOxp8CCK67Rpc/HHl2wciJ0Kl9Dxf2NvpNtkPvqj9+BUmM9WVA==", "cpu": [ "arm" ], @@ -6717,9 +6554,9 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.12.11.tgz", - "integrity": "sha512-y1HNamR/D0Hc8xIE910ysyLe269UYiGaQPoLjQS0phzWFfWdMj9bHM++oydVXZ4RSWycO7KyJ3uvw4NilvyMKQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.13.2.tgz", + "integrity": "sha512-KJUSl56DBk7AWMAIEcU83zl5mg3vlQYhLELhjwRFkGFMvghQvdqQ3zFOYa4TexKA7noBZa3C8fb24rI5sw9Exg==", "cpu": [ "arm64" ], @@ -6734,9 +6571,9 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.12.11.tgz", - "integrity": "sha512-LlBxPh/32pyQsu2emMEOFRm7poEFLsw12Y1mPY7FWZiZeptomKSOSHRzKDz9EolMiV4qhK1caP1lvW4vminYgQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.13.2.tgz", + "integrity": "sha512-teU27iG1oyWpNh9CzcGQ48ClDRt/RCem7mYO7ehd2FY102UeTws2+OzLESS1TS1tEZipq/5xwx3FzbVgiolCiQ==", "cpu": [ "arm64" ], @@ -6751,9 +6588,9 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.12.11.tgz", - "integrity": "sha512-bOjiZB8O/1AzHkzjge1jqX62HGRIpOHqFUrGPfAln/NC6NR+Z2A78u3ixV7k5KesWZFhCV0YVGJL+qToL27myA==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.13.2.tgz", + "integrity": "sha512-dRPsyPyqpLD0HMRCRpYALIh4kdOir8pPg4AhNQZLehKowigRd30RcLXGNVZcc31Ua8CiPI4QSgjOIxK+EQe4LQ==", "cpu": [ "x64" ], @@ -6768,9 +6605,9 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.12.11.tgz", - "integrity": "sha512-4dzAtbT/m3/UjF045+33gLiHd8aSXJDoqof7gTtu4q0ZyAf7XJ3HHspz+/AvOJLVo4FHHdFcdXhmo/zi1nFn8A==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.13.2.tgz", + "integrity": "sha512-CCxETW+KkYEQDqz1SYC15YIWYheqFC+PJVOW76Maa/8yu8Biw+HTAcblKf2isrlUtK8RvrQN94v3UXkC2NzCEw==", "cpu": [ "x64" ], @@ -6785,9 +6622,9 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.12.11.tgz", - "integrity": "sha512-h8HiwBZErKvCAmjW92JvQp0iOqm6bncU4ac5jxBGkRApabpUenNJcj3h2g5O6GL5K6T9/WhnXE5gyq/s1fhPQg==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.13.2.tgz", + "integrity": "sha512-Wv/QTA6PjyRLlmKcN6AmSI4jwSMRl0VTLGs57PHTqYRwwfwd7y4s2fIPJVBNbAlXd795dOEP6d/bGSQSyhOX3A==", "cpu": [ "arm64" ], @@ -6802,9 +6639,9 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.12.11.tgz", - "integrity": "sha512-1pwr325mXRNUhxTtXmx1IokV5SiRL+6iDvnt3FRXj+X5UvXXKtg2zeyftk+03u8v8v8WUr5I32hIypVJPTNxNg==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.13.2.tgz", + "integrity": "sha512-PuCdtNynEkUNbUXX/wsyUC+t4mamIU5y00lT5vJcAvco3/r16Iaxl5UCzhXYaWZSNVZMzPp9qN8NlSL8M5pPxw==", "cpu": [ "ia32" ], @@ -6819,9 +6656,9 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.12.11", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.12.11.tgz", - "integrity": "sha512-5gggWo690Gvs7XiPxAmb5tHwzB9RTVXUV7AWoGb6bmyUd1OXYaebQF0HAOtade5jIoNhfQMQJ7QReRgt/d2jAA==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.13.2.tgz", + "integrity": "sha512-qlmMkFZJus8cYuBURx1a3YAG2G7IW44i+FEYV5/32ylKkzGNAr9tDJSA53XNnNXkAB5EXSPsOz7bn5C3JlEtdQ==", "cpu": [ "x64" ], @@ -6842,18 +6679,6 @@ "dev": true, "license": "Apache-2.0" }, - "node_modules/@swc/helpers": { - "version": "0.5.17", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", - "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.8.0" - } - }, "node_modules/@swc/types": { "version": "0.1.23", "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.23.tgz", @@ -6865,23 +6690,23 @@ } }, "node_modules/@testcontainers/postgresql": { - "version": "11.2.1", - "resolved": "https://registry.npmjs.org/@testcontainers/postgresql/-/postgresql-11.2.1.tgz", - "integrity": "sha512-u0XLsjUmAHaUmB9Q1bitBu8uoxRKteDI65S5/zpJ6TeZabx9qB4EENwKqzuqEwOCzzlko9at7ZY4frwRcchgvA==", + "version": "11.4.0", + "resolved": "https://registry.npmjs.org/@testcontainers/postgresql/-/postgresql-11.4.0.tgz", + "integrity": "sha512-WiKsz3Np5twNZGp2kgatqGaE/KqNR271CPwvIgAvFyN7E581P34glQljM4iLfxdv1XpzVYGWRO6PbQAVDbehBQ==", "dev": true, "license": "MIT", "dependencies": { - "testcontainers": "^11.2.1" + "testcontainers": "^11.4.0" } }, "node_modules/@testcontainers/redis": { - "version": "11.2.1", - "resolved": "https://registry.npmjs.org/@testcontainers/redis/-/redis-11.2.1.tgz", - "integrity": "sha512-Q5j+irNw0BLec3he30s2E0fhE06Zr9ROVutkyKUgcwQoZxEVW3xV69ke2AFCT5teEcIvTKqevObN4UDkq33Qow==", + "version": "11.4.0", + "resolved": "https://registry.npmjs.org/@testcontainers/redis/-/redis-11.4.0.tgz", + "integrity": "sha512-w+2VpYt5xAEYbsdhITgwDMif+5Atae+q0ifG/ZrSUZXK528CzqsfnxIgwrZWFnLDCqk1mVNgG4mXdD8VDGd38w==", "dev": true, "license": "MIT", "dependencies": { - "testcontainers": "^11.2.1" + "testcontainers": "^11.4.0" } }, "node_modules/@tokenizer/inflate": { @@ -7239,9 +7064,9 @@ } }, "node_modules/@types/multer": { - "version": "1.4.13", - "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.13.tgz", - "integrity": "sha512-bhhdtPw7JqCiEfC9Jimx5LqX9BDIPJEh2q/fQ4bqbBPtyEZYr3cvF22NwG0DmPZNYA0CAf2CnqDB4KIGGpJcaw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-2.0.0.tgz", + "integrity": "sha512-C3Z9v9Evij2yST3RSBktxP9STm6OdMc5uR1xF1SGr98uv8dUlAL2hqwrZ3GVB3uyMyiegnscEK6PGtYvNrjTjw==", "dev": true, "license": "MIT", "dependencies": { @@ -7258,9 +7083,9 @@ } }, "node_modules/@types/node": { - "version": "22.15.34", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.34.tgz", - "integrity": "sha512-8Y6E5WUupYy1Dd0II32BsWAx5MWdcnRd8L84Oys3veg1YrYtNtzgO4CFhiBg6MDSjk7Ay36HYOnU7/tuOzIzcw==", + "version": "22.16.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.16.5.tgz", + "integrity": "sha512-bJFoMATwIGaxxx8VJPeM8TonI8t579oRvgAuT8zFugJsJZgzqv0Fu8Mhp68iecjzG7cnN3mO2dJQ5uUM2EFrgQ==", "license": "MIT", "dependencies": { "undici-types": "~6.21.0" @@ -7316,9 +7141,9 @@ } }, "node_modules/@types/picomatch": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-4.0.0.tgz", - "integrity": "sha512-J1Bng+wlyEERWSgJQU1Pi0HObCLVcr994xT/M+1wcl/yNRTGBupsCxthgkdYG+GCOMaQH7iSVUY3LJVBBqG7MQ==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-4.0.1.tgz", + "integrity": "sha512-dLqxmi5VJRC9XTvc/oaTtk+bDb4RRqxLZPZ3jIpYBHEnDXX8lu02w2yWI6NsPPsELuVK298Z2iR8jgoWKRdUVQ==", "dev": true, "license": "MIT" }, @@ -7500,17 +7325,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.36.0.tgz", - "integrity": "sha512-lZNihHUVB6ZZiPBNgOQGSxUASI7UJWhT8nHyUGCnaQ28XFCw98IfrMCG3rUl1uwUWoAvodJQby2KTs79UTcrAg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz", + "integrity": "sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/type-utils": "8.36.0", - "@typescript-eslint/utils": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/type-utils": "8.38.0", + "@typescript-eslint/utils": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -7524,7 +7349,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.36.0", + "@typescript-eslint/parser": "^8.38.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } @@ -7540,16 +7365,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.36.0.tgz", - "integrity": "sha512-FuYgkHwZLuPbZjQHzJXrtXreJdFMKl16BFYyRrLxDhWr6Qr7Kbcu2s1Yhu8tsiMXw1S0W1pjfFfYEt+R604s+Q==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.38.0.tgz", + "integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "debug": "^4.3.4" }, "engines": { @@ -7565,14 +7390,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.36.0.tgz", - "integrity": "sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz", + "integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.36.0", - "@typescript-eslint/types": "^8.36.0", + "@typescript-eslint/tsconfig-utils": "^8.38.0", + "@typescript-eslint/types": "^8.38.0", "debug": "^4.3.4" }, "engines": { @@ -7587,14 +7412,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.36.0.tgz", - "integrity": "sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz", + "integrity": "sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0" + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -7605,9 +7430,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.36.0.tgz", - "integrity": "sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz", + "integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==", "dev": true, "license": "MIT", "engines": { @@ -7622,14 +7447,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.36.0.tgz", - "integrity": "sha512-5aaGYG8cVDd6cxfk/ynpYzxBRZJk7w/ymto6uiyUFtdCozQIsQWh7M28/6r57Fwkbweng8qAzoMCPwSJfWlmsg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.38.0.tgz", + "integrity": "sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/utils": "8.36.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -7646,9 +7472,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.36.0.tgz", - "integrity": "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz", + "integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==", "dev": true, "license": "MIT", "engines": { @@ -7660,16 +7486,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.36.0.tgz", - "integrity": "sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz", + "integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.36.0", - "@typescript-eslint/tsconfig-utils": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/project-service": "8.38.0", + "@typescript-eslint/tsconfig-utils": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -7715,16 +7541,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.36.0.tgz", - "integrity": "sha512-VOqmHu42aEMT+P2qYjylw6zP/3E/HvptRwdn/PZxyV27KhZg2IOszXod4NcXisWzPAGSS4trE/g4moNj6XmH2g==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz", + "integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0" + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -7739,13 +7565,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.36.0.tgz", - "integrity": "sha512-vZrhV2lRPWDuGoxcmrzRZyxAggPL+qp3WzUrlZD+slFueDiYHxeBa34dUXPuC0RmGKzl4lS5kFJYvKCq9cnNDA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz", + "integrity": "sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.36.0", + "@typescript-eslint/types": "8.38.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -8905,9 +8731,9 @@ } }, "node_modules/bullmq": { - "version": "5.56.2", - "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.56.2.tgz", - "integrity": "sha512-bq0PSxPCWeNlFBc5yjBs3eR+e6GxIEIeHY0xxq6WELzG65GPjL+A2ni1NS7NroKsur0C3UJdabw51IswiSTSYw==", + "version": "5.56.5", + "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.56.5.tgz", + "integrity": "sha512-nhcVxoE9Y0YUuNYtvaD+N0Bk2kqcU+rXzJwdQIr8i8qC/fxoghwUYb9a+CidTv24pi1eqstLnBoa8xkR/P7Mdw==", "license": "MIT", "dependencies": { "cron-parser": "^4.9.0", @@ -9556,16 +9382,16 @@ } }, "node_modules/compression": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.0.tgz", - "integrity": "sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", "license": "MIT", "dependencies": { "bytes": "3.1.2", "compressible": "~2.0.18", "debug": "2.6.9", "negotiator": "~0.6.4", - "on-headers": "~1.0.2", + "on-headers": "~1.1.0", "safe-buffer": "5.2.1", "vary": "~1.1.2" }, @@ -9588,6 +9414,15 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, + "node_modules/compression/node_modules/on-headers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -9870,22 +9705,6 @@ "node": ">=4" } }, - "node_modules/cssstyle": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.5.0.tgz", - "integrity": "sha512-/7gw8TGrvH/0g564EnhgFZogTMVe+lifpB7LWU+PEsiq5o83TUXR3fDbzTRXOJhoJwck5IS9ez3Em5LNMMO2aw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@asamuzakjp/css-color": "^3.2.0", - "rrweb-cssom": "^0.8.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", @@ -9893,22 +9712,6 @@ "dev": true, "license": "MIT" }, - "node_modules/data-urls": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", - "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/dayjs": { "version": "1.11.13", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", @@ -9944,15 +9747,6 @@ } } }, - "node_modules/decimal.js": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", - "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/decompress-response": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", @@ -10685,9 +10479,9 @@ } }, "node_modules/eslint": { - "version": "9.30.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.30.1.tgz", - "integrity": "sha512-zmxXPNMOXmwm9E0yQLi5uqXHs7uq2UIiqEKo3Gq+3fwo1XrJ+hijAZImyF7hclW3E6oHz43Yk3RP8at6OTKflQ==", + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", + "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", "dev": true, "license": "MIT", "dependencies": { @@ -10695,9 +10489,9 @@ "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", "@eslint/config-helpers": "^0.3.0", - "@eslint/core": "^0.14.0", + "@eslint/core": "^0.15.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.30.1", + "@eslint/js": "9.31.0", "@eslint/plugin-kit": "^0.3.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -10746,9 +10540,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz", - "integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==", + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", "bin": { @@ -10762,9 +10556,9 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.1.tgz", - "integrity": "sha512-dobTkHT6XaEVOo8IO90Q4DOSxnm3Y151QxPJlM/vKC0bVy+d6cVWQZLlFiuZPP0wS6vZwSKeJgKkcS+KfMBlRw==", + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.3.tgz", + "integrity": "sha512-NAdMYww51ehKfDyDhv59/eIItUVzU0Io9H2E8nHNGKEeeqlnci+1gCvrHib6EmZdf6GxF+LCV5K7UC65Ezvw7w==", "dev": true, "license": "MIT", "dependencies": { @@ -11503,9 +11297,9 @@ } }, "node_modules/form-data": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", - "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -12053,21 +11847,6 @@ "he": "bin/he" } }, - "node_modules/html-encoding-sniffer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", - "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "whatwg-encoding": "^3.1.1" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -12514,15 +12293,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/is-promise": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", @@ -12732,48 +12502,6 @@ "dev": true, "license": "MIT" }, - "node_modules/jsdom": { - "version": "26.1.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz", - "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "cssstyle": "^4.2.1", - "data-urls": "^5.0.0", - "decimal.js": "^10.5.0", - "html-encoding-sniffer": "^4.0.0", - "http-proxy-agent": "^7.0.2", - "https-proxy-agent": "^7.0.6", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.16", - "parse5": "^7.2.1", - "rrweb-cssom": "^0.8.0", - "saxes": "^6.0.0", - "symbol-tree": "^3.2.4", - "tough-cookie": "^5.1.1", - "w3c-xmlserializer": "^5.0.0", - "webidl-conversions": "^7.0.0", - "whatwg-encoding": "^3.1.1", - "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.1.1", - "ws": "^8.18.0", - "xml-name-validator": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "canvas": "^3.0.0" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } - } - }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -13823,9 +13551,9 @@ "license": "MIT" }, "node_modules/nest-commander": { - "version": "3.17.0", - "resolved": "https://registry.npmjs.org/nest-commander/-/nest-commander-3.17.0.tgz", - "integrity": "sha512-1R9vppZT2j/9njKiG0zYTDLAyQOj14KdGWdNuhluveK8VXoQepXNb0t09dRNWy4KCWrI7wDZ2tQTEwb43JyHOw==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/nest-commander/-/nest-commander-3.18.0.tgz", + "integrity": "sha512-NWtodOl2aStnApWp9oajCoJW71lqN0CCjf9ygOWxpXnG3o4nQ8ZO5CgrExfVw2+0CVC877hr0rFR7FSu2rypGg==", "license": "MIT", "dependencies": { "@fig/complete-commander": "^3.0.0", @@ -13926,9 +13654,9 @@ "license": "MIT" }, "node_modules/node-addon-api": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.4.0.tgz", - "integrity": "sha512-D9DI/gXHvVmjHS08SVch0Em8G5S1P+QWtU31appcKT/8wFSPRcdHadIFSAntdMMVM5zz+/DL+bL/gz3UDppqtg==", + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", + "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", "license": "MIT", "engines": { "node": "^18 || ^20 || >= 21" @@ -14124,15 +13852,6 @@ "set-blocking": "^2.0.0" } }, - "node_modules/nwsapi": { - "version": "2.2.20", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.20.tgz", - "integrity": "sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/nypm": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.0.tgz", @@ -14413,36 +14132,6 @@ "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==", "license": "MIT" }, - "node_modules/parse5": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", - "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "entities": "^6.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/parse5/node_modules/entities": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", - "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "peer": true, - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, "node_modules/parseley": { "version": "0.12.1", "resolved": "https://registry.npmjs.org/parseley/-/parseley-0.12.1.tgz", @@ -14687,9 +14376,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", "engines": { "node": ">=12" @@ -15069,15 +14758,15 @@ } }, "node_modules/prettier-plugin-organize-imports": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.1.0.tgz", - "integrity": "sha512-5aWRdCgv645xaa58X8lOxzZoiHAldAPChljr/MT0crXVOWTZ+Svl4hIWlz+niYSlO6ikE5UXkN1JrRvIP2ut0A==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.2.0.tgz", + "integrity": "sha512-Zdy27UhlmyvATZi67BTnLcKTo8fm6Oik59Sz6H64PgZJVs6NJpPD1mT240mmJn62c98/QaL+r3kx9Q3gRpDajg==", "dev": true, "license": "MIT", "peerDependencies": { "prettier": ">=2.0", "typescript": ">=2.9", - "vue-tsc": "^2.1.0" + "vue-tsc": "^2.1.0 || 3" }, "peerDependenciesMeta": { "vue-tsc": { @@ -15397,9 +15086,9 @@ } }, "node_modules/react-email": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/react-email/-/react-email-4.1.0.tgz", - "integrity": "sha512-UvG5z1/gNOsLNwKPO87vgMoF7tdzUGd0kIy4fozzdBBsyLUju7hNVLBRm9j+Li/CwP5CXFT8Y5jZBtIFvSyr0w==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/react-email/-/react-email-4.2.4.tgz", + "integrity": "sha512-r5x1nlWUXKZWoIU7l9jx5jkq43RuDUlroH0FRA5MMrCOaLqAfg3vOsAxAadNkG47L0iTeDkkqTKzaV6dTaYf/A==", "license": "MIT", "dependencies": { "@babel/parser": "^7.27.0", @@ -16012,15 +15701,6 @@ "node": ">= 18" } }, - "node_modules/rrweb-cssom": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", - "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -16111,21 +15791,6 @@ "postcss": "^8.3.11" } }, - "node_modules/saxes": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", - "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", - "dev": true, - "license": "ISC", - "optional": true, - "peer": true, - "dependencies": { - "xmlchars": "^2.2.0" - }, - "engines": { - "node": ">=v12.22.7" - } - }, "node_modules/scheduler": { "version": "0.26.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", @@ -17195,35 +16860,35 @@ } }, "node_modules/superagent": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.2.2.tgz", - "integrity": "sha512-vWMq11OwWCC84pQaFPzF/VO3BrjkCeewuvJgt1jfV0499Z1QSAWN4EqfMM5WlFDDX9/oP8JjlDKpblrmEoyu4Q==", + "version": "10.2.3", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.2.3.tgz", + "integrity": "sha512-y/hkYGeXAj7wUMjxRbB21g/l6aAEituGXM9Rwl4o20+SX3e8YOSV6BxFXl+dL3Uk0mjSL3kCbNkwURm8/gEDig==", "dev": true, "license": "MIT", "dependencies": { - "component-emitter": "^1.3.0", + "component-emitter": "^1.3.1", "cookiejar": "^2.1.4", - "debug": "^4.3.4", + "debug": "^4.3.7", "fast-safe-stringify": "^2.1.1", - "form-data": "^4.0.0", + "form-data": "^4.0.4", "formidable": "^3.5.4", "methods": "^1.1.2", "mime": "2.6.0", - "qs": "^6.11.0" + "qs": "^6.11.2" }, "engines": { "node": ">=14.18.0" } }, "node_modules/supertest": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.1.3.tgz", - "integrity": "sha512-ORY0gPa6ojmg/C74P/bDoS21WL6FMXq5I8mawkEz30/zkwdu0gOeqstFy316vHG6OKxqQ+IbGneRemHI8WraEw==", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.1.4.tgz", + "integrity": "sha512-tjLPs7dVyqgItVFirHYqe2T+MfWc2VOBQ8QFKKbWTA3PU7liZR8zoSpAi/C1k1ilm9RsXIKYf197oap9wXGVYg==", "dev": true, "license": "MIT", "dependencies": { "methods": "^1.1.2", - "superagent": "^10.2.2" + "superagent": "^10.2.3" }, "engines": { "node": ">=14.18.0" @@ -17272,15 +16937,6 @@ "node": ">=0.10" } }, - "node_modules/symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/synckit": { "version": "0.11.8", "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.8.tgz", @@ -17799,9 +17455,9 @@ } }, "node_modules/testcontainers": { - "version": "11.2.1", - "resolved": "https://registry.npmjs.org/testcontainers/-/testcontainers-11.2.1.tgz", - "integrity": "sha512-KJALGi8ButKDZgzHr0PtJUVNBOSlSFncumZ34MCQTN4VEU9AK4tWTn9gCcAFzG4zBmzzC2aEbHMFUujqkbDvBg==", + "version": "11.4.0", + "resolved": "https://registry.npmjs.org/testcontainers/-/testcontainers-11.4.0.tgz", + "integrity": "sha512-eX5nc/Fi5I0LHqwxw6BuUvWNfdl+M2sKX6fX/47RP89Xs5nU6smd0iD7dpFogxy8/wACjlucLoutJc7b5mtq7w==", "dev": true, "license": "MIT", "dependencies": { @@ -17819,7 +17475,7 @@ "ssh-remote-port-forward": "^1.0.4", "tar-fs": "^3.1.0", "tmp": "^0.2.3", - "undici": "^7.11.0" + "undici": "^7.12.0" } }, "node_modules/testcontainers/node_modules/tmp": { @@ -17941,30 +17597,6 @@ "node": ">=14.0.0" } }, - "node_modules/tldts": { - "version": "6.1.86", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", - "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "tldts-core": "^6.1.86" - }, - "bin": { - "tldts": "bin/cli.js" - } - }, - "node_modules/tldts-core": { - "version": "6.1.86", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", - "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -18024,36 +17656,6 @@ "node": ">=6" } }, - "node_modules/tough-cookie": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", - "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", - "dev": true, - "license": "BSD-3-Clause", - "optional": true, - "peer": true, - "dependencies": { - "tldts": "^6.1.32" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/tr46": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", - "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "punycode": "^2.3.1" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", @@ -18447,15 +18049,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.36.0.tgz", - "integrity": "sha512-fTCqxthY+h9QbEgSIBfL9iV6CvKDFuoxg6bHPNpJ9HIUzS+jy2lCEyCmGyZRWEBSaykqcDPf1SJ+BfCI8DRopA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.38.0.tgz", + "integrity": "sha512-FsZlrYK6bPDGoLeZRuvx2v6qrM03I0U0SnfCLPs/XCCPCFD80xU9Pg09H/K+XFa68uJuZo7l/Xhs+eDRg2l3hg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.36.0", - "@typescript-eslint/parser": "8.36.0", - "@typescript-eslint/utils": "8.36.0" + "@typescript-eslint/eslint-plugin": "8.38.0", + "@typescript-eslint/parser": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -18569,9 +18172,9 @@ } }, "node_modules/undici": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.11.0.tgz", - "integrity": "sha512-heTSIac3iLhsmZhUCjyS3JQEkZELateufzZuBaVM5RHXdSBMb1LPMQf5x+FH7qjsZYDP0ttAc3nnVpUB+wYbOg==", + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.12.0.tgz", + "integrity": "sha512-GrKEsc3ughskmGA9jevVlIOPMiiAHJ4OFUtaAH+NhfTUSiZ1wMPIQqQvAJUrJspFXJt3EBWgpAeoHEDVT1IBug==", "dev": true, "license": "MIT", "engines": { @@ -18985,21 +18588,6 @@ } } }, - "node_modules/w3c-xmlserializer": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", - "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "xml-name-validator": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/watchpack": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", @@ -19023,18 +18611,6 @@ "defaults": "^1.0.3" } }, - "node_modules/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "peer": true, - "engines": { - "node": ">=12" - } - }, "node_modules/webpack": { "version": "5.99.6", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.6.tgz", @@ -19231,49 +18807,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/whatwg-encoding": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", - "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "iconv-lite": "0.6.3" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/whatwg-mimetype": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", - "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/whatwg-url": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", - "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "tr46": "^5.1.0", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -19412,51 +18945,6 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" }, - "node_modules/ws": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz", - "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xml-name-validator": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", - "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/server/package.json b/server/package.json index c765786960..472b746630 100644 --- a/server/package.json +++ b/server/package.json @@ -1,6 +1,6 @@ { "name": "immich", - "version": "1.135.3", + "version": "1.137.3", "description": "", "author": "", "private": true, @@ -55,7 +55,7 @@ "@opentelemetry/sdk-metrics": "^2.0.1", "@opentelemetry/sdk-node": "^0.203.0", "@opentelemetry/semantic-conventions": "^1.34.0", - "@react-email/components": "^0.2.0", + "@react-email/components": "^0.3.0", "@react-email/render": "^1.1.2", "@socket.io/redis-adapter": "^8.3.0", "archiver": "^7.0.0", @@ -79,7 +79,7 @@ "i18n-iso-countries": "^7.6.0", "ioredis": "^5.3.2", "js-yaml": "^4.1.0", - "kysely": "^0.28.0", + "kysely": "0.28.2", "kysely-postgres-js": "^2.0.0", "lodash": "^4.17.21", "luxon": "^3.4.2", @@ -113,7 +113,6 @@ "validator": "^13.12.0" }, "devDependencies": { - "canvas": "^3.1.0", "@eslint/eslintrc": "^3.1.0", "@eslint/js": "^9.8.0", "@nestjs/cli": "^11.0.2", @@ -134,8 +133,8 @@ "@types/lodash": "^4.14.197", "@types/luxon": "^3.6.2", "@types/mock-fs": "^4.13.1", - "@types/multer": "^1.4.7", - "@types/node": "^22.15.33", + "@types/multer": "^2.0.0", + "@types/node": "^22.16.5", "@types/nodemailer": "^6.4.14", "@types/picomatch": "^4.0.0", "@types/pngjs": "^6.0.5", @@ -146,8 +145,9 @@ "@types/ua-parser-js": "^0.7.36", "@types/validator": "^13.15.2", "@vitest/coverage-v8": "^3.0.0", + "canvas": "^3.1.0", "eslint": "^9.14.0", - "eslint-config-prettier": "^10.0.0", + "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-unicorn": "^59.0.0", "globals": "^16.0.0", @@ -172,7 +172,7 @@ "vitest": "^3.0.0" }, "volta": { - "node": "22.17.0" + "node": "22.17.1" }, "overrides": { "sharp": "^0.34.2" diff --git a/server/src/app.module.ts b/server/src/app.module.ts index 9ea75d78c4..8d261463e7 100644 --- a/server/src/app.module.ts +++ b/server/src/app.module.ts @@ -5,7 +5,7 @@ import { ScheduleModule, SchedulerRegistry } from '@nestjs/schedule'; import { ClsModule } from 'nestjs-cls'; import { KyselyModule } from 'nestjs-kysely'; import { OpenTelemetryModule } from 'nestjs-otel'; -import { commands } from 'src/commands'; +import { commandsAndQuestions } from 'src/commands'; import { IWorker } from 'src/constants'; import { controllers } from 'src/controllers'; import { ImmichWorker } from 'src/enum'; @@ -97,7 +97,7 @@ export class MicroservicesModule extends BaseModule {} @Module({ imports: [...imports], - providers: [...common, ...commands, SchedulerRegistry], + providers: [...common, ...commandsAndQuestions, SchedulerRegistry], }) export class ImmichAdminModule implements OnModuleDestroy { constructor(private service: CliService) {} diff --git a/server/src/bin/migrations.ts b/server/src/bin/migrations.ts index 3bdfb3bbc6..ebb07af442 100644 --- a/server/src/bin/migrations.ts +++ b/server/src/bin/migrations.ts @@ -98,7 +98,7 @@ const create = (path: string, up: string[], down: string[]) => { const folder = dirname(path); const fullPath = join(folder, filename); mkdirSync(folder, { recursive: true }); - writeFileSync(fullPath, asMigration('kysely', { name, timestamp, up, down })); + writeFileSync(fullPath, asMigration({ up, down })); console.log(`Wrote ${fullPath}`); }; @@ -128,34 +128,11 @@ const compare = async () => { }; type MigrationProps = { - name: string; - timestamp: number; up: string[]; down: string[]; }; -const asMigration = (type: 'kysely' | 'typeorm', options: MigrationProps) => - type === 'typeorm' ? asTypeOrmMigration(options) : asKyselyMigration(options); - -const asTypeOrmMigration = ({ timestamp, name, up, down }: MigrationProps) => { - const upSql = up.map((sql) => ` await queryRunner.query(\`${sql}\`);`).join('\n'); - const downSql = down.map((sql) => ` await queryRunner.query(\`${sql}\`);`).join('\n'); - - return `import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class ${name}${timestamp} implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { -${upSql} - } - - public async down(queryRunner: QueryRunner): Promise { -${downSql} - } -} -`; -}; - -const asKyselyMigration = ({ up, down }: MigrationProps) => { +const asMigration = ({ up, down }: MigrationProps) => { const upSql = up.map((sql) => ` await sql\`${sql}\`.execute(db);`).join('\n'); const downSql = down.map((sql) => ` await sql\`${sql}\`.execute(db);`).join('\n'); diff --git a/server/src/commands/index.ts b/server/src/commands/index.ts index ce085f6e34..46a8d13e35 100644 --- a/server/src/commands/index.ts +++ b/server/src/commands/index.ts @@ -1,11 +1,16 @@ import { GrantAdminCommand, PromptEmailQuestion, RevokeAdminCommand } from 'src/commands/grant-admin'; import { ListUsersCommand } from 'src/commands/list-users.command'; +import { + ChangeMediaLocationCommand, + PromptConfirmMoveQuestions, + PromptMediaLocationQuestions, +} from 'src/commands/media-location.command'; import { DisableOAuthLogin, EnableOAuthLogin } from 'src/commands/oauth-login'; import { DisablePasswordLoginCommand, EnablePasswordLoginCommand } from 'src/commands/password-login'; import { PromptPasswordQuestions, ResetAdminPasswordCommand } from 'src/commands/reset-admin-password.command'; import { VersionCommand } from 'src/commands/version.command'; -export const commands = [ +export const commandsAndQuestions = [ ResetAdminPasswordCommand, PromptPasswordQuestions, PromptEmailQuestion, @@ -17,4 +22,7 @@ export const commands = [ VersionCommand, GrantAdminCommand, RevokeAdminCommand, + ChangeMediaLocationCommand, + PromptMediaLocationQuestions, + PromptConfirmMoveQuestions, ]; diff --git a/server/src/commands/media-location.command.ts b/server/src/commands/media-location.command.ts new file mode 100644 index 0000000000..0d32749c02 --- /dev/null +++ b/server/src/commands/media-location.command.ts @@ -0,0 +1,106 @@ +import { Command, CommandRunner, InquirerService, Question, QuestionSet } from 'nest-commander'; +import { CliService } from 'src/services/cli.service'; + +@Command({ + name: 'change-media-location', + description: 'Change database file paths to align with a new media location', +}) +export class ChangeMediaLocationCommand extends CommandRunner { + constructor( + private service: CliService, + private inquirer: InquirerService, + ) { + super(); + } + + private async showSamplePaths(hint?: string) { + hint = hint ? ` (${hint})` : ''; + + const paths = await this.service.getSampleFilePaths(); + if (paths.length > 0) { + let message = ` Examples from the database${hint}:\n`; + for (const path of paths) { + message += ` - ${path}\n`; + } + + console.log(`\n${message}`); + } + } + + async run(): Promise { + try { + await this.showSamplePaths(); + + const { oldValue, newValue } = await this.inquirer.ask<{ oldValue: string; newValue: string }>( + 'prompt-media-location', + {}, + ); + + const success = await this.service.migrateFilePaths({ + oldValue, + newValue, + confirm: async ({ sourceFolder, targetFolder }) => { + console.log(` + Previous value: ${oldValue} + Current value: ${newValue} + + Changing from "${sourceFolder}/*" to "${targetFolder}/*" +`); + + const { value: confirmed } = await this.inquirer.ask<{ value: boolean }>('prompt-confirm-move', {}); + return confirmed; + }, + }); + + const successMessage = `Matching database file paths were updated successfully! 🎉 + + You may now set IMMICH_MEDIA_LOCATION=${newValue} and restart! + + (please remember to update applicable volume mounts e.g + services: + immich-server: + ... + volumes: + - \${UPLOAD_LOCATION}:/data + ... + )`; + + console.log(`\n ${success ? successMessage : 'No rows were updated'}\n`); + + await this.showSamplePaths('after'); + } catch (error) { + console.error(error); + console.error('Unable to update database file paths.'); + } + } +} + +const currentValue = process.env.IMMICH_MEDIA_LOCATION || ''; + +const makePrompt = (which: string) => { + return `Enter the ${which} value of IMMICH_MEDIA_LOCATION:${currentValue ? ` [${currentValue}]` : ''}`; +}; + +@QuestionSet({ name: 'prompt-media-location' }) +export class PromptMediaLocationQuestions { + @Question({ message: makePrompt('previous'), name: 'oldValue' }) + oldValue(value: string) { + return value || currentValue; + } + + @Question({ message: makePrompt('new'), name: 'newValue' }) + newValue(value: string) { + return value || currentValue; + } +} + +@QuestionSet({ name: 'prompt-confirm-move' }) +export class PromptConfirmMoveQuestions { + @Question({ + message: 'Do you want to proceed? [Y/n]', + name: 'value', + }) + value(value: string): boolean { + return ['yes', 'y'].includes((value || 'y').toLowerCase()); + } +} diff --git a/server/src/constants.ts b/server/src/constants.ts index 447d8a09c9..3215a58291 100644 --- a/server/src/constants.ts +++ b/server/src/constants.ts @@ -47,14 +47,13 @@ export const serverVersion = new SemVer(version); export const AUDIT_LOG_MAX_DURATION = Duration.fromObject({ days: 100 }); export const ONE_HOUR = Duration.fromObject({ hours: 1 }); -export const APP_MEDIA_LOCATION = process.env.IMMICH_MEDIA_LOCATION || './upload'; - export const MACHINE_LEARNING_PING_TIMEOUT = Number(process.env.MACHINE_LEARNING_PING_TIMEOUT || 2000); export const MACHINE_LEARNING_AVAILABILITY_BACKOFF_TIME = Number( process.env.MACHINE_LEARNING_AVAILABILITY_BACKOFF_TIME || 30_000, ); export const citiesFile = 'cities500.txt'; +export const reverseGeocodeMaxDistance = 25_000; export const MOBILE_REDIRECT = 'app.immich:///oauth-callback'; export const LOGIN_URL = '/auth/login?autoLaunch=0'; diff --git a/server/src/controllers/album.controller.ts b/server/src/controllers/album.controller.ts index 2e6d1e7e43..36c5e0b13b 100644 --- a/server/src/controllers/album.controller.ts +++ b/server/src/controllers/album.controller.ts @@ -67,7 +67,7 @@ export class AlbumController { } @Put(':id/assets') - @Authenticated({ sharedLink: true }) + @Authenticated({ permission: Permission.AlbumAssetCreate, sharedLink: true }) addAssetsToAlbum( @Auth() auth: AuthDto, @Param() { id }: UUIDParamDto, @@ -77,7 +77,7 @@ export class AlbumController { } @Delete(':id/assets') - @Authenticated() + @Authenticated({ permission: Permission.AlbumAssetDelete }) removeAssetFromAlbum( @Auth() auth: AuthDto, @Body() dto: BulkIdsDto, @@ -87,7 +87,7 @@ export class AlbumController { } @Put(':id/users') - @Authenticated() + @Authenticated({ permission: Permission.AlbumUserCreate }) addUsersToAlbum( @Auth() auth: AuthDto, @Param() { id }: UUIDParamDto, @@ -97,7 +97,7 @@ export class AlbumController { } @Put(':id/user/:userId') - @Authenticated() + @Authenticated({ permission: Permission.AlbumUserUpdate }) updateAlbumUser( @Auth() auth: AuthDto, @Param() { id }: UUIDParamDto, @@ -108,7 +108,7 @@ export class AlbumController { } @Delete(':id/user/:userId') - @Authenticated() + @Authenticated({ permission: Permission.AlbumUserDelete }) removeUserFromAlbum( @Auth() auth: AuthDto, @Param() { id }: UUIDParamDto, diff --git a/server/src/controllers/asset-media.controller.ts b/server/src/controllers/asset-media.controller.ts index ea6c9602c8..8e83b77fb0 100644 --- a/server/src/controllers/asset-media.controller.ts +++ b/server/src/controllers/asset-media.controller.ts @@ -34,7 +34,7 @@ import { UploadFieldName, } from 'src/dtos/asset-media.dto'; import { AuthDto } from 'src/dtos/auth.dto'; -import { ImmichHeader, RouteKey } from 'src/enum'; +import { ImmichHeader, Permission, RouteKey } from 'src/enum'; import { AssetUploadInterceptor } from 'src/middleware/asset-upload.interceptor'; import { Auth, Authenticated, FileResponse } from 'src/middleware/auth.guard'; import { FileUploadInterceptor, getFiles } from 'src/middleware/file-upload.interceptor'; @@ -61,7 +61,7 @@ export class AssetMediaController { required: false, }) @ApiBody({ description: 'Asset Upload Information', type: AssetMediaCreateDto }) - @Authenticated({ sharedLink: true }) + @Authenticated({ permission: Permission.AssetUpload, sharedLink: true }) async uploadAsset( @Auth() auth: AuthDto, @UploadedFiles(new ParseFilePipe({ validators: [new FileNotEmptyValidator(['assetData'])] })) files: UploadFiles, @@ -80,7 +80,7 @@ export class AssetMediaController { @Get(':id/original') @FileResponse() - @Authenticated({ sharedLink: true }) + @Authenticated({ permission: Permission.AssetDownload, sharedLink: true }) async downloadAsset( @Auth() auth: AuthDto, @Param() { id }: UUIDParamDto, @@ -101,7 +101,7 @@ export class AssetMediaController { summary: 'replaceAsset', description: 'Replace the asset with new file, without changing its id', }) - @Authenticated({ sharedLink: true }) + @Authenticated({ permission: Permission.AssetReplace, sharedLink: true }) async replaceAsset( @Auth() auth: AuthDto, @Param() { id }: UUIDParamDto, @@ -120,7 +120,7 @@ export class AssetMediaController { @Get(':id/thumbnail') @FileResponse() - @Authenticated({ sharedLink: true }) + @Authenticated({ permission: Permission.AssetView, sharedLink: true }) async viewAsset( @Auth() auth: AuthDto, @Param() { id }: UUIDParamDto, @@ -157,7 +157,7 @@ export class AssetMediaController { @Get(':id/video/playback') @FileResponse() - @Authenticated({ sharedLink: true }) + @Authenticated({ permission: Permission.AssetView, sharedLink: true }) async playAssetVideo( @Auth() auth: AuthDto, @Param() { id }: UUIDParamDto, diff --git a/server/src/controllers/asset.controller.ts b/server/src/controllers/asset.controller.ts index bb17daddf3..d23785a5ff 100644 --- a/server/src/controllers/asset.controller.ts +++ b/server/src/controllers/asset.controller.ts @@ -13,7 +13,7 @@ import { UpdateAssetDto, } from 'src/dtos/asset.dto'; import { AuthDto } from 'src/dtos/auth.dto'; -import { RouteKey } from 'src/enum'; +import { Permission, RouteKey } from 'src/enum'; import { Auth, Authenticated } from 'src/middleware/auth.guard'; import { AssetService } from 'src/services/asset.service'; import { UUIDParamDto } from 'src/validation'; @@ -24,7 +24,7 @@ export class AssetController { constructor(private service: AssetService) {} @Get('random') - @Authenticated() + @Authenticated({ permission: Permission.AssetRead }) @EndpointLifecycle({ deprecatedAt: 'v1.116.0' }) getRandom(@Auth() auth: AuthDto, @Query() dto: RandomAssetsDto): Promise { return this.service.getRandom(auth, dto.count ?? 1); @@ -44,7 +44,7 @@ export class AssetController { } @Get('statistics') - @Authenticated() + @Authenticated({ permission: Permission.AssetStatistics }) getAssetStatistics(@Auth() auth: AuthDto, @Query() dto: AssetStatsDto): Promise { return this.service.getStatistics(auth, dto); } @@ -58,26 +58,26 @@ export class AssetController { @Put() @HttpCode(HttpStatus.NO_CONTENT) - @Authenticated() + @Authenticated({ permission: Permission.AssetUpdate }) updateAssets(@Auth() auth: AuthDto, @Body() dto: AssetBulkUpdateDto): Promise { return this.service.updateAll(auth, dto); } @Delete() @HttpCode(HttpStatus.NO_CONTENT) - @Authenticated() + @Authenticated({ permission: Permission.AssetDelete }) deleteAssets(@Auth() auth: AuthDto, @Body() dto: AssetBulkDeleteDto): Promise { return this.service.deleteAll(auth, dto); } @Get(':id') - @Authenticated({ sharedLink: true }) + @Authenticated({ permission: Permission.AssetRead, sharedLink: true }) getAssetInfo(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { return this.service.get(auth, id) as Promise; } @Put(':id') - @Authenticated() + @Authenticated({ permission: Permission.AssetUpdate }) updateAsset( @Auth() auth: AuthDto, @Param() { id }: UUIDParamDto, diff --git a/server/src/controllers/auth.controller.ts b/server/src/controllers/auth.controller.ts index 9bc5fd0fbb..30b0d662f2 100644 --- a/server/src/controllers/auth.controller.ts +++ b/server/src/controllers/auth.controller.ts @@ -16,7 +16,7 @@ import { ValidateAccessTokenResponseDto, } from 'src/dtos/auth.dto'; import { UserAdminResponseDto } from 'src/dtos/user.dto'; -import { AuthType, ImmichCookie } from 'src/enum'; +import { AuthType, ImmichCookie, Permission } from 'src/enum'; import { Auth, Authenticated, GetLoginDetails } from 'src/middleware/auth.guard'; import { AuthService, LoginDetails } from 'src/services/auth.service'; import { respondWithCookie, respondWithoutCookie } from 'src/utils/response'; @@ -57,7 +57,7 @@ export class AuthController { @Post('change-password') @HttpCode(HttpStatus.OK) - @Authenticated() + @Authenticated({ permission: Permission.AuthChangePassword }) changePassword(@Auth() auth: AuthDto, @Body() dto: ChangePasswordDto): Promise { return this.service.changePassword(auth, dto); } @@ -87,19 +87,19 @@ export class AuthController { } @Post('pin-code') - @Authenticated() + @Authenticated({ permission: Permission.PinCodeCreate }) setupPinCode(@Auth() auth: AuthDto, @Body() dto: PinCodeSetupDto): Promise { return this.service.setupPinCode(auth, dto); } @Put('pin-code') - @Authenticated() + @Authenticated({ permission: Permission.PinCodeUpdate }) async changePinCode(@Auth() auth: AuthDto, @Body() dto: PinCodeChangeDto): Promise { return this.service.changePinCode(auth, dto); } @Delete('pin-code') - @Authenticated() + @Authenticated({ permission: Permission.PinCodeDelete }) async resetPinCode(@Auth() auth: AuthDto, @Body() dto: PinCodeResetDto): Promise { return this.service.resetPinCode(auth, dto); } diff --git a/server/src/controllers/download.controller.spec.ts b/server/src/controllers/download.controller.spec.ts index 9385c445b5..00d03fc46f 100644 --- a/server/src/controllers/download.controller.spec.ts +++ b/server/src/controllers/download.controller.spec.ts @@ -1,9 +1,9 @@ +import { Readable } from 'node:stream'; import { DownloadController } from 'src/controllers/download.controller'; import { DownloadService } from 'src/services/download.service'; import request from 'supertest'; import { factory } from 'test/small.factory'; import { ControllerContext, controllerSetup, mockBaseService } from 'test/utils'; -import { Readable } from 'typeorm/platform/PlatformTools.js'; describe(DownloadController.name, () => { let ctx: ControllerContext; diff --git a/server/src/controllers/download.controller.ts b/server/src/controllers/download.controller.ts index 880e636dd1..4f5b18e585 100644 --- a/server/src/controllers/download.controller.ts +++ b/server/src/controllers/download.controller.ts @@ -3,6 +3,7 @@ import { ApiTags } from '@nestjs/swagger'; import { AssetIdsDto } from 'src/dtos/asset.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { DownloadInfoDto, DownloadResponseDto } from 'src/dtos/download.dto'; +import { Permission } from 'src/enum'; import { Auth, Authenticated, FileResponse } from 'src/middleware/auth.guard'; import { DownloadService } from 'src/services/download.service'; import { asStreamableFile } from 'src/utils/file'; @@ -13,7 +14,7 @@ export class DownloadController { constructor(private service: DownloadService) {} @Post('info') - @Authenticated({ sharedLink: true }) + @Authenticated({ permission: Permission.AssetDownload, sharedLink: true }) getDownloadInfo(@Auth() auth: AuthDto, @Body() dto: DownloadInfoDto): Promise { return this.service.getDownloadInfo(auth, dto); } @@ -21,7 +22,7 @@ export class DownloadController { @Post('archive') @HttpCode(HttpStatus.OK) @FileResponse() - @Authenticated({ sharedLink: true }) + @Authenticated({ permission: Permission.AssetDownload, sharedLink: true }) downloadArchive(@Auth() auth: AuthDto, @Body() dto: AssetIdsDto): Promise { return this.service.downloadArchive(auth, dto).then(asStreamableFile); } diff --git a/server/src/controllers/duplicate.controller.ts b/server/src/controllers/duplicate.controller.ts index f6b09e6e7a..da6fe4042d 100644 --- a/server/src/controllers/duplicate.controller.ts +++ b/server/src/controllers/duplicate.controller.ts @@ -3,6 +3,7 @@ import { ApiTags } from '@nestjs/swagger'; import { BulkIdsDto } from 'src/dtos/asset-ids.response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { DuplicateResponseDto } from 'src/dtos/duplicate.dto'; +import { Permission } from 'src/enum'; import { Auth, Authenticated } from 'src/middleware/auth.guard'; import { DuplicateService } from 'src/services/duplicate.service'; import { UUIDParamDto } from 'src/validation'; @@ -13,19 +14,19 @@ export class DuplicateController { constructor(private service: DuplicateService) {} @Get() - @Authenticated() + @Authenticated({ permission: Permission.DuplicateRead }) getAssetDuplicates(@Auth() auth: AuthDto): Promise { return this.service.getDuplicates(auth); } @Delete() - @Authenticated() + @Authenticated({ permission: Permission.DuplicateDelete }) deleteDuplicates(@Auth() auth: AuthDto, @Body() dto: BulkIdsDto): Promise { return this.service.deleteAll(auth, dto); } @Delete(':id') - @Authenticated() + @Authenticated({ permission: Permission.DuplicateDelete }) deleteDuplicate(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { return this.service.delete(auth, id); } diff --git a/server/src/controllers/job.controller.ts b/server/src/controllers/job.controller.ts index 7da19e207f..e6b40e6810 100644 --- a/server/src/controllers/job.controller.ts +++ b/server/src/controllers/job.controller.ts @@ -1,6 +1,7 @@ import { Body, Controller, Get, Param, Post, Put } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { AllJobStatusResponseDto, JobCommandDto, JobCreateDto, JobIdParamDto, JobStatusDto } from 'src/dtos/job.dto'; +import { Permission } from 'src/enum'; import { Authenticated } from 'src/middleware/auth.guard'; import { JobService } from 'src/services/job.service'; @@ -10,19 +11,19 @@ export class JobController { constructor(private service: JobService) {} @Get() - @Authenticated({ admin: true }) + @Authenticated({ permission: Permission.JobRead, admin: true }) getAllJobsStatus(): Promise { return this.service.getAllJobsStatus(); } @Post() - @Authenticated({ admin: true }) + @Authenticated({ permission: Permission.JobCreate, admin: true }) createJob(@Body() dto: JobCreateDto): Promise { return this.service.create(dto); } @Put(':id') - @Authenticated({ admin: true }) + @Authenticated({ permission: Permission.JobCreate, admin: true }) sendJobCommand(@Param() { id }: JobIdParamDto, @Body() dto: JobCommandDto): Promise { return this.service.handleCommand(id, dto); } diff --git a/server/src/controllers/memory.controller.ts b/server/src/controllers/memory.controller.ts index a5bbbd7411..786f2af8a4 100644 --- a/server/src/controllers/memory.controller.ts +++ b/server/src/controllers/memory.controller.ts @@ -32,7 +32,7 @@ export class MemoryController { } @Get('statistics') - @Authenticated({ permission: Permission.MemoryRead }) + @Authenticated({ permission: Permission.MemoryStatistics }) memoriesStatistics(@Auth() auth: AuthDto, @Query() dto: MemorySearchDto): Promise { return this.service.statistics(auth, dto); } @@ -61,7 +61,7 @@ export class MemoryController { } @Put(':id/assets') - @Authenticated() + @Authenticated({ permission: Permission.MemoryAssetCreate }) addMemoryAssets( @Auth() auth: AuthDto, @Param() { id }: UUIDParamDto, @@ -72,7 +72,7 @@ export class MemoryController { @Delete(':id/assets') @HttpCode(HttpStatus.OK) - @Authenticated() + @Authenticated({ permission: Permission.MemoryAssetDelete }) removeMemoryAssets( @Auth() auth: AuthDto, @Body() dto: BulkIdsDto, diff --git a/server/src/controllers/search.controller.ts b/server/src/controllers/search.controller.ts index 9bda1fcada..15f8bc3a5a 100644 --- a/server/src/controllers/search.controller.ts +++ b/server/src/controllers/search.controller.ts @@ -4,6 +4,7 @@ import { AssetResponseDto } from 'src/dtos/asset-response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; import { PersonResponseDto } from 'src/dtos/person.dto'; import { + LargeAssetSearchDto, MetadataSearchDto, PlacesResponseDto, RandomSearchDto, @@ -16,6 +17,7 @@ import { SmartSearchDto, StatisticsSearchDto, } from 'src/dtos/search.dto'; +import { Permission } from 'src/enum'; import { Auth, Authenticated } from 'src/middleware/auth.guard'; import { SearchService } from 'src/services/search.service'; @@ -26,58 +28,65 @@ export class SearchController { @Post('metadata') @HttpCode(HttpStatus.OK) - @Authenticated() + @Authenticated({ permission: Permission.AssetRead }) searchAssets(@Auth() auth: AuthDto, @Body() dto: MetadataSearchDto): Promise { return this.service.searchMetadata(auth, dto); } @Post('statistics') @HttpCode(HttpStatus.OK) - @Authenticated() + @Authenticated({ permission: Permission.AssetStatistics }) searchAssetStatistics(@Auth() auth: AuthDto, @Body() dto: StatisticsSearchDto): Promise { return this.service.searchStatistics(auth, dto); } @Post('random') @HttpCode(HttpStatus.OK) - @Authenticated() + @Authenticated({ permission: Permission.AssetRead }) searchRandom(@Auth() auth: AuthDto, @Body() dto: RandomSearchDto): Promise { return this.service.searchRandom(auth, dto); } + @Post('large-assets') + @HttpCode(HttpStatus.OK) + @Authenticated({ permission: Permission.AssetRead }) + searchLargeAssets(@Auth() auth: AuthDto, @Query() dto: LargeAssetSearchDto): Promise { + return this.service.searchLargeAssets(auth, dto); + } + @Post('smart') @HttpCode(HttpStatus.OK) - @Authenticated() + @Authenticated({ permission: Permission.AssetRead }) searchSmart(@Auth() auth: AuthDto, @Body() dto: SmartSearchDto): Promise { return this.service.searchSmart(auth, dto); } @Get('explore') - @Authenticated() + @Authenticated({ permission: Permission.AssetRead }) getExploreData(@Auth() auth: AuthDto): Promise { return this.service.getExploreData(auth); } @Get('person') - @Authenticated() + @Authenticated({ permission: Permission.PersonRead }) searchPerson(@Auth() auth: AuthDto, @Query() dto: SearchPeopleDto): Promise { return this.service.searchPerson(auth, dto); } @Get('places') - @Authenticated() + @Authenticated({ permission: Permission.AssetRead }) searchPlaces(@Query() dto: SearchPlacesDto): Promise { return this.service.searchPlaces(dto); } @Get('cities') - @Authenticated() + @Authenticated({ permission: Permission.AssetRead }) getAssetsByCity(@Auth() auth: AuthDto): Promise { return this.service.getAssetsByCity(auth); } @Get('suggestions') - @Authenticated() + @Authenticated({ permission: Permission.AssetRead }) getSearchSuggestions(@Auth() auth: AuthDto, @Query() dto: SearchSuggestionRequestDto): Promise { // TODO fix open api generation to indicate that results can be nullable return this.service.getSearchSuggestions(auth, dto) as Promise; diff --git a/server/src/controllers/server.controller.ts b/server/src/controllers/server.controller.ts index 3544fce2a0..9a1004c280 100644 --- a/server/src/controllers/server.controller.ts +++ b/server/src/controllers/server.controller.ts @@ -15,6 +15,7 @@ import { ServerVersionResponseDto, } from 'src/dtos/server.dto'; import { VersionCheckStateResponseDto } from 'src/dtos/system-metadata.dto'; +import { Permission } from 'src/enum'; import { Authenticated } from 'src/middleware/auth.guard'; import { ServerService } from 'src/services/server.service'; import { SystemMetadataService } from 'src/services/system-metadata.service'; @@ -30,19 +31,19 @@ export class ServerController { ) {} @Get('about') - @Authenticated() + @Authenticated({ permission: Permission.ServerAbout }) getAboutInfo(): Promise { return this.service.getAboutInfo(); } @Get('apk-links') - @Authenticated() + @Authenticated({ permission: Permission.ServerApkLinks }) getApkLinks(): ServerApkLinksDto { return this.service.getApkLinks(); } @Get('storage') - @Authenticated() + @Authenticated({ permission: Permission.ServerStorage }) getStorage(): Promise { return this.service.getStorage(); } @@ -78,7 +79,7 @@ export class ServerController { } @Get('statistics') - @Authenticated({ admin: true }) + @Authenticated({ permission: Permission.ServerStatistics, admin: true }) getServerStatistics(): Promise { return this.service.getStatistics(); } @@ -88,25 +89,25 @@ export class ServerController { return this.service.getSupportedMediaTypes(); } + @Get('license') + @Authenticated({ permission: Permission.ServerLicenseRead, admin: true }) + @ApiNotFoundResponse() + getServerLicense(): Promise { + return this.service.getLicense(); + } + @Put('license') - @Authenticated({ admin: true }) + @Authenticated({ permission: Permission.ServerLicenseUpdate, admin: true }) setServerLicense(@Body() license: LicenseKeyDto): Promise { return this.service.setLicense(license); } @Delete('license') - @Authenticated({ admin: true }) + @Authenticated({ permission: Permission.ServerLicenseDelete, admin: true }) deleteServerLicense(): Promise { return this.service.deleteLicense(); } - @Get('license') - @Authenticated({ admin: true }) - @ApiNotFoundResponse() - getServerLicense(): Promise { - return this.service.getLicense(); - } - @Get('version-check') @Authenticated() getVersionCheck(): Promise { diff --git a/server/src/controllers/stack.controller.ts b/server/src/controllers/stack.controller.ts index 238753734c..5b153a163b 100644 --- a/server/src/controllers/stack.controller.ts +++ b/server/src/controllers/stack.controller.ts @@ -6,7 +6,7 @@ import { StackCreateDto, StackResponseDto, StackSearchDto, StackUpdateDto } from import { Permission } from 'src/enum'; import { Auth, Authenticated } from 'src/middleware/auth.guard'; import { StackService } from 'src/services/stack.service'; -import { UUIDParamDto } from 'src/validation'; +import { UUIDAssetIDParamDto, UUIDParamDto } from 'src/validation'; @ApiTags('Stacks') @Controller('stacks') @@ -54,4 +54,11 @@ export class StackController { deleteStack(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise { return this.service.delete(auth, id); } + + @Delete(':id/assets/:assetId') + @Authenticated({ permission: Permission.StackUpdate }) + @HttpCode(HttpStatus.NO_CONTENT) + removeAssetFromStack(@Auth() auth: AuthDto, @Param() dto: UUIDAssetIDParamDto): Promise { + return this.service.removeAsset(auth, dto); + } } diff --git a/server/src/controllers/sync.controller.ts b/server/src/controllers/sync.controller.ts index 0945810be7..a7b2b21a54 100644 --- a/server/src/controllers/sync.controller.ts +++ b/server/src/controllers/sync.controller.ts @@ -12,6 +12,7 @@ import { SyncAckSetDto, SyncStreamDto, } from 'src/dtos/sync.dto'; +import { Permission } from 'src/enum'; import { Auth, Authenticated } from 'src/middleware/auth.guard'; import { GlobalExceptionFilter } from 'src/middleware/global-exception.filter'; import { SyncService } from 'src/services/sync.service'; @@ -41,7 +42,7 @@ export class SyncController { @Post('stream') @Header('Content-Type', 'application/jsonlines+json') @HttpCode(HttpStatus.OK) - @Authenticated() + @Authenticated({ permission: Permission.SyncStream }) async getSyncStream(@Auth() auth: AuthDto, @Res() res: Response, @Body() dto: SyncStreamDto) { try { await this.service.stream(auth, res, dto); @@ -52,21 +53,21 @@ export class SyncController { } @Get('ack') - @Authenticated() + @Authenticated({ permission: Permission.SyncCheckpointRead }) getSyncAck(@Auth() auth: AuthDto): Promise { return this.service.getAcks(auth); } @Post('ack') @HttpCode(HttpStatus.NO_CONTENT) - @Authenticated() + @Authenticated({ permission: Permission.SyncCheckpointUpdate }) sendSyncAck(@Auth() auth: AuthDto, @Body() dto: SyncAckSetDto) { return this.service.setAcks(auth, dto); } @Delete('ack') @HttpCode(HttpStatus.NO_CONTENT) - @Authenticated() + @Authenticated({ permission: Permission.SyncCheckpointDelete }) deleteSyncAck(@Auth() auth: AuthDto, @Body() dto: SyncAckDeleteDto) { return this.service.deleteAcks(auth, dto); } diff --git a/server/src/controllers/user.controller.ts b/server/src/controllers/user.controller.ts index 76d2cf4c5a..1b91e1a848 100644 --- a/server/src/controllers/user.controller.ts +++ b/server/src/controllers/user.controller.ts @@ -21,7 +21,7 @@ import { OnboardingDto, OnboardingResponseDto } from 'src/dtos/onboarding.dto'; import { UserPreferencesResponseDto, UserPreferencesUpdateDto } from 'src/dtos/user-preferences.dto'; import { CreateProfileImageDto, CreateProfileImageResponseDto } from 'src/dtos/user-profile.dto'; import { UserAdminResponseDto, UserResponseDto, UserUpdateMeDto } from 'src/dtos/user.dto'; -import { RouteKey } from 'src/enum'; +import { Permission, RouteKey } from 'src/enum'; import { Auth, Authenticated, FileResponse } from 'src/middleware/auth.guard'; import { FileUploadInterceptor } from 'src/middleware/file-upload.interceptor'; import { LoggingRepository } from 'src/repositories/logging.repository'; @@ -38,31 +38,31 @@ export class UserController { ) {} @Get() - @Authenticated() + @Authenticated({ permission: Permission.UserRead }) searchUsers(@Auth() auth: AuthDto): Promise { return this.service.search(auth); } @Get('me') - @Authenticated() + @Authenticated({ permission: Permission.UserRead }) getMyUser(@Auth() auth: AuthDto): Promise { return this.service.getMe(auth); } @Put('me') - @Authenticated() + @Authenticated({ permission: Permission.UserUpdate }) updateMyUser(@Auth() auth: AuthDto, @Body() dto: UserUpdateMeDto): Promise { return this.service.updateMe(auth, dto); } @Get('me/preferences') - @Authenticated() + @Authenticated({ permission: Permission.UserPreferenceRead }) getMyPreferences(@Auth() auth: AuthDto): Promise { return this.service.getMyPreferences(auth); } @Put('me/preferences') - @Authenticated() + @Authenticated({ permission: Permission.UserPreferenceUpdate }) updateMyPreferences( @Auth() auth: AuthDto, @Body() dto: UserPreferencesUpdateDto, @@ -71,43 +71,43 @@ export class UserController { } @Get('me/license') - @Authenticated() + @Authenticated({ permission: Permission.UserLicenseRead }) getUserLicense(@Auth() auth: AuthDto): Promise { return this.service.getLicense(auth); } @Put('me/license') - @Authenticated() + @Authenticated({ permission: Permission.UserLicenseUpdate }) async setUserLicense(@Auth() auth: AuthDto, @Body() license: LicenseKeyDto): Promise { return this.service.setLicense(auth, license); } @Delete('me/license') - @Authenticated() + @Authenticated({ permission: Permission.UserLicenseDelete }) async deleteUserLicense(@Auth() auth: AuthDto): Promise { await this.service.deleteLicense(auth); } @Get('me/onboarding') - @Authenticated() + @Authenticated({ permission: Permission.UserOnboardingRead }) getUserOnboarding(@Auth() auth: AuthDto): Promise { return this.service.getOnboarding(auth); } @Put('me/onboarding') - @Authenticated() + @Authenticated({ permission: Permission.UserOnboardingUpdate }) async setUserOnboarding(@Auth() auth: AuthDto, @Body() Onboarding: OnboardingDto): Promise { return this.service.setOnboarding(auth, Onboarding); } @Delete('me/onboarding') - @Authenticated() + @Authenticated({ permission: Permission.UserOnboardingDelete }) async deleteUserOnboarding(@Auth() auth: AuthDto): Promise { await this.service.deleteOnboarding(auth); } @Get(':id') - @Authenticated() + @Authenticated({ permission: Permission.UserRead }) getUser(@Param() { id }: UUIDParamDto): Promise { return this.service.get(id); } @@ -116,7 +116,7 @@ export class UserController { @ApiConsumes('multipart/form-data') @ApiBody({ description: 'A new avatar for the user', type: CreateProfileImageDto }) @Post('profile-image') - @Authenticated() + @Authenticated({ permission: Permission.UserProfileImageUpdate }) createProfileImage( @Auth() auth: AuthDto, @UploadedFile() fileInfo: Express.Multer.File, @@ -126,14 +126,14 @@ export class UserController { @Delete('profile-image') @HttpCode(HttpStatus.NO_CONTENT) - @Authenticated() + @Authenticated({ permission: Permission.UserProfileImageDelete }) deleteProfileImage(@Auth() auth: AuthDto): Promise { return this.service.deleteProfileImage(auth); } @Get(':id/profile-image') @FileResponse() - @Authenticated() + @Authenticated({ permission: Permission.UserProfileImageRead }) async getProfileImage(@Res() res: Response, @Next() next: NextFunction, @Param() { id }: UUIDParamDto) { await sendFile(res, next, () => this.service.getProfileImage(id), this.logger); } diff --git a/server/src/cores/storage.core.spec.ts b/server/src/cores/storage.core.spec.ts index 7bb2cdb1be..ed446f9259 100644 --- a/server/src/cores/storage.core.spec.ts +++ b/server/src/cores/storage.core.spec.ts @@ -2,7 +2,6 @@ import { StorageCore } from 'src/cores/storage.core'; import { vitest } from 'vitest'; vitest.mock('src/constants', () => ({ - APP_MEDIA_LOCATION: '/photos', ADDED_IN_PREFIX: 'This property was added in ', DEPRECATED_IN_PREFIX: 'This property was deprecated in ', IWorker: 'IWorker', @@ -10,6 +9,10 @@ vitest.mock('src/constants', () => ({ describe('StorageCore', () => { describe('isImmichPath', () => { + beforeAll(() => { + StorageCore.setMediaLocation('/photos'); + }); + it('should return true for APP_MEDIA_LOCATION path', () => { const immichPath = '/photos'; expect(StorageCore.isImmichPath(immichPath)).toBe(true); diff --git a/server/src/cores/storage.core.ts b/server/src/cores/storage.core.ts index 6576b397e3..06dde4644c 100644 --- a/server/src/cores/storage.core.ts +++ b/server/src/cores/storage.core.ts @@ -1,6 +1,5 @@ import { randomUUID } from 'node:crypto'; import { dirname, join, resolve } from 'node:path'; -import { APP_MEDIA_LOCATION } from 'src/constants'; import { StorageAsset } from 'src/database'; import { AssetFileType, AssetPathType, ImageFormat, PathType, PersonPathType, StorageFolder } from 'src/enum'; import { AssetRepository } from 'src/repositories/asset.repository'; @@ -32,6 +31,8 @@ export type ThumbnailPathEntity = { id: string; ownerId: string }; let instance: StorageCore | null; +let mediaLocation: string | undefined; + export class StorageCore { private constructor( private assetRepository: AssetRepository, @@ -74,6 +75,18 @@ export class StorageCore { instance = null; } + static getMediaLocation(): string { + if (mediaLocation === undefined) { + throw new Error('Media location is not set.'); + } + + return mediaLocation; + } + + static setMediaLocation(location: string) { + mediaLocation = location; + } + static getFolderLocation(folder: StorageFolder, userId: string) { return join(StorageCore.getBaseFolder(folder), userId); } @@ -83,7 +96,7 @@ export class StorageCore { } static getBaseFolder(folder: StorageFolder) { - return join(APP_MEDIA_LOCATION, folder); + return join(StorageCore.getMediaLocation(), folder); } static getPersonThumbnailPath(person: ThumbnailPathEntity) { @@ -108,7 +121,7 @@ export class StorageCore { static isImmichPath(path: string) { const resolvedPath = resolve(path); - const resolvedAppMediaLocation = resolve(APP_MEDIA_LOCATION); + const resolvedAppMediaLocation = StorageCore.getMediaLocation(); const normalizedPath = resolvedPath.endsWith('/') ? resolvedPath : resolvedPath + '/'; const normalizedAppMediaLocation = resolvedAppMediaLocation.endsWith('/') ? resolvedAppMediaLocation diff --git a/server/src/database.ts b/server/src/database.ts index d42b2618a4..e7946cd8fb 100644 --- a/server/src/database.ts +++ b/server/src/database.ts @@ -192,6 +192,7 @@ export type SharedLink = { showExif: boolean; type: SharedLinkType; userId: string; + slug: string | null; }; export type Album = Selectable & { @@ -272,6 +273,8 @@ export type AssetFace = { personId: string | null; sourceType: SourceType; person?: Person | null; + updatedAt: Date; + updateId: string; }; const userColumns = ['id', 'name', 'email', 'avatarColor', 'profileImagePath', 'profileChangedAt'] as const; @@ -351,9 +354,11 @@ export const columns = { 'asset.duration', 'asset.livePhotoVideoId', 'asset.stackId', + 'asset.libraryId', ], syncAlbumUser: ['album_user.albumsId as albumId', 'album_user.usersId as userId', 'album_user.role'], syncStack: ['stack.id', 'stack.createdAt', 'stack.updatedAt', 'stack.primaryAssetId', 'stack.ownerId'], + syncUser: ['id', 'name', 'email', 'avatarColor', 'deletedAt', 'updateId', 'profileImagePath', 'profileChangedAt'], stack: ['stack.id', 'stack.primaryAssetId', 'ownerId'], syncAssetExif: [ 'asset_exif.assetId', diff --git a/server/src/dtos/env.dto.ts b/server/src/dtos/env.dto.ts index 99fd1d2149..3543d8dae9 100644 --- a/server/src/dtos/env.dto.ts +++ b/server/src/dtos/env.dto.ts @@ -1,5 +1,5 @@ import { Transform, Type } from 'class-transformer'; -import { IsEnum, IsInt, IsString } from 'class-validator'; +import { IsEnum, IsInt, IsString, Matches } from 'class-validator'; import { DatabaseSslMode, ImmichEnvironment, LogLevel } from 'src/enum'; import { IsIPRange, Optional, ValidateBoolean } from 'src/validation'; @@ -48,6 +48,10 @@ export class EnvDto { @Optional() IMMICH_LOG_LEVEL?: LogLevel; + @Optional() + @Matches(/^\//, { message: 'IMMICH_MEDIA_LOCATION must be an absolute path' }) + IMMICH_MEDIA_LOCATION?: string; + @IsInt() @Optional() @Type(() => Number) diff --git a/server/src/dtos/search.dto.ts b/server/src/dtos/search.dto.ts index aef78e51ea..f709ad94ab 100644 --- a/server/src/dtos/search.dto.ts +++ b/server/src/dtos/search.dto.ts @@ -126,6 +126,15 @@ export class RandomSearchDto extends BaseSearchWithResultsDto { withPeople?: boolean; } +export class LargeAssetSearchDto extends BaseSearchWithResultsDto { + @Optional() + @IsInt() + @Min(0) + @Type(() => Number) + @ApiProperty({ type: 'integer' }) + minFileSize?: number; +} + export class MetadataSearchDto extends RandomSearchDto { @ValidateUUID({ optional: true }) id?: string; diff --git a/server/src/dtos/shared-link.dto.ts b/server/src/dtos/shared-link.dto.ts index 299590c0e3..011707f1f7 100644 --- a/server/src/dtos/shared-link.dto.ts +++ b/server/src/dtos/shared-link.dto.ts @@ -22,13 +22,17 @@ export class SharedLinkCreateDto { @ValidateUUID({ optional: true }) albumId?: string; + @Optional({ nullable: true, emptyToNull: true }) @IsString() - @Optional() - description?: string; + description?: string | null; + @Optional({ nullable: true, emptyToNull: true }) @IsString() - @Optional() - password?: string; + password?: string | null; + + @Optional({ nullable: true, emptyToNull: true }) + @IsString() + slug?: string | null; @ValidateDate({ optional: true, nullable: true }) expiresAt?: Date | null = null; @@ -44,16 +48,22 @@ export class SharedLinkCreateDto { } export class SharedLinkEditDto { - @Optional() - description?: string; + @Optional({ nullable: true, emptyToNull: true }) + @IsString() + description?: string | null; - @Optional() - password?: string; + @Optional({ nullable: true, emptyToNull: true }) + @IsString() + password?: string | null; + + @Optional({ nullable: true, emptyToNull: true }) + @IsString() + slug?: string | null; @Optional({ nullable: true }) expiresAt?: Date | null; - @Optional() + @ValidateBoolean({ optional: true }) allowUpload?: boolean; @ValidateBoolean({ optional: true }) @@ -99,6 +109,8 @@ export class SharedLinkResponseDto { allowDownload!: boolean; showMetadata!: boolean; + + slug!: string | null; } export function mapSharedLink(sharedLink: SharedLink): SharedLinkResponseDto { @@ -118,6 +130,7 @@ export function mapSharedLink(sharedLink: SharedLink): SharedLinkResponseDto { allowUpload: sharedLink.allowUpload, allowDownload: sharedLink.allowDownload, showMetadata: sharedLink.showExif, + slug: sharedLink.slug, }; } @@ -141,5 +154,6 @@ export function mapSharedLinkWithoutMetadata(sharedLink: SharedLink): SharedLink allowUpload: sharedLink.allowUpload, allowDownload: sharedLink.allowDownload, showMetadata: sharedLink.showExif, + slug: sharedLink.slug, }; } diff --git a/server/src/dtos/sync.dto.ts b/server/src/dtos/sync.dto.ts index 9725539e3d..9c304c0d3c 100644 --- a/server/src/dtos/sync.dto.ts +++ b/server/src/dtos/sync.dto.ts @@ -10,6 +10,7 @@ import { MemoryType, SyncEntityType, SyncRequestType, + UserAvatarColor, UserMetadataKey, } from 'src/enum'; import { UserMetadata } from 'src/types'; @@ -58,7 +59,23 @@ export class SyncUserV1 { id!: string; name!: string; email!: string; + @ValidateEnum({ enum: UserAvatarColor, name: 'UserAvatarColor', nullable: true }) + avatarColor!: UserAvatarColor | null; deletedAt!: Date | null; + hasProfileImage!: boolean; + profileChangedAt!: Date; +} + +@ExtraModel() +export class SyncAuthUserV1 extends SyncUserV1 { + isAdmin!: boolean; + pinCode!: string | null; + oauthId!: string; + storageLabel!: string | null; + @ApiProperty({ type: 'integer' }) + quotaSizeInBytes!: number | null; + @ApiProperty({ type: 'integer' }) + quotaUsageInBytes!: number; } @ExtraModel() @@ -98,6 +115,7 @@ export class SyncAssetV1 { visibility!: AssetVisibility; livePhotoVideoId!: string | null; stackId!: string | null; + libraryId!: string | null; } @ExtraModel() @@ -245,7 +263,6 @@ export class SyncPersonV1 { ownerId!: string; name!: string; birthDate!: Date | null; - thumbnailPath!: string; isHidden!: boolean; isFavorite!: boolean; color!: string | null; @@ -257,17 +274,44 @@ export class SyncPersonDeleteV1 { personId!: string; } +@ExtraModel() +export class SyncAssetFaceV1 { + id!: string; + assetId!: string; + personId!: string | null; + @ApiProperty({ type: 'integer' }) + imageWidth!: number; + @ApiProperty({ type: 'integer' }) + imageHeight!: number; + @ApiProperty({ type: 'integer' }) + boundingBoxX1!: number; + @ApiProperty({ type: 'integer' }) + boundingBoxY1!: number; + @ApiProperty({ type: 'integer' }) + boundingBoxX2!: number; + @ApiProperty({ type: 'integer' }) + boundingBoxY2!: number; + sourceType!: string; +} + +@ExtraModel() +export class SyncAssetFaceDeleteV1 { + assetFaceId!: string; +} + @ExtraModel() export class SyncUserMetadataV1 { userId!: string; - key!: string; + @ValidateEnum({ enum: UserMetadataKey, name: 'UserMetadataKey' }) + key!: UserMetadataKey; value!: UserMetadata[UserMetadataKey]; } @ExtraModel() export class SyncUserMetadataDeleteV1 { userId!: string; - key!: string; + @ValidateEnum({ enum: UserMetadataKey, name: 'UserMetadataKey' }) + key!: UserMetadataKey; } @ExtraModel() @@ -277,6 +321,7 @@ export class SyncAckV1 {} export class SyncResetV1 {} export type SyncItem = { + [SyncEntityType.AuthUserV1]: SyncAuthUserV1; [SyncEntityType.UserV1]: SyncUserV1; [SyncEntityType.UserDeleteV1]: SyncUserDeleteV1; [SyncEntityType.PartnerV1]: SyncPartnerV1; @@ -312,6 +357,8 @@ export type SyncItem = { [SyncEntityType.PartnerStackV1]: SyncStackV1; [SyncEntityType.PersonV1]: SyncPersonV1; [SyncEntityType.PersonDeleteV1]: SyncPersonDeleteV1; + [SyncEntityType.AssetFaceV1]: SyncAssetFaceV1; + [SyncEntityType.AssetFaceDeleteV1]: SyncAssetFaceDeleteV1; [SyncEntityType.UserMetadataV1]: SyncUserMetadataV1; [SyncEntityType.UserMetadataDeleteV1]: SyncUserMetadataDeleteV1; [SyncEntityType.SyncAckV1]: SyncAckV1; diff --git a/server/src/enum.ts b/server/src/enum.ts index 587a76126f..93d271f19c 100644 --- a/server/src/enum.ts +++ b/server/src/enum.ts @@ -17,12 +17,14 @@ export enum ImmichHeader { UserToken = 'x-immich-user-token', SessionToken = 'x-immich-session-token', SharedLinkKey = 'x-immich-share-key', + SharedLinkSlug = 'x-immich-share-slug', Checksum = 'x-immich-checksum', Cid = 'x-immich-cid', } export enum ImmichQuery { SharedLinkKey = 'key', + SharedLinkSlug = 'slug', ApiKey = 'apiKey', SessionKey = 'sessionKey', } @@ -87,31 +89,45 @@ export enum Permission { AssetRead = 'asset.read', AssetUpdate = 'asset.update', AssetDelete = 'asset.delete', + AssetStatistics = 'asset.statistics', AssetShare = 'asset.share', AssetView = 'asset.view', AssetDownload = 'asset.download', AssetUpload = 'asset.upload', + AssetReplace = 'asset.replace', AlbumCreate = 'album.create', AlbumRead = 'album.read', AlbumUpdate = 'album.update', AlbumDelete = 'album.delete', AlbumStatistics = 'album.statistics', - - AlbumAddAsset = 'album.addAsset', - AlbumRemoveAsset = 'album.removeAsset', AlbumShare = 'album.share', AlbumDownload = 'album.download', + AlbumAssetCreate = 'albumAsset.create', + AlbumAssetDelete = 'albumAsset.delete', + + AlbumUserCreate = 'albumUser.create', + AlbumUserUpdate = 'albumUser.update', + AlbumUserDelete = 'albumUser.delete', + + AuthChangePassword = 'auth.changePassword', + AuthDeviceDelete = 'authDevice.delete', ArchiveRead = 'archive.read', + DuplicateRead = 'duplicate.read', + DuplicateDelete = 'duplicate.delete', + FaceCreate = 'face.create', FaceRead = 'face.read', FaceUpdate = 'face.update', FaceDelete = 'face.delete', + JobCreate = 'job.create', + JobRead = 'job.read', + LibraryCreate = 'library.create', LibraryRead = 'library.read', LibraryUpdate = 'library.update', @@ -125,6 +141,10 @@ export enum Permission { MemoryRead = 'memory.read', MemoryUpdate = 'memory.update', MemoryDelete = 'memory.delete', + MemoryStatistics = 'memory.statistics', + + MemoryAssetCreate = 'memoryAsset.create', + MemoryAssetDelete = 'memoryAsset.delete', NotificationCreate = 'notification.create', NotificationRead = 'notification.read', @@ -144,6 +164,19 @@ export enum Permission { PersonMerge = 'person.merge', PersonReassign = 'person.reassign', + PinCodeCreate = 'pinCode.create', + PinCodeUpdate = 'pinCode.update', + PinCodeDelete = 'pinCode.delete', + + ServerAbout = 'server.about', + ServerApkLinks = 'server.apkLinks', + ServerStorage = 'server.storage', + ServerStatistics = 'server.statistics', + + ServerLicenseRead = 'serverLicense.read', + ServerLicenseUpdate = 'serverLicense.update', + ServerLicenseDelete = 'serverLicense.delete', + SessionCreate = 'session.create', SessionRead = 'session.read', SessionUpdate = 'session.update', @@ -160,6 +193,11 @@ export enum Permission { StackUpdate = 'stack.update', StackDelete = 'stack.delete', + SyncStream = 'sync.stream', + SyncCheckpointRead = 'syncCheckpoint.read', + SyncCheckpointUpdate = 'syncCheckpoint.update', + SyncCheckpointDelete = 'syncCheckpoint.delete', + SystemConfigRead = 'systemConfig.read', SystemConfigUpdate = 'systemConfig.update', @@ -172,10 +210,30 @@ export enum Permission { TagDelete = 'tag.delete', TagAsset = 'tag.asset', - AdminUserCreate = 'admin.user.create', - AdminUserRead = 'admin.user.read', - AdminUserUpdate = 'admin.user.update', - AdminUserDelete = 'admin.user.delete', + UserRead = 'user.read', + UserUpdate = 'user.update', + + UserLicenseCreate = 'userLicense.create', + UserLicenseRead = 'userLicense.read', + UserLicenseUpdate = 'userLicense.update', + UserLicenseDelete = 'userLicense.delete', + + UserOnboardingRead = 'userOnboarding.read', + UserOnboardingUpdate = 'userOnboarding.update', + UserOnboardingDelete = 'userOnboarding.delete', + + UserPreferenceRead = 'userPreference.read', + UserPreferenceUpdate = 'userPreference.update', + + UserProfileImageCreate = 'userProfileImage.create', + UserProfileImageRead = 'userProfileImage.read', + UserProfileImageUpdate = 'userProfileImage.update', + UserProfileImageDelete = 'userProfileImage.delete', + + AdminUserCreate = 'adminUser.create', + AdminUserRead = 'adminUser.read', + AdminUserUpdate = 'adminUser.update', + AdminUserDelete = 'adminUser.delete', } export enum SharedLinkType { @@ -198,6 +256,7 @@ export enum StorageFolder { } export enum SystemMetadataKey { + MediaLocation = 'MediaLocation', ReverseGeocodingState = 'reverse-geocoding-state', FacialRecognitionState = 'facial-recognition-state', MemoriesState = 'memories-state', @@ -354,6 +413,11 @@ export enum LogLevel { Fatal = 'fatal', } +export enum ApiCustomExtension { + Permission = 'x-immich-permission', + AdminOnly = 'x-immich-admin-only', +} + export enum MetadataKey { AuthRoute = 'auth_route', AdminRoute = 'admin_route', @@ -416,6 +480,8 @@ export enum DatabaseExtension { export enum BootstrapEventPriority { // Database service should be initialized before anything else, most other services need database access DatabaseService = -200, + // Detect and configure the media location before jobs are queued which may use it + StorageService = -195, // Other services may need to queue jobs on bootstrap. JobService = -190, // Initialise config after other bootstrap services, stop other services from using config on bootstrap @@ -544,6 +610,7 @@ export enum DatabaseLock { CLIPDimSize = 512, Library = 1337, NightlyJobs = 600, + MediaLocation = 700, GetSystemConfig = 69, BackupDatabase = 42, MemoryCreation = 777, @@ -557,6 +624,7 @@ export enum SyncRequestType { AlbumAssetExifsV1 = 'AlbumAssetExifsV1', AssetsV1 = 'AssetsV1', AssetExifsV1 = 'AssetExifsV1', + AuthUsersV1 = 'AuthUsersV1', MemoriesV1 = 'MemoriesV1', MemoryToAssetsV1 = 'MemoryToAssetsV1', PartnersV1 = 'PartnersV1', @@ -566,10 +634,13 @@ export enum SyncRequestType { StacksV1 = 'StacksV1', UsersV1 = 'UsersV1', PeopleV1 = 'PeopleV1', + AssetFacesV1 = 'AssetFacesV1', UserMetadataV1 = 'UserMetadataV1', } export enum SyncEntityType { + AuthUserV1 = 'AuthUserV1', + UserV1 = 'UserV1', UserDeleteV1 = 'UserDeleteV1', @@ -617,6 +688,9 @@ export enum SyncEntityType { PersonV1 = 'PersonV1', PersonDeleteV1 = 'PersonDeleteV1', + AssetFaceV1 = 'AssetFaceV1', + AssetFaceDeleteV1 = 'AssetFaceDeleteV1', + UserMetadataV1 = 'UserMetadataV1', UserMetadataDeleteV1 = 'UserMetadataDeleteV1', diff --git a/server/src/main.ts b/server/src/main.ts index 591fc156d9..68ea396e7a 100644 --- a/server/src/main.ts +++ b/server/src/main.ts @@ -1,5 +1,6 @@ import { CommandFactory } from 'nest-commander'; import { ChildProcess, fork } from 'node:child_process'; +import { dirname, join } from 'node:path'; import { Worker } from 'node:worker_threads'; import { ImmichAdminModule } from 'src/app.module'; import { ImmichWorker, LogLevel } from 'src/enum'; @@ -33,14 +34,18 @@ const onExit = (name: string, exitCode: number | null) => { function bootstrapWorker(name: ImmichWorker) { console.log(`Starting ${name} worker`); + // eslint-disable-next-line unicorn/prefer-module + const basePath = dirname(__filename); + const workerFile = join(basePath, 'workers', `${name}.js`); + let worker: Worker | ChildProcess; if (name === ImmichWorker.Api) { - worker = fork(`./dist/workers/${name}.js`, [], { + worker = fork(workerFile, [], { execArgv: process.execArgv.map((arg) => (arg.startsWith('--inspect') ? '--inspect=0.0.0.0:9231' : arg)), }); apiProcess = worker; } else { - worker = new Worker(`./dist/workers/${name}.js`); + worker = new Worker(workerFile); } worker.on('error', (error) => onError(name, error)); diff --git a/server/src/middleware/auth.guard.ts b/server/src/middleware/auth.guard.ts index 238f99257a..80d7a37435 100644 --- a/server/src/middleware/auth.guard.ts +++ b/server/src/middleware/auth.guard.ts @@ -7,10 +7,10 @@ import { createParamDecorator, } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; -import { ApiBearerAuth, ApiCookieAuth, ApiOkResponse, ApiQuery, ApiSecurity } from '@nestjs/swagger'; +import { ApiBearerAuth, ApiCookieAuth, ApiExtension, ApiOkResponse, ApiQuery, ApiSecurity } from '@nestjs/swagger'; import { Request } from 'express'; import { AuthDto } from 'src/dtos/auth.dto'; -import { ImmichQuery, MetadataKey, Permission } from 'src/enum'; +import { ApiCustomExtension, ImmichQuery, MetadataKey, Permission } from 'src/enum'; import { LoggingRepository } from 'src/repositories/logging.repository'; import { AuthService, LoginDetails } from 'src/services/auth.service'; import { UAParser } from 'ua-parser-js'; @@ -19,16 +19,27 @@ type AdminRoute = { admin?: true }; type SharedLinkRoute = { sharedLink?: true }; type AuthenticatedOptions = { permission?: Permission } & (AdminRoute | SharedLinkRoute); -export const Authenticated = (options?: AuthenticatedOptions): MethodDecorator => { +export const Authenticated = (options: AuthenticatedOptions = {}): MethodDecorator => { const decorators: MethodDecorator[] = [ ApiBearerAuth(), ApiCookieAuth(), ApiSecurity(MetadataKey.ApiKeySecurity), - SetMetadata(MetadataKey.AuthRoute, options || {}), + SetMetadata(MetadataKey.AuthRoute, options), ]; + if ((options as AdminRoute).admin) { + decorators.push(ApiExtension(ApiCustomExtension.AdminOnly, true)); + } + + if (options?.permission) { + decorators.push(ApiExtension(ApiCustomExtension.Permission, options.permission ?? Permission.All)); + } + if ((options as SharedLinkRoute)?.sharedLink) { - decorators.push(ApiQuery({ name: ImmichQuery.SharedLinkKey, type: String, required: false })); + decorators.push( + ApiQuery({ name: ImmichQuery.SharedLinkKey, type: String, required: false }), + ApiQuery({ name: ImmichQuery.SharedLinkSlug, type: String, required: false }), + ); } return applyDecorators(...decorators); diff --git a/server/src/migrations/1645130759468-CreateUserTable.ts b/server/src/migrations/1645130759468-CreateUserTable.ts deleted file mode 100644 index 1aedfb67d4..0000000000 --- a/server/src/migrations/1645130759468-CreateUserTable.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateUserTable1645130759468 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`); - await queryRunner.query(` - create table if not exists users - ( - id uuid default uuid_generate_v4() not null - constraint "PK_a3ffb1c0c8416b9fc6f907b7433" - primary key, - email varchar not null, - password varchar not null, - salt varchar not null, - "createdAt" timestamp default now() not null - ); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`drop table users`); - } -} diff --git a/server/src/migrations/1645130777674-CreateDeviceInfoTable.ts b/server/src/migrations/1645130777674-CreateDeviceInfoTable.ts deleted file mode 100644 index bf53d7910b..0000000000 --- a/server/src/migrations/1645130777674-CreateDeviceInfoTable.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateDeviceInfoTable1645130777674 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - create table if not exists device_info - ( - id serial - constraint "PK_b1c15a80b0a4e5f4eebadbdd92c" - primary key, - "userId" varchar not null, - "deviceId" varchar not null, - "deviceType" varchar not null, - "notificationToken" varchar, - "createdAt" timestamp default now() not null, - "isAutoBackup" boolean default false not null, - constraint "UQ_ebad78f36b10d15fbea8560e107" - unique ("userId", "deviceId") - ); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`drop table device_info`); - } -} diff --git a/server/src/migrations/1645130805273-CreateAssetsTable.ts b/server/src/migrations/1645130805273-CreateAssetsTable.ts deleted file mode 100644 index 82727e18a5..0000000000 --- a/server/src/migrations/1645130805273-CreateAssetsTable.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateAssetsTable1645130805273 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - create table if not exists assets - ( - id uuid default uuid_generate_v4() not null - constraint "PK_da96729a8b113377cfb6a62439c" - primary key, - "deviceAssetId" varchar not null, - "userId" varchar not null, - "deviceId" varchar not null, - type varchar not null, - "originalPath" varchar not null, - "resizePath" varchar, - "createdAt" varchar not null, - "modifiedAt" varchar not null, - "isFavorite" boolean default false not null, - "mimeType" varchar, - duration varchar, - constraint "UQ_b599ab0bd9574958acb0b30a90e" - unique ("deviceAssetId", "userId", "deviceId") - ); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`drop table assets`); - } -} diff --git a/server/src/migrations/1645130817965-CreateExifTable.ts b/server/src/migrations/1645130817965-CreateExifTable.ts deleted file mode 100644 index af46b86507..0000000000 --- a/server/src/migrations/1645130817965-CreateExifTable.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateExifTable1645130817965 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - create table if not exists exif - ( - id serial - constraint "PK_28663352d85078ad0046dafafaa" - primary key, - "assetId" uuid not null - constraint "REL_c0117fdbc50b917ef9067740c4" - unique - constraint "FK_c0117fdbc50b917ef9067740c44" - references assets - on delete cascade, - make varchar, - model varchar, - "imageName" varchar, - "exifImageWidth" integer, - "exifImageHeight" integer, - "fileSizeInByte" integer, - orientation varchar, - "dateTimeOriginal" timestamp with time zone, - "modifyDate" timestamp with time zone, - "lensModel" varchar, - "fNumber" double precision, - "focalLength" double precision, - iso integer, - "exposureTime" double precision, - latitude double precision, - longitude double precision - ); - - create unique index if not exists "IDX_c0117fdbc50b917ef9067740c4" on exif ("assetId"); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`drop table exif`); - } -} diff --git a/server/src/migrations/1645130870184-CreateSmartInfoTable.ts b/server/src/migrations/1645130870184-CreateSmartInfoTable.ts deleted file mode 100644 index 9c81f6099a..0000000000 --- a/server/src/migrations/1645130870184-CreateSmartInfoTable.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateSmartInfoTable1645130870184 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - create table if not exists smart_info - ( - id serial - constraint "PK_0beace66440e9713f5c40470e46" - primary key, - "assetId" uuid not null - constraint "UQ_5e3753aadd956110bf3ec0244ac" - unique - constraint "FK_5e3753aadd956110bf3ec0244ac" - references assets - on delete cascade, - tags text[] - ); - - create unique index if not exists "IDX_5e3753aadd956110bf3ec0244a" - on smart_info ("assetId"); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - drop table smart_info; - `); - } -} diff --git a/server/src/migrations/1646249209023-AddExifTextSearchColumn.ts b/server/src/migrations/1646249209023-AddExifTextSearchColumn.ts deleted file mode 100644 index 071d4bd40d..0000000000 --- a/server/src/migrations/1646249209023-AddExifTextSearchColumn.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddExifTextSearchColumn1646249209023 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE exif - ADD COLUMN IF NOT EXISTS exif_text_searchable_column tsvector - GENERATED ALWAYS AS ( - TO_TSVECTOR('english', - COALESCE(make, '') || ' ' || - COALESCE(model, '') || ' ' || - COALESCE(orientation, '') || ' ' || - COALESCE("lensModel", '') - ) - ) STORED; - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE exif - DROP COLUMN IF EXISTS exif_text_searchable_column; - `); - } -} diff --git a/server/src/migrations/1646249734844-CreateExifTextSearchIndex.ts b/server/src/migrations/1646249734844-CreateExifTextSearchIndex.ts deleted file mode 100644 index 664d06c4bc..0000000000 --- a/server/src/migrations/1646249734844-CreateExifTextSearchIndex.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateExifTextSearchIndex1646249734844 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - CREATE INDEX exif_text_searchable_idx - ON exif - USING GIN (exif_text_searchable_column); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - DROP INDEX IF EXISTS exif_text_searchable_idx ON exif; - `); - } -} diff --git a/server/src/migrations/1646709533213-AddRegionCityToExIf.ts b/server/src/migrations/1646709533213-AddRegionCityToExIf.ts deleted file mode 100644 index e2d226cfa4..0000000000 --- a/server/src/migrations/1646709533213-AddRegionCityToExIf.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddRegionCityToExIf1646709533213 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE exif - ADD COLUMN if not exists city varchar; - - ALTER TABLE exif - ADD COLUMN if not exists state varchar; - - ALTER TABLE exif - ADD COLUMN if not exists country varchar; - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE exif - DROP COLUMN city; - - ALTER TABLE exif - DROP COLUMN state; - - ALTER TABLE exif - DROP COLUMN country; - `); - } -} diff --git a/server/src/migrations/1646710459852-AddLocationToExifTextSearch.ts b/server/src/migrations/1646710459852-AddLocationToExifTextSearch.ts deleted file mode 100644 index 9116bf2866..0000000000 --- a/server/src/migrations/1646710459852-AddLocationToExifTextSearch.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddLocationToExifTextSearch1646710459852 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE exif - DROP COLUMN IF EXISTS exif_text_searchable_column; - - ALTER TABLE exif - ADD COLUMN IF NOT EXISTS exif_text_searchable_column tsvector - GENERATED ALWAYS AS ( - TO_TSVECTOR('english', - COALESCE(make, '') || ' ' || - COALESCE(model, '') || ' ' || - COALESCE(orientation, '') || ' ' || - COALESCE("lensModel", '') || ' ' || - COALESCE("city", '') || ' ' || - COALESCE("state", '') || ' ' || - COALESCE("country", '') - ) - ) STORED; - - CREATE INDEX exif_text_searchable_idx - ON exif - USING GIN (exif_text_searchable_column); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE exif - DROP COLUMN IF EXISTS exif_text_searchable_column; - - DROP INDEX IF EXISTS exif_text_searchable_idx ON exif; - `); - } -} diff --git a/server/src/migrations/1648317474768-AddObjectColumnToSmartInfo.ts b/server/src/migrations/1648317474768-AddObjectColumnToSmartInfo.ts deleted file mode 100644 index bdf3dff5df..0000000000 --- a/server/src/migrations/1648317474768-AddObjectColumnToSmartInfo.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddObjectColumnToSmartInfo1648317474768 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE smart_info - ADD COLUMN if not exists objects text[]; - - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE smart_info - DROP COLUMN objects; - `); - } -} diff --git a/server/src/migrations/1649643216111-CreateSharedAlbumAndRelatedTables.ts b/server/src/migrations/1649643216111-CreateSharedAlbumAndRelatedTables.ts deleted file mode 100644 index ef633d6f12..0000000000 --- a/server/src/migrations/1649643216111-CreateSharedAlbumAndRelatedTables.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateSharedAlbumAndRelatedTables1649643216111 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - // Create shared_albums - await queryRunner.query(` - create table if not exists shared_albums - ( - id uuid default uuid_generate_v4() not null - constraint "PK_7f71c7b5bc7c87b8f94c9a93a00" - primary key, - "ownerId" varchar not null, - "albumName" varchar default 'Untitled Album'::character varying not null, - "createdAt" timestamp with time zone default now() not null, - "albumThumbnailAssetId" varchar - ); - - comment on column shared_albums."albumThumbnailAssetId" is 'Asset ID to be used as thumbnail'; - `); - - // Create user_shared_album - await queryRunner.query(` - create table if not exists user_shared_album - ( - id serial - constraint "PK_b6562316a98845a7b3e9a25cdd0" - primary key, - "albumId" uuid not null - constraint "FK_7b3bf0f5f8da59af30519c25f18" - references shared_albums - on delete cascade, - "sharedUserId" uuid not null - constraint "FK_543c31211653e63e080ba882eb5" - references users, - constraint "PK_unique_user_in_album" - unique ("albumId", "sharedUserId") - ); - `); - - // Create asset_shared_album - await queryRunner.query( - ` - create table if not exists asset_shared_album - ( - id serial - constraint "PK_a34e076afbc601d81938e2c2277" - primary key, - "albumId" uuid not null - constraint "FK_a8b79a84996cef6ba6a3662825d" - references shared_albums - on delete cascade, - "assetId" uuid not null - constraint "FK_64f2e7d68d1d1d8417acc844a4a" - references assets - on delete cascade, - constraint "UQ_a1e2734a1ce361e7a26f6b28288" - unique ("albumId", "assetId") - ); - `, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - drop table asset_shared_album; - drop table user_shared_album; - drop table shared_albums; - `); - } -} diff --git a/server/src/migrations/1652633525943-UpdateUserTableWithAdminAndName.ts b/server/src/migrations/1652633525943-UpdateUserTableWithAdminAndName.ts deleted file mode 100644 index af5082ebb2..0000000000 --- a/server/src/migrations/1652633525943-UpdateUserTableWithAdminAndName.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UpdateUserTableWithAdminAndName1652633525943 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - alter table users - add column if not exists "firstName" varchar default ''; - - alter table users - add column if not exists "lastName" varchar default ''; - - alter table users - add column if not exists "profileImagePath" varchar default ''; - - alter table users - add column if not exists "isAdmin" bool default false; - - alter table users - add column if not exists "isFirstLoggedIn" bool default true; - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - alter table users - drop column "firstName"; - - alter table users - drop column "lastName"; - - alter table users - drop column "isAdmin"; - - `); - } -} diff --git a/server/src/migrations/1653214255670-UpdateAssetTableWithWebpPath.ts b/server/src/migrations/1653214255670-UpdateAssetTableWithWebpPath.ts deleted file mode 100644 index 4de9684f18..0000000000 --- a/server/src/migrations/1653214255670-UpdateAssetTableWithWebpPath.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UpdateAssetTableWithWebpPath1653214255670 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - alter table assets - add column if not exists "webpPath" varchar default ''; - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - alter table assets - drop column if exists "webpPath"; - `); - } -} diff --git a/server/src/migrations/1654299904583-UpdateAssetTableWithEncodeVideoPath.ts b/server/src/migrations/1654299904583-UpdateAssetTableWithEncodeVideoPath.ts deleted file mode 100644 index 169f7db171..0000000000 --- a/server/src/migrations/1654299904583-UpdateAssetTableWithEncodeVideoPath.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UpdateAssetTableWithEncodeVideoPath1654299904583 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - alter table assets - add column if not exists "encodedVideoPath" varchar default ''; - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - alter table assets - drop column if exists "encodedVideoPath"; - `); - } -} diff --git a/server/src/migrations/1655401127251-RenameSharedAlbums.ts b/server/src/migrations/1655401127251-RenameSharedAlbums.ts deleted file mode 100644 index 9bb71fb08c..0000000000 --- a/server/src/migrations/1655401127251-RenameSharedAlbums.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RenameSharedAlbums1655401127251 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE shared_albums RENAME TO albums; - - ALTER TABLE asset_shared_album RENAME TO asset_album; - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE asset_album RENAME TO asset_shared_album; - - ALTER TABLE albums RENAME TO shared_albums; - `); - } -} diff --git a/server/src/migrations/1656338626260-RenameIsFirstLoggedInColumn.ts b/server/src/migrations/1656338626260-RenameIsFirstLoggedInColumn.ts deleted file mode 100644 index c4e4d7cd63..0000000000 --- a/server/src/migrations/1656338626260-RenameIsFirstLoggedInColumn.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RenameIsFirstLoggedInColumn1656338626260 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE users - RENAME COLUMN "isFirstLoggedIn" to "shouldChangePassword"; - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE users - RENAME COLUMN "shouldChangePassword" to "isFirstLoggedIn"; - `); - } -} diff --git a/server/src/migrations/1656888591977-RenameAssetAlbumIdSequence.ts b/server/src/migrations/1656888591977-RenameAssetAlbumIdSequence.ts deleted file mode 100644 index 07d5592f53..0000000000 --- a/server/src/migrations/1656888591977-RenameAssetAlbumIdSequence.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RenameAssetAlbumIdSequence1656888591977 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`alter sequence asset_shared_album_id_seq rename to asset_album_id_seq;`); - await queryRunner.query( - `alter table asset_album alter column id set default nextval('asset_album_id_seq'::regclass);`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`alter sequence asset_album_id_seq rename to asset_shared_album_id_seq;`); - await queryRunner.query( - `alter table asset_album alter column id set default nextval('asset_shared_album_id_seq'::regclass);`, - ); - } -} diff --git a/server/src/migrations/1656888918620-DropExifTextSearchableColumn.ts b/server/src/migrations/1656888918620-DropExifTextSearchableColumn.ts deleted file mode 100644 index 305b67ee84..0000000000 --- a/server/src/migrations/1656888918620-DropExifTextSearchableColumn.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class DropExifTextSearchableColumns1656888918620 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "exif_text_searchable_column"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE exif - DROP COLUMN IF EXISTS exif_text_searchable_column; - - ALTER TABLE exif - ADD COLUMN IF NOT EXISTS exif_text_searchable_column tsvector - GENERATED ALWAYS AS ( - TO_TSVECTOR('english', - COALESCE(make, '') || ' ' || - COALESCE(model, '') || ' ' || - COALESCE(orientation, '') || ' ' || - COALESCE("lensModel", '') || ' ' || - COALESCE("city", '') || ' ' || - COALESCE("state", '') || ' ' || - COALESCE("country", '') - ) - ) STORED; - - CREATE INDEX exif_text_searchable_idx - ON exif - USING GIN (exif_text_searchable_column); - `); - } -} diff --git a/server/src/migrations/1656889061566-MatchMigrationsWithTypeORMEntities.ts b/server/src/migrations/1656889061566-MatchMigrationsWithTypeORMEntities.ts deleted file mode 100644 index 00a66d78e9..0000000000 --- a/server/src/migrations/1656889061566-MatchMigrationsWithTypeORMEntities.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class MatchMigrationsWithTypeORMEntities1656889061566 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "exifTextSearchableColumn" tsvector GENERATED ALWAYS AS (TO_TSVECTOR('english', - COALESCE(make, '') || ' ' || - COALESCE(model, '') || ' ' || - COALESCE(orientation, '') || ' ' || - COALESCE("lensModel", '') || ' ' || - COALESCE("city", '') || ' ' || - COALESCE("state", '') || ' ' || - COALESCE("country", ''))) STORED`); - await queryRunner.query(`ALTER TABLE "exif" ALTER COLUMN "exifTextSearchableColumn" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "firstName" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "lastName" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "isAdmin" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "profileImagePath" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "shouldChangePassword" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "FK_a8b79a84996cef6ba6a3662825d"`); - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "FK_64f2e7d68d1d1d8417acc844a4a"`); - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "UQ_a1e2734a1ce361e7a26f6b28288"`); - await queryRunner.query( - `ALTER TABLE "asset_album" ADD CONSTRAINT "UQ_unique_asset_in_album" UNIQUE ("albumId", "assetId")`, - ); - await queryRunner.query( - `ALTER TABLE "asset_album" ADD CONSTRAINT "FK_256a30a03a4a0aff0394051397d" FOREIGN KEY ("albumId") REFERENCES "albums"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, - ); - await queryRunner.query( - `ALTER TABLE "asset_album" ADD CONSTRAINT "FK_7ae4e03729895bf87e056d7b598" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "shouldChangePassword" DROP NOT NULL`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "profileImagePath" DROP NOT NULL`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "isAdmin" DROP NOT NULL`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "lastName" DROP NOT NULL`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "firstName" DROP NOT NULL`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "exifTextSearchableColumn"`); - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "FK_7ae4e03729895bf87e056d7b598"`); - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "FK_256a30a03a4a0aff0394051397d"`); - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "UQ_unique_asset_in_album"`); - await queryRunner.query( - `ALTER TABLE "asset_album" ADD CONSTRAINT "UQ_a1e2734a1ce361e7a26f6b28288" UNIQUE ("albumId", "assetId")`, - ); - await queryRunner.query( - `ALTER TABLE "asset_album" ADD CONSTRAINT "FK_64f2e7d68d1d1d8417acc844a4a" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, - ); - await queryRunner.query( - `ALTER TABLE "asset_album" ADD CONSTRAINT "FK_a8b79a84996cef6ba6a3662825d" FOREIGN KEY ("albumId") REFERENCES "albums"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, - ); - } -} diff --git a/server/src/migrations/1658860470248-AddExifImageNameAsSearchableText.ts b/server/src/migrations/1658860470248-AddExifImageNameAsSearchableText.ts deleted file mode 100644 index 3b175be3e5..0000000000 --- a/server/src/migrations/1658860470248-AddExifImageNameAsSearchableText.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddExifImageNameAsSearchableText1658860470248 implements MigrationInterface { - name = 'AddExifImageNameAsSearchableText1658860470248'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "exifTextSearchableColumn"`); - await queryRunner.query(`ALTER TABLE "exif" ADD "exifTextSearchableColumn" tsvector GENERATED ALWAYS AS (TO_TSVECTOR('english', - COALESCE(make, '') || ' ' || - COALESCE(model, '') || ' ' || - COALESCE(orientation, '') || ' ' || - COALESCE("lensModel", '') || ' ' || - COALESCE("imageName", '') || ' ' || - COALESCE("city", '') || ' ' || - COALESCE("state", '') || ' ' || - COALESCE("country", ''))) STORED`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "exifTextSearchableColumn" tsvector NOT NULL`); - } -} diff --git a/server/src/migrations/1661011331242-AddCaption.ts b/server/src/migrations/1661011331242-AddCaption.ts deleted file mode 100644 index f6370a7b66..0000000000 --- a/server/src/migrations/1661011331242-AddCaption.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddCaption1661011331242 implements MigrationInterface { - name = 'AddCaption1661011331242'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "description" text DEFAULT ''`); - await queryRunner.query(`ALTER TABLE "exif" ADD "fps" double precision`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "fps"`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "description"`); - } -} diff --git a/server/src/migrations/1661528919411-ChangeExifFileSizeInByteToBigInt.ts b/server/src/migrations/1661528919411-ChangeExifFileSizeInByteToBigInt.ts deleted file mode 100644 index da614e7f9c..0000000000 --- a/server/src/migrations/1661528919411-ChangeExifFileSizeInByteToBigInt.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class ChangeExifFileSizeInByteToBigInt1661528919411 implements MigrationInterface { - name = 'ChangeExifFileSizeInByteToBigInt1661528919411'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE exif - ALTER COLUMN "fileSizeInByte" type bigint using "fileSizeInByte"::bigint; - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE exif - ALTER COLUMN "fileSizeInByte" type integer using "fileSizeInByte"::integer; - `); - } -} diff --git a/server/src/migrations/1661881837496-AddAssetChecksum.ts b/server/src/migrations/1661881837496-AddAssetChecksum.ts deleted file mode 100644 index 2901b4f554..0000000000 --- a/server/src/migrations/1661881837496-AddAssetChecksum.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddAssetChecksum1661881837496 implements MigrationInterface { - name = 'AddAssetChecksum1661881837496'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "checksum" bytea`); - await queryRunner.query( - `CREATE INDEX "IDX_64c507300988dd1764f9a6530c" ON "assets" ("checksum") WHERE 'checksum' IS NOT NULL`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_64c507300988dd1764f9a6530c"`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "checksum"`); - } -} diff --git a/server/src/migrations/1661971370662-UpdateAssetTableWithNewUniqueConstraint.ts b/server/src/migrations/1661971370662-UpdateAssetTableWithNewUniqueConstraint.ts deleted file mode 100644 index 15fa467878..0000000000 --- a/server/src/migrations/1661971370662-UpdateAssetTableWithNewUniqueConstraint.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UpdateAssetTableWithNewUniqueConstraint1661971370662 implements MigrationInterface { - name = 'UpdateAssetTableWithNewUniqueConstraint1661971370662'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "UQ_b599ab0bd9574958acb0b30a90e"`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "UQ_userid_checksum" UNIQUE ("userId", "checksum")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "UQ_userid_checksum"`); - await queryRunner.query( - `ALTER TABLE "assets" ADD CONSTRAINT "UQ_b599ab0bd9574958acb0b30a90e" UNIQUE ("deviceAssetId", "userId", "deviceId")`, - ); - } -} diff --git a/server/src/migrations/1662427365521-FixTimestampDataTypeInAssetTable.ts b/server/src/migrations/1662427365521-FixTimestampDataTypeInAssetTable.ts deleted file mode 100644 index a0ce4dc8c6..0000000000 --- a/server/src/migrations/1662427365521-FixTimestampDataTypeInAssetTable.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class FixTimestampDataTypeInAssetTable1662427365521 implements MigrationInterface { - name = 'FixTimestampDataTypeInAssetTable1662427365521'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ALTER COLUMN "exifTextSearchableColumn" SET NOT NULL`); - await queryRunner.query( - `ALTER TABLE "assets" ALTER COLUMN "createdAt" TYPE timestamptz USING "createdAt"::timestamptz`, - ); - await queryRunner.query( - `ALTER TABLE "assets" ALTER COLUMN "modifiedAt" TYPE timestamptz USING "createdAt"::timestamptz`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "createdAt" TYPE varchar USING "createdAt"::varchar`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "modifiedAt" TYPE varchar USING "createdAt"::varchar`); - await queryRunner.query(`ALTER TABLE "exif" ALTER COLUMN "exifTextSearchableColumn" DROP NOT NULL`); - } -} diff --git a/server/src/migrations/1665540663419-CreateSystemConfigTable.ts b/server/src/migrations/1665540663419-CreateSystemConfigTable.ts deleted file mode 100644 index 40dd87c644..0000000000 --- a/server/src/migrations/1665540663419-CreateSystemConfigTable.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateSystemConfigTable1665540663419 implements MigrationInterface { - name = 'CreateSystemConfigTable1665540663419'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE TABLE "system_config" ("key" character varying NOT NULL, "value" character varying, CONSTRAINT "PK_aab69295b445016f56731f4d535" PRIMARY KEY ("key"))`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE "system_config"`); - } -} diff --git a/server/src/migrations/1667762360744-AddingDeletedAtColumnInUserEntity.ts b/server/src/migrations/1667762360744-AddingDeletedAtColumnInUserEntity.ts deleted file mode 100644 index 1e80fc089a..0000000000 --- a/server/src/migrations/1667762360744-AddingDeletedAtColumnInUserEntity.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddingDeletedAtColumnInUserEntity1667762360744 implements MigrationInterface { - name = 'AddingDeletedAtColumnInUserEntity1667762360744'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "deletedAt" TIMESTAMP`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "deletedAt"`); - } -} diff --git a/server/src/migrations/1668383120461-AddLivePhotosRelatedColumnToAssetTable.ts b/server/src/migrations/1668383120461-AddLivePhotosRelatedColumnToAssetTable.ts deleted file mode 100644 index 62ce314f30..0000000000 --- a/server/src/migrations/1668383120461-AddLivePhotosRelatedColumnToAssetTable.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddLivePhotosRelatedColumnToAssetTable1668383120461 implements MigrationInterface { - name = 'AddLivePhotosRelatedColumnToAssetTable1668383120461' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "isVisible" boolean NOT NULL DEFAULT true`); - await queryRunner.query(`ALTER TABLE "assets" ADD "livePhotoVideoId" uuid`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "livePhotoVideoId"`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "isVisible"`); - } - -} diff --git a/server/src/migrations/1668835311083-UpdateUserTableForOIDC.ts b/server/src/migrations/1668835311083-UpdateUserTableForOIDC.ts deleted file mode 100644 index 044b79c808..0000000000 --- a/server/src/migrations/1668835311083-UpdateUserTableForOIDC.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class UpdateUserTableForOIDC1668835311083 implements MigrationInterface { - name = 'UpdateUserTableForOIDC1668835311083' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "password" SET DEFAULT ''`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "salt" SET DEFAULT ''`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "salt" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "password" DROP DEFAULT`); - } - -} diff --git a/server/src/migrations/1670104716264-OAuthId.ts b/server/src/migrations/1670104716264-OAuthId.ts deleted file mode 100644 index 46b99a79d5..0000000000 --- a/server/src/migrations/1670104716264-OAuthId.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class OAuthId1670104716264 implements MigrationInterface { - name = 'OAuthId1670104716264' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "oauthId" character varying NOT NULL DEFAULT ''`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "oauthId"`); - } - -} diff --git a/server/src/migrations/1670257571385-CreateTagsTable.ts b/server/src/migrations/1670257571385-CreateTagsTable.ts deleted file mode 100644 index 75fba9249c..0000000000 --- a/server/src/migrations/1670257571385-CreateTagsTable.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class CreateTagsTable1670257571385 implements MigrationInterface { - name = 'CreateTagsTable1670257571385' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "tags" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "type" character varying NOT NULL, "name" character varying NOT NULL, "userId" uuid NOT NULL, "renameTagId" uuid, CONSTRAINT "UQ_tag_name_userId" UNIQUE ("name", "userId"), CONSTRAINT "PK_e7dc17249a1148a1970748eda99" PRIMARY KEY ("id")); COMMENT ON COLUMN "tags"."renameTagId" IS 'The new renamed tagId'`); - await queryRunner.query(`CREATE TABLE "tag_asset" ("assetsId" uuid NOT NULL, "tagsId" uuid NOT NULL, CONSTRAINT "PK_ef5346fe522b5fb3bc96454747e" PRIMARY KEY ("assetsId", "tagsId"))`); - await queryRunner.query(`CREATE INDEX "IDX_f8e8a9e893cb5c54907f1b798e" ON "tag_asset" ("assetsId") `); - await queryRunner.query(`CREATE INDEX "IDX_e99f31ea4cdf3a2c35c7287eb4" ON "tag_asset" ("tagsId") `); - await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "FK_92e67dc508c705dd66c94615576" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "tag_asset" ADD CONSTRAINT "FK_f8e8a9e893cb5c54907f1b798e9" FOREIGN KEY ("assetsId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "tag_asset" ADD CONSTRAINT "FK_e99f31ea4cdf3a2c35c7287eb42" FOREIGN KEY ("tagsId") REFERENCES "tags"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "tag_asset" DROP CONSTRAINT "FK_e99f31ea4cdf3a2c35c7287eb42"`); - await queryRunner.query(`ALTER TABLE "tag_asset" DROP CONSTRAINT "FK_f8e8a9e893cb5c54907f1b798e9"`); - await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "FK_92e67dc508c705dd66c94615576"`); - await queryRunner.query(`DROP INDEX "IDX_e99f31ea4cdf3a2c35c7287eb4"`); - await queryRunner.query(`DROP INDEX "IDX_f8e8a9e893cb5c54907f1b798e"`); - await queryRunner.query(`DROP TABLE "tag_asset"`); - await queryRunner.query(`DROP TABLE "tags"`); - } - -} diff --git a/server/src/migrations/1670607437008-TruncateOldConfigItems.ts b/server/src/migrations/1670607437008-TruncateOldConfigItems.ts deleted file mode 100644 index 0a82783f89..0000000000 --- a/server/src/migrations/1670607437008-TruncateOldConfigItems.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class TruncateOldConfigItems1670607437008 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`TRUNCATE TABLE "system_config"`); - } - - public async down(): Promise { - // noop - } -} diff --git a/server/src/migrations/1670633210032-AddUserEmailUniqueConstraint.ts b/server/src/migrations/1670633210032-AddUserEmailUniqueConstraint.ts deleted file mode 100644 index 50a67ae94f..0000000000 --- a/server/src/migrations/1670633210032-AddUserEmailUniqueConstraint.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddUserEmailUniqueConstraint1670633210032 implements MigrationInterface { - name = 'AddUserEmailUniqueConstraint1670633210032' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD CONSTRAINT "UQ_97672ac88f789774dd47f7c8be3" UNIQUE ("email")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP CONSTRAINT "UQ_97672ac88f789774dd47f7c8be3"`); - } - -} diff --git a/server/src/migrations/1672109862870-DropSaltColumn.ts b/server/src/migrations/1672109862870-DropSaltColumn.ts deleted file mode 100644 index 91ca5ade11..0000000000 --- a/server/src/migrations/1672109862870-DropSaltColumn.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class DropSaltColumn1672109862870 implements MigrationInterface { - name = 'DropSaltColumn1672109862870' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "salt"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "salt" character varying NOT NULL DEFAULT ''`); - } - -} diff --git a/server/src/migrations/1672502270115-AddAPIKeys.ts b/server/src/migrations/1672502270115-AddAPIKeys.ts deleted file mode 100644 index e72b3dc2fe..0000000000 --- a/server/src/migrations/1672502270115-AddAPIKeys.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAPIKeys1672502270115 implements MigrationInterface { - name = 'AddAPIKeys1672502270115' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "api_keys" ("id" SERIAL NOT NULL, "name" character varying NOT NULL, "key" character varying NOT NULL, "userId" uuid NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "PK_5c8a79801b44bd27b79228e1dad" PRIMARY KEY ("id"))`); - await queryRunner.query(`ALTER TABLE "api_keys" ADD CONSTRAINT "FK_6c2e267ae764a9413b863a29342" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "api_keys" DROP CONSTRAINT "FK_6c2e267ae764a9413b863a29342"`); - await queryRunner.query(`DROP TABLE "api_keys"`); - } - -} diff --git a/server/src/migrations/1673150490490-AddSharedLinkTable.ts b/server/src/migrations/1673150490490-AddSharedLinkTable.ts deleted file mode 100644 index 8d5bd2f5a5..0000000000 --- a/server/src/migrations/1673150490490-AddSharedLinkTable.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddSharedLinkTable1673150490490 implements MigrationInterface { - name = 'AddSharedLinkTable1673150490490' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "shared_links" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "description" character varying, "userId" character varying NOT NULL, "key" bytea NOT NULL, "type" character varying NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "expiresAt" TIMESTAMP WITH TIME ZONE, "allowUpload" boolean NOT NULL DEFAULT false, "albumId" uuid, CONSTRAINT "UQ_sharedlink_key" UNIQUE ("key"), CONSTRAINT "PK_642e2b0f619e4876e5f90a43465" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_sharedlink_key" ON "shared_links" ("key") `); - await queryRunner.query(`CREATE TABLE "shared_link__asset" ("assetsId" uuid NOT NULL, "sharedLinksId" uuid NOT NULL, CONSTRAINT "PK_9b4f3687f9b31d1e311336b05e3" PRIMARY KEY ("assetsId", "sharedLinksId"))`); - await queryRunner.query(`CREATE INDEX "IDX_5b7decce6c8d3db9593d6111a6" ON "shared_link__asset" ("assetsId") `); - await queryRunner.query(`CREATE INDEX "IDX_c9fab4aa97ffd1b034f3d6581a" ON "shared_link__asset" ("sharedLinksId") `); - await queryRunner.query(`ALTER TABLE "shared_links" ADD CONSTRAINT "FK_0c6ce9058c29f07cdf7014eac66" FOREIGN KEY ("albumId") REFERENCES "albums"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "shared_link__asset" ADD CONSTRAINT "FK_5b7decce6c8d3db9593d6111a66" FOREIGN KEY ("assetsId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "shared_link__asset" ADD CONSTRAINT "FK_c9fab4aa97ffd1b034f3d6581ab" FOREIGN KEY ("sharedLinksId") REFERENCES "shared_links"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_link__asset" DROP CONSTRAINT "FK_c9fab4aa97ffd1b034f3d6581ab"`); - await queryRunner.query(`ALTER TABLE "shared_link__asset" DROP CONSTRAINT "FK_5b7decce6c8d3db9593d6111a66"`); - await queryRunner.query(`ALTER TABLE "shared_links" DROP CONSTRAINT "FK_0c6ce9058c29f07cdf7014eac66"`); - await queryRunner.query(`DROP INDEX "IDX_c9fab4aa97ffd1b034f3d6581a"`); - await queryRunner.query(`DROP INDEX "IDX_5b7decce6c8d3db9593d6111a6"`); - await queryRunner.query(`DROP TABLE "shared_link__asset"`); - await queryRunner.query(`DROP INDEX "IDX_sharedlink_key"`); - await queryRunner.query(`DROP TABLE "shared_links"`); - } - -} diff --git a/server/src/migrations/1673907194740-AddMorePermissionToSharedLink.ts b/server/src/migrations/1673907194740-AddMorePermissionToSharedLink.ts deleted file mode 100644 index af0a0280a8..0000000000 --- a/server/src/migrations/1673907194740-AddMorePermissionToSharedLink.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddMorePermissionToSharedLink1673907194740 implements MigrationInterface { - name = 'AddMorePermissionToSharedLink1673907194740'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" ADD "allowDownload" boolean NOT NULL DEFAULT true`); - await queryRunner.query(`ALTER TABLE "shared_links" ADD "showExif" boolean NOT NULL DEFAULT true`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" DROP COLUMN "showExif"`); - await queryRunner.query(`ALTER TABLE "shared_links" DROP COLUMN "allowDownload"`); - } -} diff --git a/server/src/migrations/1674263302005-RemoveVideoCodecConfigOption.ts b/server/src/migrations/1674263302005-RemoveVideoCodecConfigOption.ts deleted file mode 100644 index 5f64b11559..0000000000 --- a/server/src/migrations/1674263302005-RemoveVideoCodecConfigOption.ts +++ /dev/null @@ -1,12 +0,0 @@ -import {MigrationInterface, QueryRunner} from 'typeorm'; - -export class RemoveVideoCodecConfigOption1674263302006 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DELETE FROM "system_config" WHERE key = 'ffmpeg.targetVideoCodec'`); - await queryRunner.query(`DELETE FROM "system_config" WHERE key = 'ffmpeg.targetAudioCodec'`); - } - - public async down(): Promise { - // noop - } -} diff --git a/server/src/migrations/1674342044239-CreateUserTokenEntity.ts b/server/src/migrations/1674342044239-CreateUserTokenEntity.ts deleted file mode 100644 index e289787f91..0000000000 --- a/server/src/migrations/1674342044239-CreateUserTokenEntity.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class CreateUserTokenEntity1674342044239 implements MigrationInterface { - name = 'CreateUserTokenEntity1674342044239' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "user_token" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "token" character varying NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "userId" uuid, CONSTRAINT "PK_48cb6b5c20faa63157b3c1baf7f" PRIMARY KEY ("id"))`); - await queryRunner.query(`ALTER TABLE "user_token" ADD CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "user_token" DROP CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918"`); - await queryRunner.query(`DROP TABLE "user_token"`); - } - -} diff --git a/server/src/migrations/1674757936889-AlterExifExposureTimeToString.ts b/server/src/migrations/1674757936889-AlterExifExposureTimeToString.ts deleted file mode 100644 index de21e180b7..0000000000 --- a/server/src/migrations/1674757936889-AlterExifExposureTimeToString.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AlterExifExposureTimeToString1674757936889 implements MigrationInterface { - name = 'AlterExifExposureTimeToString1674757936889' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "exposureTime"`); - await queryRunner.query(`ALTER TABLE "exif" ADD "exposureTime" character varying`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "exposureTime"`); - await queryRunner.query(`ALTER TABLE "exif" ADD "exposureTime" double precision`); - } - -} diff --git a/server/src/migrations/1674774248319-TruncateAPIKeys.ts b/server/src/migrations/1674774248319-TruncateAPIKeys.ts deleted file mode 100644 index efbb5c41af..0000000000 --- a/server/src/migrations/1674774248319-TruncateAPIKeys.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm" - -export class TruncateAPIKeys1674774248319 implements MigrationInterface { - name = 'TruncateAPIKeys1674774248319' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`TRUNCATE TABLE "api_keys"`); - } - - public async down(): Promise { - //noop - } - -} diff --git a/server/src/migrations/1674939383309-AddSharedLinkUserForeignKeyConstraint.ts b/server/src/migrations/1674939383309-AddSharedLinkUserForeignKeyConstraint.ts deleted file mode 100644 index 9119c57065..0000000000 --- a/server/src/migrations/1674939383309-AddSharedLinkUserForeignKeyConstraint.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddSharedLinkUserForeignKeyConstraint1674939383309 implements MigrationInterface { - name = 'AddSharedLinkUserForeignKeyConstraint1674939383309'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" ALTER COLUMN "userId" TYPE varchar(36)`); - await queryRunner.query(`ALTER TABLE "shared_links" ALTER COLUMN "userId" TYPE uuid using "userId"::uuid`); - await queryRunner.query( - `ALTER TABLE "shared_links" ADD CONSTRAINT "FK_66fe3837414c5a9f1c33ca49340" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" DROP CONSTRAINT "FK_66fe3837414c5a9f1c33ca49340"`); - await queryRunner.query(`ALTER TABLE "shared_links" ALTER COLUMN "userId" TYPE character varying`); - } -} diff --git a/server/src/migrations/1675667878312-AddUpdatedAtColumnToAlbumsUsersAssets.ts b/server/src/migrations/1675667878312-AddUpdatedAtColumnToAlbumsUsersAssets.ts deleted file mode 100644 index 90c00b38e9..0000000000 --- a/server/src/migrations/1675667878312-AddUpdatedAtColumnToAlbumsUsersAssets.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddUpdatedAtColumnToAlbumsUsersAssets1675667878312 implements MigrationInterface { - name = 'AddUpdatedAtColumnToAlbumsUsersAssets1675667878312'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" ADD "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`); - await queryRunner.query(`ALTER TABLE "users" ADD "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`); - await queryRunner.query(`ALTER TABLE "assets" ADD "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" DROP COLUMN "updatedAt"`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "updatedAt"`); - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "updatedAt"`); - } -} diff --git a/server/src/migrations/1675701909594-AddAlbumUserForeignKeyConstraint.ts b/server/src/migrations/1675701909594-AddAlbumUserForeignKeyConstraint.ts deleted file mode 100644 index 0898accfb7..0000000000 --- a/server/src/migrations/1675701909594-AddAlbumUserForeignKeyConstraint.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddAlbumUserForeignKeyConstraint1675701909594 implements MigrationInterface { - name = 'AddAlbumUserForeignKeyConstraint1675701909594'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DELETE FROM "albums" WHERE "ownerId"::uuid NOT IN (SELECT id FROM "users") `) - await queryRunner.query(`ALTER TABLE "albums" ALTER COLUMN "ownerId" TYPE varchar(36)`); - await queryRunner.query(`ALTER TABLE "albums" ALTER COLUMN "ownerId" TYPE uuid using "ownerId"::uuid`); - await queryRunner.query( - `ALTER TABLE "albums" ADD CONSTRAINT "FK_b22c53f35ef20c28c21637c85f4" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" DROP CONSTRAINT "FK_b22c53f35ef20c28c21637c85f4"`); - await queryRunner.query(`ALTER TABLE "albums" ALTER COLUMN "ownerId" TYPE character varying`); - } -} diff --git a/server/src/migrations/1675808874445-APIKeyUUIDPrimaryKey.ts b/server/src/migrations/1675808874445-APIKeyUUIDPrimaryKey.ts deleted file mode 100644 index 368a9ca9c5..0000000000 --- a/server/src/migrations/1675808874445-APIKeyUUIDPrimaryKey.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class APIKeyUUIDPrimaryKey1675808874445 implements MigrationInterface { - name = 'APIKeyUUIDPrimaryKey1675808874445' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "api_keys" DROP CONSTRAINT "PK_5c8a79801b44bd27b79228e1dad"`); - await queryRunner.query(`ALTER TABLE "api_keys" DROP COLUMN "id"`); - await queryRunner.query(`ALTER TABLE "api_keys" ADD "id" uuid NOT NULL DEFAULT uuid_generate_v4()`); - await queryRunner.query(`ALTER TABLE "api_keys" ADD CONSTRAINT "PK_5c8a79801b44bd27b79228e1dad" PRIMARY KEY ("id")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "api_keys" DROP CONSTRAINT "PK_5c8a79801b44bd27b79228e1dad"`); - await queryRunner.query(`ALTER TABLE "api_keys" DROP COLUMN "id"`); - await queryRunner.query(`ALTER TABLE "api_keys" ADD "id" SERIAL NOT NULL`); - await queryRunner.query(`ALTER TABLE "api_keys" ADD CONSTRAINT "PK_5c8a79801b44bd27b79228e1dad" PRIMARY KEY ("id")`); - } - -} diff --git a/server/src/migrations/1675812532822-FixAlbumEntityTypeORM.ts b/server/src/migrations/1675812532822-FixAlbumEntityTypeORM.ts deleted file mode 100644 index 6f48ac736d..0000000000 --- a/server/src/migrations/1675812532822-FixAlbumEntityTypeORM.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class FixAlbumEntityTypeORM1675812532822 implements MigrationInterface { - name = 'FixAlbumEntityTypeORM1675812532822' - - public async up(queryRunner: QueryRunner): Promise { - - await queryRunner.query(`ALTER TABLE "asset_album" RENAME TO "albums_assets_assets"`); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" DROP CONSTRAINT "FK_7ae4e03729895bf87e056d7b598"`); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" DROP CONSTRAINT "FK_256a30a03a4a0aff0394051397d"`); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" DROP CONSTRAINT "UQ_unique_asset_in_album"`); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" DROP CONSTRAINT "PK_a34e076afbc601d81938e2c2277"`); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" DROP COLUMN "id"`); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" RENAME COLUMN "albumId" TO "albumsId"`); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" RENAME COLUMN "assetId" TO "assetsId"`); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" ADD CONSTRAINT "PK_c67bc36fa845fb7b18e0e398180" PRIMARY KEY ("albumsId", "assetsId")`); - await queryRunner.query(`CREATE INDEX "IDX_e590fa396c6898fcd4a50e4092" ON "albums_assets_assets" ("albumsId") `); - await queryRunner.query(`CREATE INDEX "IDX_4bd1303d199f4e72ccdf998c62" ON "albums_assets_assets" ("assetsId") `); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" ADD CONSTRAINT "FK_e590fa396c6898fcd4a50e40927" FOREIGN KEY ("albumsId") REFERENCES "albums"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "albums_assets_assets" ADD CONSTRAINT "FK_4bd1303d199f4e72ccdf998c621" FOREIGN KEY ("assetsId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - - await queryRunner.query(`ALTER TABLE "user_shared_album" RENAME TO "albums_shared_users_users"`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" DROP CONSTRAINT "FK_543c31211653e63e080ba882eb5"`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" DROP CONSTRAINT "FK_7b3bf0f5f8da59af30519c25f18"`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" DROP CONSTRAINT "PK_unique_user_in_album"`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" DROP CONSTRAINT "PK_b6562316a98845a7b3e9a25cdd0"`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" DROP COLUMN "id"`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" RENAME COLUMN "albumId" TO "albumsId"`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" RENAME COLUMN "sharedUserId" TO "usersId"`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" ADD CONSTRAINT "PK_7df55657e0b2e8b626330a0ebc8" PRIMARY KEY ("albumsId", "usersId")`); - await queryRunner.query(`CREATE INDEX "IDX_427c350ad49bd3935a50baab73" ON "albums_shared_users_users" ("albumsId") `); - await queryRunner.query(`CREATE INDEX "IDX_f48513bf9bccefd6ff3ad30bd0" ON "albums_shared_users_users" ("usersId") `); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" ADD CONSTRAINT "FK_427c350ad49bd3935a50baab737" FOREIGN KEY ("albumsId") REFERENCES "albums"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" ADD CONSTRAINT "FK_f48513bf9bccefd6ff3ad30bd06" FOREIGN KEY ("usersId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - - await queryRunner.query(`ALTER TABLE "albums" DROP CONSTRAINT "FK_b22c53f35ef20c28c21637c85f4"`) - await queryRunner.query(`ALTER TABLE "albums" ADD CONSTRAINT "FK_b22c53f35ef20c28c21637c85f4" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums_assets_assets" RENAME TO "asset_album"`); - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" RENAME TO "user_shared_album"`); - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "FK_e590fa396c6898fcd4a50e40927"`); - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "FK_4bd1303d199f4e72ccdf998c621"`); - await queryRunner.query(`ALTER TABLE "user_shared_album" DROP CONSTRAINT "FK_427c350ad49bd3935a50baab737"`); - await queryRunner.query(`ALTER TABLE "user_shared_album" DROP CONSTRAINT "FK_f48513bf9bccefd6ff3ad30bd06"`); - await queryRunner.query(`DROP INDEX "IDX_427c350ad49bd3935a50baab73"`); - await queryRunner.query(`DROP INDEX "IDX_f48513bf9bccefd6ff3ad30bd0"`); - await queryRunner.query(`DROP INDEX "IDX_e590fa396c6898fcd4a50e4092"`); - await queryRunner.query(`DROP INDEX "IDX_4bd1303d199f4e72ccdf998c62"`); - - await queryRunner.query(`ALTER TABLE "albums" DROP CONSTRAINT "FK_b22c53f35ef20c28c21637c85f4"`); - await queryRunner.query( - `ALTER TABLE "albums" ADD CONSTRAINT "FK_b22c53f35ef20c28c21637c85f4" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - - await queryRunner.query(`ALTER TABLE "user_shared_album" DROP CONSTRAINT "PK_7df55657e0b2e8b626330a0ebc8"`); - await queryRunner.query(`ALTER TABLE "user_shared_album" ADD CONSTRAINT "PK_323f8dcbe85373722886940f143" PRIMARY KEY ("albumsId")`); - await queryRunner.query(`ALTER TABLE "user_shared_album" DROP COLUMN "usersId"`); - await queryRunner.query(`ALTER TABLE "user_shared_album" DROP CONSTRAINT "PK_323f8dcbe85373722886940f143"`); - await queryRunner.query(`ALTER TABLE "user_shared_album" DROP COLUMN "albumsId"`); - await queryRunner.query(`ALTER TABLE "user_shared_album" ADD "sharedUserId" uuid NOT NULL`); - await queryRunner.query(`ALTER TABLE "user_shared_album" ADD "albumId" uuid NOT NULL`); - await queryRunner.query(`ALTER TABLE "user_shared_album" ADD "id" SERIAL NOT NULL`); - await queryRunner.query(`ALTER TABLE "user_shared_album" ADD CONSTRAINT "PK_b6562316a98845a7b3e9a25cdd0" PRIMARY KEY ("id")`); - await queryRunner.query(`ALTER TABLE "user_shared_album" ADD CONSTRAINT "PK_unique_user_in_album" UNIQUE ("albumId", "sharedUserId")`); - await queryRunner.query(`ALTER TABLE "user_shared_album" ADD CONSTRAINT "FK_7b3bf0f5f8da59af30519c25f18" FOREIGN KEY ("albumId") REFERENCES "albums"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "user_shared_album" ADD CONSTRAINT "FK_543c31211653e63e080ba882eb5" FOREIGN KEY ("sharedUserId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "PK_c67bc36fa845fb7b18e0e398180"`); - await queryRunner.query(`ALTER TABLE "asset_album" ADD CONSTRAINT "PK_b4f2e5b96efc25cbccd80a04f7a" PRIMARY KEY ("albumsId")`); - await queryRunner.query(`ALTER TABLE "asset_album" DROP CONSTRAINT "PK_b4f2e5b96efc25cbccd80a04f7a"`); - await queryRunner.query(`ALTER TABLE "asset_album" RENAME COLUMN "albumsId" TO "albumId"`); - await queryRunner.query(`ALTER TABLE "asset_album" RENAME COLUMN "assetsId" TO "assetId"`); - await queryRunner.query(`ALTER TABLE "asset_album" ADD "id" SERIAL NOT NULL`); - await queryRunner.query(`ALTER TABLE "asset_album" ADD CONSTRAINT "PK_a34e076afbc601d81938e2c2277" PRIMARY KEY ("id")`); - await queryRunner.query(`ALTER TABLE "asset_album" ADD CONSTRAINT "UQ_unique_asset_in_album" UNIQUE ("albumId", "assetId")`); - await queryRunner.query(`ALTER TABLE "asset_album" ADD CONSTRAINT "FK_256a30a03a4a0aff0394051397d" FOREIGN KEY ("albumId") REFERENCES "albums"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "asset_album" ADD CONSTRAINT "FK_7ae4e03729895bf87e056d7b598" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - } - -} diff --git a/server/src/migrations/1676437878377-AppleContentIdentifier.ts b/server/src/migrations/1676437878377-AppleContentIdentifier.ts deleted file mode 100644 index 8d11139878..0000000000 --- a/server/src/migrations/1676437878377-AppleContentIdentifier.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AppleContentIdentifier1676437878377 implements MigrationInterface { - name = 'AppleContentIdentifier1676437878377'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "livePhotoCID" character varying`); - await queryRunner.query(`CREATE INDEX "IDX_live_photo_cid" ON "exif" ("livePhotoCID") `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_live_photo_cid"`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "livePhotoCID"`); - } -} diff --git a/server/src/migrations/1676680127415-FixAssetRelations.ts b/server/src/migrations/1676680127415-FixAssetRelations.ts deleted file mode 100644 index 439e86a78a..0000000000 --- a/server/src/migrations/1676680127415-FixAssetRelations.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class FixAssetRelations1676680127415 implements MigrationInterface { - name = 'FixAssetRelations1676680127415' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" RENAME COLUMN "modifiedAt" TO "fileModifiedAt"`); - await queryRunner.query(`ALTER TABLE "assets" RENAME COLUMN "createdAt" TO "fileCreatedAt"`); - - await queryRunner.query(`ALTER TABLE "assets" RENAME COLUMN "userId" TO "ownerId"`); - await queryRunner.query(`ALTER TABLE assets ALTER COLUMN "ownerId" TYPE uuid USING "ownerId"::uuid;`); - - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "UQ_16294b83fa8c0149719a1f631ef" UNIQUE ("livePhotoVideoId")`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "FK_2c5ac0d6fb58b238fd2068de67d" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "FK_16294b83fa8c0149719a1f631ef" FOREIGN KEY ("livePhotoVideoId") REFERENCES "assets"("id") ON DELETE SET NULL ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_16294b83fa8c0149719a1f631ef"`); - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_2c5ac0d6fb58b238fd2068de67d"`); - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "UQ_16294b83fa8c0149719a1f631ef"`); - - await queryRunner.query(`ALTER TABLE "assets" RENAME COLUMN "fileCreatedAt" TO "createdAt"`); - await queryRunner.query(`ALTER TABLE "assets" RENAME COLUMN "fileModifiedAt" TO "modifiedAt"`); - - await queryRunner.query(`ALTER TABLE "assets" RENAME COLUMN "ownerId" TO "userId"`); - await queryRunner.query(`ALTER TABLE assets ALTER COLUMN "userId" TYPE varchar`); - } - -} diff --git a/server/src/migrations/1676721296440-AssetCreatedAtField.ts b/server/src/migrations/1676721296440-AssetCreatedAtField.ts deleted file mode 100644 index 304c7b2190..0000000000 --- a/server/src/migrations/1676721296440-AssetCreatedAtField.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AssetCreatedAtField1676721296440 implements MigrationInterface { - name = 'AssetCreatedAtField1676721296440' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "createdAt"`); - } - -} diff --git a/server/src/migrations/1676848629119-ExifEntityDefinitionFixes.ts b/server/src/migrations/1676848629119-ExifEntityDefinitionFixes.ts deleted file mode 100644 index 947559ed2d..0000000000 --- a/server/src/migrations/1676848629119-ExifEntityDefinitionFixes.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class ExifEntityDefinitionFixes1676848629119 implements MigrationInterface { - name = 'ExifEntityDefinitionFixes1676848629119' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ALTER COLUMN "description" SET NOT NULL`); - - await queryRunner.query(`DROP INDEX "IDX_c0117fdbc50b917ef9067740c4"`); - await queryRunner.query(`ALTER TABLE "exif" DROP CONSTRAINT "PK_28663352d85078ad0046dafafaa"`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "id"`); - await queryRunner.query(`ALTER TABLE "exif" DROP CONSTRAINT "FK_c0117fdbc50b917ef9067740c44"`); - await queryRunner.query(`ALTER TABLE "exif" ADD CONSTRAINT "PK_c0117fdbc50b917ef9067740c44" PRIMARY KEY ("assetId")`); - await queryRunner.query(`ALTER TABLE "exif" ADD CONSTRAINT "FK_c0117fdbc50b917ef9067740c44" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ALTER COLUMN "description" DROP NOT NULL`); - - await queryRunner.query(`ALTER TABLE "exif" DROP CONSTRAINT "FK_c0117fdbc50b917ef9067740c44"`); - await queryRunner.query(`ALTER TABLE "exif" DROP CONSTRAINT "PK_c0117fdbc50b917ef9067740c44"`); - await queryRunner.query(`ALTER TABLE "exif" ADD CONSTRAINT "FK_c0117fdbc50b917ef9067740c44" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "exif" ADD "id" SERIAL NOT NULL`); - await queryRunner.query(`ALTER TABLE "exif" ADD CONSTRAINT "PK_28663352d85078ad0046dafafaa" PRIMARY KEY ("id")`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_c0117fdbc50b917ef9067740c4" ON "exif" ("assetId") `); - } - -} diff --git a/server/src/migrations/1676848694786-SharedLinkEntityDefinitionFixes.ts b/server/src/migrations/1676848694786-SharedLinkEntityDefinitionFixes.ts deleted file mode 100644 index d48f543fef..0000000000 --- a/server/src/migrations/1676848694786-SharedLinkEntityDefinitionFixes.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class SharedLinkEntityDefinitionFixes1676848694786 implements MigrationInterface { - name = 'SharedLinkEntityDefinitionFixes1676848694786' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" ALTER COLUMN "createdAt" SET DEFAULT now()`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" ALTER COLUMN "createdAt" DROP DEFAULT`); - } - -} diff --git a/server/src/migrations/1676852143506-SmartInfoEntityDefinitionFixes.ts b/server/src/migrations/1676852143506-SmartInfoEntityDefinitionFixes.ts deleted file mode 100644 index e089619c6d..0000000000 --- a/server/src/migrations/1676852143506-SmartInfoEntityDefinitionFixes.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class SmartInfoEntityDefinitionFixes1676852143506 implements MigrationInterface { - name = 'SmartInfoEntityDefinitionFixes1676852143506' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_5e3753aadd956110bf3ec0244a"`); - await queryRunner.query(`ALTER TABLE "smart_info" DROP CONSTRAINT "PK_0beace66440e9713f5c40470e46"`); - await queryRunner.query(`ALTER TABLE "smart_info" DROP COLUMN "id"`); - await queryRunner.query(`ALTER TABLE "smart_info" DROP CONSTRAINT "FK_5e3753aadd956110bf3ec0244ac"`); - await queryRunner.query(`ALTER TABLE "smart_info" ADD CONSTRAINT "PK_5e3753aadd956110bf3ec0244ac" PRIMARY KEY ("assetId")`); - await queryRunner.query(`ALTER TABLE "smart_info" ADD CONSTRAINT "FK_5e3753aadd956110bf3ec0244ac" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "smart_info" DROP CONSTRAINT "FK_5e3753aadd956110bf3ec0244ac"`); - await queryRunner.query(`ALTER TABLE "smart_info" DROP CONSTRAINT "PK_5e3753aadd956110bf3ec0244ac"`); - await queryRunner.query(`ALTER TABLE "smart_info" ADD CONSTRAINT "FK_5e3753aadd956110bf3ec0244ac" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "smart_info" ADD "id" SERIAL NOT NULL`); - await queryRunner.query(`ALTER TABLE "smart_info" ADD CONSTRAINT "PK_0beace66440e9713f5c40470e46" PRIMARY KEY ("id")`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_5e3753aadd956110bf3ec0244a" ON "smart_info" ("assetId") `); - } - -} diff --git a/server/src/migrations/1677497925328-AddExifTimeZone.ts b/server/src/migrations/1677497925328-AddExifTimeZone.ts deleted file mode 100644 index 33f958336e..0000000000 --- a/server/src/migrations/1677497925328-AddExifTimeZone.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddExifTimeZone1677497925328 implements MigrationInterface { - name = 'AddExifTimeZone1677497925328' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "timeZone" character varying`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "timeZone"`); - } - -} diff --git a/server/src/migrations/1677535643119-AddIndexForAlbumInSharedLinkTable.ts b/server/src/migrations/1677535643119-AddIndexForAlbumInSharedLinkTable.ts deleted file mode 100644 index 986b5ebd20..0000000000 --- a/server/src/migrations/1677535643119-AddIndexForAlbumInSharedLinkTable.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddIndexForAlbumInSharedLinkTable1677535643119 implements MigrationInterface { - name = 'AddIndexForAlbumInSharedLinkTable1677535643119' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX "IDX_sharedlink_albumId" ON "shared_links" ("albumId") `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_sharedlink_albumId"`); - } - -} diff --git a/server/src/migrations/1677613712565-AlbumThumbnailRelation.ts b/server/src/migrations/1677613712565-AlbumThumbnailRelation.ts deleted file mode 100644 index 71f022dcf6..0000000000 --- a/server/src/migrations/1677613712565-AlbumThumbnailRelation.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AlbumThumbnailRelation1677613712565 implements MigrationInterface { - name = 'AlbumThumbnailRelation1677613712565'; - - public async up(queryRunner: QueryRunner): Promise { - // Make sure all albums have a valid albumThumbnailAssetId UUID or NULL. - await queryRunner.query(` - UPDATE "albums" - SET - "albumThumbnailAssetId" = ( - SELECT - "albums_assets2"."assetsId" - FROM - "assets" "assets", - "albums_assets_assets" "albums_assets2" - WHERE - "albums_assets2"."assetsId" = "assets"."id" - AND "albums_assets2"."albumsId" = "albums"."id" - ORDER BY - "assets"."fileCreatedAt" DESC - LIMIT 1 - ), - "updatedAt" = CURRENT_TIMESTAMP - WHERE - "albums"."albumThumbnailAssetId" IS NULL - AND EXISTS ( - SELECT 1 - FROM "albums_assets_assets" "albums_assets" - WHERE "albums"."id" = "albums_assets"."albumsId" - ) - OR "albums"."albumThumbnailAssetId" IS NOT NULL - AND NOT EXISTS ( - SELECT 1 - FROM "albums_assets_assets" "albums_assets" - WHERE - "albums"."id" = "albums_assets"."albumsId" - AND "albums"."albumThumbnailAssetId" = "albums_assets"."assetsId"::varchar - ) - `); - - await queryRunner.query(` - ALTER TABLE "albums" - ALTER COLUMN "albumThumbnailAssetId" - TYPE uuid USING "albumThumbnailAssetId"::uuid - `); - - await queryRunner.query(` - ALTER TABLE "albums" ADD CONSTRAINT "FK_05895aa505a670300d4816debce" FOREIGN KEY ("albumThumbnailAssetId") REFERENCES "assets"("id") ON DELETE SET NULL ON UPDATE CASCADE - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" DROP CONSTRAINT "FK_05895aa505a670300d4816debce"`); - - await queryRunner.query(` - ALTER TABLE "albums" ALTER COLUMN "albumThumbnailAssetId" TYPE varchar USING "albumThumbnailAssetId"::varchar - `); - } -} diff --git a/server/src/migrations/1677971458822-AddCLIPEncodeDataColumn.ts b/server/src/migrations/1677971458822-AddCLIPEncodeDataColumn.ts deleted file mode 100644 index 82f8176b0d..0000000000 --- a/server/src/migrations/1677971458822-AddCLIPEncodeDataColumn.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddCLIPEncodeDataColumn1677971458822 implements MigrationInterface { - name = 'AddCLIPEncodeDataColumn1677971458822'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "smart_info" ADD "clipEmbedding" numeric(20,19) array`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "smart_info" DROP COLUMN "clipEmbedding"`); - } -} diff --git a/server/src/migrations/1679751316282-UpdateTranscodeOption.ts b/server/src/migrations/1679751316282-UpdateTranscodeOption.ts deleted file mode 100644 index 989622e831..0000000000 --- a/server/src/migrations/1679751316282-UpdateTranscodeOption.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UpdateTranscodeOption1679751316282 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - UPDATE system_config - SET - key = 'ffmpeg.transcode', - value = '"all"' - WHERE - key = 'ffmpeg.transcodeAll' AND value = 'true' - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - UPDATE system_config - SET - key = 'ffmpeg.transcodeAll', - value = 'true' - WHERE - key = 'ffmpeg.transcode' AND value = '"all"' - `); - - await queryRunner.query(`DELETE FROM "system_config" WHERE key = 'ffmpeg.transcode'`); - } -} diff --git a/server/src/migrations/1679901204458-ClipEmbeddingFloat4.ts b/server/src/migrations/1679901204458-ClipEmbeddingFloat4.ts deleted file mode 100644 index 3afa8c6d10..0000000000 --- a/server/src/migrations/1679901204458-ClipEmbeddingFloat4.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class ClipEmbeddingFloat41679901204458 implements MigrationInterface { - name = 'ClipEmbeddingFloat41679901204458'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "smart_info" ALTER COLUMN "clipEmbedding" TYPE real array USING "clipEmbedding"::real array`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "smart_info" ALTER COLUMN "clipEmbedding" TYPE numeric(20,19) array USING "clipEmbedding"::numeric(20,19) array`, - ); - } -} diff --git a/server/src/migrations/1680632845740-AddIsArchivedColumn.ts b/server/src/migrations/1680632845740-AddIsArchivedColumn.ts deleted file mode 100644 index 325e37f489..0000000000 --- a/server/src/migrations/1680632845740-AddIsArchivedColumn.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddIsArchivedColumn1680632845740 implements MigrationInterface { - name = 'AddIsArchivedColumn1680632845740' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "isArchived" boolean NOT NULL DEFAULT false`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "isArchived"`); - } - -} diff --git a/server/src/migrations/1680694465853-RemoveRedundantConstraints.ts b/server/src/migrations/1680694465853-RemoveRedundantConstraints.ts deleted file mode 100644 index 1c3b051b02..0000000000 --- a/server/src/migrations/1680694465853-RemoveRedundantConstraints.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class RemoveRedundantConstraints1680694465853 implements MigrationInterface { - name = 'RemoveRedundantConstraints1680694465853' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP CONSTRAINT "REL_c0117fdbc50b917ef9067740c4"`); - await queryRunner.query(`ALTER TABLE "smart_info" DROP CONSTRAINT "UQ_5e3753aadd956110bf3ec0244ac"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "smart_info" ADD CONSTRAINT "UQ_5e3753aadd956110bf3ec0244ac" UNIQUE ("assetId")`); - await queryRunner.query(`ALTER TABLE "exif" ADD CONSTRAINT "REL_c0117fdbc50b917ef9067740c4" UNIQUE ("assetId")`); - } - -} diff --git a/server/src/migrations/1681144628393-AddOriginalFileNameToAssetTable.ts b/server/src/migrations/1681144628393-AddOriginalFileNameToAssetTable.ts deleted file mode 100644 index 7c547108b5..0000000000 --- a/server/src/migrations/1681144628393-AddOriginalFileNameToAssetTable.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddOriginalFileNameToAssetTable1681144628393 implements MigrationInterface { - name = 'AddOriginalFileNameToAssetTable1681144628393'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "originalFileName" character varying`); - - await queryRunner.query(` - UPDATE assets a - SET "originalFileName" = ( - select e."imageName" - from exif e - where e."assetId" = a.id - ) - `); - - await queryRunner.query(` - UPDATE assets a - SET "originalFileName" = a.id - where a."originalFileName" IS NULL or a."originalFileName" = '' - `); - - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "originalFileName" SET NOT NULL`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "originalFileName"`); - } -} diff --git a/server/src/migrations/1681159594469-RemoveImageNameFromEXIFTable.ts b/server/src/migrations/1681159594469-RemoveImageNameFromEXIFTable.ts deleted file mode 100644 index e188ea3506..0000000000 --- a/server/src/migrations/1681159594469-RemoveImageNameFromEXIFTable.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveImageNameFromEXIFTable1681159594469 implements MigrationInterface { - name = 'RemoveImageNameFromEXIFTable1681159594469'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN IF EXISTS "exifTextSearchableColumn"`); - await queryRunner.query(`ALTER TABLE "exif" ADD "exifTextSearchableColumn" tsvector GENERATED ALWAYS AS (TO_TSVECTOR('english', - COALESCE(make, '') || ' ' || - COALESCE(model, '') || ' ' || - COALESCE(orientation, '') || ' ' || - COALESCE("lensModel", '') || ' ' || - COALESCE("city", '') || ' ' || - COALESCE("state", '') || ' ' || - COALESCE("country", ''))) STORED NOT NULL`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "imageName"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "exifTextSearchableColumn"`); - await queryRunner.query(`ALTER TABLE "exif" ADD "exifTextSearchableColumn" tsvector GENERATED ALWAYS AS (TO_TSVECTOR('english', - COALESCE(make, '') || ' ' || - COALESCE(model, '') || ' ' || - COALESCE(orientation, '') || ' ' || - COALESCE("lensModel", '') || ' ' || - COALESCE("imageName", '') || ' ' || - COALESCE("city", '') || ' ' || - COALESCE("state", '') || ' ' || - COALESCE("country", ''))) STORED NOT NULL`); - await queryRunner.query(`ALTER TABLE "exif" ADD "imageName" character varying`); - } -} diff --git a/server/src/migrations/1682371561743-FixNullableRelations.ts b/server/src/migrations/1682371561743-FixNullableRelations.ts deleted file mode 100644 index 42c34f9399..0000000000 --- a/server/src/migrations/1682371561743-FixNullableRelations.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class FixNullableRelations1682371561743 implements MigrationInterface { - name = 'FixNullableRelations1682371561743'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "user_token" DROP CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918"`); - await queryRunner.query(`ALTER TABLE "user_token" ALTER COLUMN "userId" SET NOT NULL`); - await queryRunner.query( - `ALTER TABLE "user_token" ADD CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "user_token" DROP CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918"`); - await queryRunner.query(`ALTER TABLE "user_token" ALTER COLUMN "userId" DROP NOT NULL`); - await queryRunner.query( - `ALTER TABLE "user_token" ADD CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - } -} diff --git a/server/src/migrations/1682371791038-AddDeviceInfoToUserToken.ts b/server/src/migrations/1682371791038-AddDeviceInfoToUserToken.ts deleted file mode 100644 index bb60e452ef..0000000000 --- a/server/src/migrations/1682371791038-AddDeviceInfoToUserToken.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddDeviceInfoToUserToken1682371791038 implements MigrationInterface { - name = 'AddDeviceInfoToUserToken1682371791038' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "user_token" ADD "deviceType" character varying NOT NULL DEFAULT ''`); - await queryRunner.query(`ALTER TABLE "user_token" ADD "deviceOS" character varying NOT NULL DEFAULT ''`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "user_token" DROP COLUMN "deviceOS"`); - await queryRunner.query(`ALTER TABLE "user_token" DROP COLUMN "deviceType"`); - } - -} diff --git a/server/src/migrations/1682710252424-DropDeviceInfoTable.ts b/server/src/migrations/1682710252424-DropDeviceInfoTable.ts deleted file mode 100644 index 9a07676351..0000000000 --- a/server/src/migrations/1682710252424-DropDeviceInfoTable.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class DropDeviceInfoTable1682710252424 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`drop table device_info`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - create table if not exists device_info - ( - id serial - constraint "PK_b1c15a80b0a4e5f4eebadbdd92c" - primary key, - "userId" varchar not null, - "deviceId" varchar not null, - "deviceType" varchar not null, - "notificationToken" varchar, - "createdAt" timestamp default now() not null, - "isAutoBackup" boolean default false not null, - constraint "UQ_ebad78f36b10d15fbea8560e107" - unique ("userId", "deviceId") - ); - `); - } -} diff --git a/server/src/migrations/1683808254676-AddPartnersTable.ts b/server/src/migrations/1683808254676-AddPartnersTable.ts deleted file mode 100644 index 64afb0b76c..0000000000 --- a/server/src/migrations/1683808254676-AddPartnersTable.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddPartnersTable1683808254676 implements MigrationInterface { - name = 'AddPartnersTable1683808254676' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "partners" ("sharedById" uuid NOT NULL, "sharedWithId" uuid NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "PK_f1cc8f73d16b367f426261a8736" PRIMARY KEY ("sharedById", "sharedWithId"))`); - await queryRunner.query(`ALTER TABLE "partners" ADD CONSTRAINT "FK_7e077a8b70b3530138610ff5e04" FOREIGN KEY ("sharedById") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "partners" ADD CONSTRAINT "FK_d7e875c6c60e661723dbf372fd3" FOREIGN KEY ("sharedWithId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "partners" DROP CONSTRAINT "FK_d7e875c6c60e661723dbf372fd3"`); - await queryRunner.query(`ALTER TABLE "partners" DROP CONSTRAINT "FK_7e077a8b70b3530138610ff5e04"`); - await queryRunner.query(`DROP TABLE "partners"`); - } - -} diff --git a/server/src/migrations/1684255168091-AddFacialTables.ts b/server/src/migrations/1684255168091-AddFacialTables.ts deleted file mode 100644 index 1f2426bb0c..0000000000 --- a/server/src/migrations/1684255168091-AddFacialTables.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddFacialTables1684255168091 implements MigrationInterface { - name = 'AddFacialTables1684255168091' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "person" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "ownerId" uuid NOT NULL, "name" character varying NOT NULL DEFAULT '', "thumbnailPath" character varying NOT NULL DEFAULT '', CONSTRAINT "PK_5fdaf670315c4b7e70cce85daa3" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE TABLE "asset_faces" ("assetId" uuid NOT NULL, "personId" uuid NOT NULL, "embedding" real array, CONSTRAINT "PK_bf339a24070dac7e71304ec530a" PRIMARY KEY ("assetId", "personId"))`); - await queryRunner.query(`ALTER TABLE "person" ADD CONSTRAINT "FK_5527cc99f530a547093f9e577b6" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD CONSTRAINT "FK_02a43fd0b3c50fb6d7f0cb7282c" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD CONSTRAINT "FK_95ad7106dd7b484275443f580f9" FOREIGN KEY ("personId") REFERENCES "person"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_faces" DROP CONSTRAINT "FK_95ad7106dd7b484275443f580f9"`); - await queryRunner.query(`ALTER TABLE "asset_faces" DROP CONSTRAINT "FK_02a43fd0b3c50fb6d7f0cb7282c"`); - await queryRunner.query(`ALTER TABLE "person" DROP CONSTRAINT "FK_5527cc99f530a547093f9e577b6"`); - await queryRunner.query(`DROP TABLE "asset_faces"`); - await queryRunner.query(`DROP TABLE "person"`); - } - -} diff --git a/server/src/migrations/1684273840676-AddSidecarFile.ts b/server/src/migrations/1684273840676-AddSidecarFile.ts deleted file mode 100644 index 46c4b5d375..0000000000 --- a/server/src/migrations/1684273840676-AddSidecarFile.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddSidecarFile1684273840676 implements MigrationInterface { - name = 'AddSidecarFile1684273840676' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "sidecarPath" character varying`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "sidecarPath"`); - } - -} diff --git a/server/src/migrations/1684328185099-RequireChecksumNotNull.ts b/server/src/migrations/1684328185099-RequireChecksumNotNull.ts deleted file mode 100644 index e691fff2b1..0000000000 --- a/server/src/migrations/1684328185099-RequireChecksumNotNull.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RequireChecksumNotNull1684328185099 implements MigrationInterface { - name = 'removeNotNullFromChecksumIndex1684328185099'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_64c507300988dd1764f9a6530c"`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "checksum" SET NOT NULL`); - await queryRunner.query(`CREATE INDEX "IDX_8d3efe36c0755849395e6ea866" ON "assets" ("checksum") `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_8d3efe36c0755849395e6ea866"`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "checksum" DROP NOT NULL`); - await queryRunner.query( - `CREATE INDEX "IDX_64c507300988dd1764f9a6530c" ON "assets" ("checksum") WHERE ('checksum' IS NOT NULL)`, - ); - } -} diff --git a/server/src/migrations/1684410565398-AddStorageLabel.ts b/server/src/migrations/1684410565398-AddStorageLabel.ts deleted file mode 100644 index 6c6ea9702f..0000000000 --- a/server/src/migrations/1684410565398-AddStorageLabel.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddStorageLabel1684410565398 implements MigrationInterface { - name = 'AddStorageLabel1684410565398' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "storageLabel" character varying`); - await queryRunner.query(`ALTER TABLE "users" ADD CONSTRAINT "UQ_b309cf34fa58137c416b32cea3a" UNIQUE ("storageLabel")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP CONSTRAINT "UQ_b309cf34fa58137c416b32cea3a"`); - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "storageLabel"`); - } - -} diff --git a/server/src/migrations/1684867360825-AddUserTokenAndAPIKeyCascades.ts b/server/src/migrations/1684867360825-AddUserTokenAndAPIKeyCascades.ts deleted file mode 100644 index 273c6b830f..0000000000 --- a/server/src/migrations/1684867360825-AddUserTokenAndAPIKeyCascades.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddUserTokenAndAPIKeyCascades1684867360825 implements MigrationInterface { - name = 'AddUserTokenAndAPIKeyCascades1684867360825' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "api_keys" DROP CONSTRAINT "FK_6c2e267ae764a9413b863a29342"`); - await queryRunner.query(`ALTER TABLE "user_token" DROP CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918"`); - await queryRunner.query(`ALTER TABLE "api_keys" ADD CONSTRAINT "FK_6c2e267ae764a9413b863a29342" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "user_token" ADD CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "user_token" DROP CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918"`); - await queryRunner.query(`ALTER TABLE "api_keys" DROP CONSTRAINT "FK_6c2e267ae764a9413b863a29342"`); - await queryRunner.query(`ALTER TABLE "user_token" ADD CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "api_keys" ADD CONSTRAINT "FK_6c2e267ae764a9413b863a29342" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - -} diff --git a/server/src/migrations/1685044328272-AddSharedLinkCascade.ts b/server/src/migrations/1685044328272-AddSharedLinkCascade.ts deleted file mode 100644 index 3aefd3989e..0000000000 --- a/server/src/migrations/1685044328272-AddSharedLinkCascade.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddSharedLinkCascade1685044328272 implements MigrationInterface { - name = 'AddSharedLinkCascade1685044328272' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" DROP CONSTRAINT "FK_0c6ce9058c29f07cdf7014eac66"`); - await queryRunner.query(`ALTER TABLE "shared_links" ADD CONSTRAINT "FK_0c6ce9058c29f07cdf7014eac66" FOREIGN KEY ("albumId") REFERENCES "albums"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" DROP CONSTRAINT "FK_0c6ce9058c29f07cdf7014eac66"`); - await queryRunner.query(`ALTER TABLE "shared_links" ADD CONSTRAINT "FK_0c6ce9058c29f07cdf7014eac66" FOREIGN KEY ("albumId") REFERENCES "albums"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - -} diff --git a/server/src/migrations/1685370430343-UserDatesTimestamptz.ts b/server/src/migrations/1685370430343-UserDatesTimestamptz.ts deleted file mode 100644 index 0a70e012d8..0000000000 --- a/server/src/migrations/1685370430343-UserDatesTimestamptz.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class UserDatesTimestamptz1685370430343 implements MigrationInterface { - name = 'UserDatesTimestamptz1685370430343' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "deletedAt" TYPE TIMESTAMP WITH TIME ZONE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "deletedAt" TYPE TIMESTAMP`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "createdAt" TYPE TIMESTAMP`); - } - -} diff --git a/server/src/migrations/1685731372040-RemoveInvalidCoordinates.ts b/server/src/migrations/1685731372040-RemoveInvalidCoordinates.ts deleted file mode 100644 index 9a9b00a366..0000000000 --- a/server/src/migrations/1685731372040-RemoveInvalidCoordinates.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveInvalidCoordinates1685731372040 implements MigrationInterface { - name = 'RemoveInvalidCoordinates1685731372040'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`UPDATE "exif" SET "latitude" = NULL WHERE "latitude" IN ('NaN', 'Infinity', '-Infinity')`); - await queryRunner.query( - `UPDATE "exif" SET "longitude" = NULL WHERE "longitude" IN ('NaN', 'Infinity', '-Infinity')`, - ); - } - - public async down(): Promise { - // Empty, data cannot be restored - } -} diff --git a/server/src/migrations/1686584273471-ImportAsset.ts b/server/src/migrations/1686584273471-ImportAsset.ts deleted file mode 100644 index d9f5819a8d..0000000000 --- a/server/src/migrations/1686584273471-ImportAsset.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class ImportAsset1686584273471 implements MigrationInterface { - name = 'ImportAsset1686584273471' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "isReadOnly" boolean NOT NULL DEFAULT false`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "UQ_4ed4f8052685ff5b1e7ca1058ba" UNIQUE ("originalPath")`); - await queryRunner.query(`ALTER TABLE "users" ADD "externalPath" character varying`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "UQ_4ed4f8052685ff5b1e7ca1058ba"`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "isReadOnly"`); - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "externalPath"`); - } - -} diff --git a/server/src/migrations/1686762895180-AddThumbhashColumn.ts b/server/src/migrations/1686762895180-AddThumbhashColumn.ts deleted file mode 100644 index 4ad73163db..0000000000 --- a/server/src/migrations/1686762895180-AddThumbhashColumn.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddThumbhashColumn1685546571785 implements MigrationInterface { - name = 'AddThumbhashColumn1686762895180'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "thumbhash" bytea NULL`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "thumbhash"`); - } -} diff --git a/server/src/migrations/1688241394489-AddDetectFaceResultInfo.ts b/server/src/migrations/1688241394489-AddDetectFaceResultInfo.ts deleted file mode 100644 index b026685bfb..0000000000 --- a/server/src/migrations/1688241394489-AddDetectFaceResultInfo.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddDetectFaceResultInfo1688241394489 implements MigrationInterface { - name = 'AddDetectFaceResultInfo1688241394489'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_faces" ADD "imageWidth" integer NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD "imageHeight" integer NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD "boundingBoxX1" integer NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD "boundingBoxY1" integer NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD "boundingBoxX2" integer NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD "boundingBoxY2" integer NOT NULL DEFAULT '0'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_faces" DROP COLUMN "boundingBoxY2"`); - await queryRunner.query(`ALTER TABLE "asset_faces" DROP COLUMN "boundingBoxX2"`); - await queryRunner.query(`ALTER TABLE "asset_faces" DROP COLUMN "boundingBoxY1"`); - await queryRunner.query(`ALTER TABLE "asset_faces" DROP COLUMN "boundingBoxX1"`); - await queryRunner.query(`ALTER TABLE "asset_faces" DROP COLUMN "imageHeight"`); - await queryRunner.query(`ALTER TABLE "asset_faces" DROP COLUMN "imageWidth"`); - } -} diff --git a/server/src/migrations/1688392120838-AddLibraryTable.ts b/server/src/migrations/1688392120838-AddLibraryTable.ts deleted file mode 100644 index 4d394adaf1..0000000000 --- a/server/src/migrations/1688392120838-AddLibraryTable.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddLibraries1688392120838 implements MigrationInterface { - name = 'AddLibraryTable1688392120838'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "UQ_userid_checksum"`); - await queryRunner.query( - `CREATE TABLE "libraries" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying NOT NULL, "ownerId" uuid NOT NULL, "type" character varying NOT NULL, "importPaths" text array NOT NULL, "exclusionPatterns" text array NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP WITH TIME ZONE, "refreshedAt" TIMESTAMP WITH TIME ZONE, "isVisible" boolean NOT NULL DEFAULT true, CONSTRAINT "PK_505fedfcad00a09b3734b4223de" PRIMARY KEY ("id"))`, - ); - await queryRunner.query(`ALTER TABLE "assets" ADD "isOffline" boolean NOT NULL DEFAULT false`); - await queryRunner.query(`ALTER TABLE "assets" ADD "libraryId" uuid`); - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "UQ_4ed4f8052685ff5b1e7ca1058ba"`); - await queryRunner.query(`ALTER TABLE "assets" ADD "isExternal" boolean NOT NULL DEFAULT false`); - - await queryRunner.query( - `CREATE UNIQUE INDEX "UQ_assets_owner_library_checksum" on "assets" ("ownerId", "libraryId", checksum)`, - ); - await queryRunner.query( - `ALTER TABLE "libraries" ADD CONSTRAINT "FK_0f6fc2fb195f24d19b0fb0d57c1" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`, - ); - await queryRunner.query( - `ALTER TABLE "assets" ADD CONSTRAINT "FK_9977c3c1de01c3d848039a6b90c" FOREIGN KEY ("libraryId") REFERENCES "libraries"("id") ON DELETE CASCADE ON UPDATE CASCADE`, - ); - - // Create default library for each user and assign all assets to it - const users = await queryRunner.query(`SELECT id FROM "users"`); - const userIds: string[] = users.map((user: any) => user.id); - - for (const userId of userIds) { - await queryRunner.query( - `INSERT INTO "libraries" ("name", "ownerId", "type", "importPaths", "exclusionPatterns") VALUES ('Default Library', '${userId}', 'UPLOAD', '{}', '{}')`, - ); - - await queryRunner.query( - `UPDATE "assets" SET "libraryId" = (SELECT id FROM "libraries" WHERE "ownerId" = '${userId}' LIMIT 1) WHERE "ownerId" = '${userId}'`, - ); - } - - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "libraryId" SET NOT NULL`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "libraryId" DROP NOT NULL`); - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_9977c3c1de01c3d848039a6b90c"`); - await queryRunner.query(`ALTER TABLE "libraries" DROP CONSTRAINT "FK_0f6fc2fb195f24d19b0fb0d57c1"`); - await queryRunner.query(`DROP INDEX "UQ_assets_owner_library_checksum"`); - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "UQ_owner_library_originalpath"`); - await queryRunner.query( - `ALTER TABLE "assets" ADD CONSTRAINT "UQ_4ed4f8052685ff5b1e7ca1058ba" UNIQUE ("originalPath")`, - ); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "libraryId"`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "isOffline"`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "isExternal"`); - await queryRunner.query(`DROP TABLE "libraries"`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "UQ_userid_checksum" UNIQUE ("ownerId", "checksum")`); - } -} diff --git a/server/src/migrations/1689001889950-DropMimeTypeColumn.ts b/server/src/migrations/1689001889950-DropMimeTypeColumn.ts deleted file mode 100644 index 45559313a1..0000000000 --- a/server/src/migrations/1689001889950-DropMimeTypeColumn.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class DropMimeTypeColumn1689001889950 implements MigrationInterface { - name = 'DropMimeTypeColumn1689001889950' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "mimeType"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "mimeType" character varying`); - } - -} diff --git a/server/src/migrations/1689281196844-AddHiddenFaces.ts b/server/src/migrations/1689281196844-AddHiddenFaces.ts deleted file mode 100644 index 234b77dd34..0000000000 --- a/server/src/migrations/1689281196844-AddHiddenFaces.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class Infra1689281196844 implements MigrationInterface { - name = 'Infra1689281196844' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" ADD "isHidden" boolean NOT NULL DEFAULT false`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" DROP COLUMN "isHidden"`); - } - -} diff --git a/server/src/migrations/1690469489288-Panoramas.ts b/server/src/migrations/1690469489288-Panoramas.ts deleted file mode 100644 index ee0934b43a..0000000000 --- a/server/src/migrations/1690469489288-Panoramas.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class Panoramas1690217088596 implements MigrationInterface { - name = 'Panoramas1690217088596'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "projectionType" character varying`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "projectionType"`); - } -} diff --git a/server/src/migrations/1691209138541-AddAlbumDescription.ts b/server/src/migrations/1691209138541-AddAlbumDescription.ts deleted file mode 100644 index f4167598af..0000000000 --- a/server/src/migrations/1691209138541-AddAlbumDescription.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddAlbumDescription1691209138541 implements MigrationInterface { - name = 'AddAlbumDescription1691209138541'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" ADD "description" text NOT NULL DEFAULT ''`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" DROP COLUMN "description"`); - } -} diff --git a/server/src/migrations/1691600216749-UserMemoryPreference.ts b/server/src/migrations/1691600216749-UserMemoryPreference.ts deleted file mode 100644 index 7238749080..0000000000 --- a/server/src/migrations/1691600216749-UserMemoryPreference.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UserMemoryPreference1691600216749 implements MigrationInterface { - name = 'UserMemoryPreference1691600216749'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "memoriesEnabled" boolean NOT NULL DEFAULT true`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "memoriesEnabled"`); - } -} diff --git a/server/src/migrations/1692057328660-fixGPSNullIsland.ts b/server/src/migrations/1692057328660-fixGPSNullIsland.ts deleted file mode 100644 index 74dc40a474..0000000000 --- a/server/src/migrations/1692057328660-fixGPSNullIsland.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm" - -export class FixGPSNullIsland1692057328660 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`UPDATE "exif" SET latitude = NULL, longitude = NULL WHERE latitude = 0 AND longitude = 0;`); - } - - public async down(): Promise { - // Setting lat,lon to 0 not necessary - } - -} diff --git a/server/src/migrations/1692112147855-AddPersonBirthDate.ts b/server/src/migrations/1692112147855-AddPersonBirthDate.ts deleted file mode 100644 index db2ba35dad..0000000000 --- a/server/src/migrations/1692112147855-AddPersonBirthDate.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm" - -export class AddPersonBirthDate1692112147855 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" ADD "birthDate" date`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" DROP COLUMN "birthDate"`); - } - -} diff --git a/server/src/migrations/1692804658140-AddAuditTable.ts b/server/src/migrations/1692804658140-AddAuditTable.ts deleted file mode 100644 index d398051a79..0000000000 --- a/server/src/migrations/1692804658140-AddAuditTable.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAuditTable1692804658140 implements MigrationInterface { - name = 'AddAuditTable1692804658140' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "audit" ("id" SERIAL NOT NULL, "entityType" character varying NOT NULL, "entityId" uuid NOT NULL, "action" character varying NOT NULL, "ownerId" uuid NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "PK_1d3d120ddaf7bc9b1ed68ed463a" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_ownerId_createdAt" ON "audit" ("ownerId", "createdAt") `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_ownerId_createdAt"`); - await queryRunner.query(`DROP TABLE "audit"`); - } - -} diff --git a/server/src/migrations/1693236627291-RenameMLEnableFlags.ts b/server/src/migrations/1693236627291-RenameMLEnableFlags.ts deleted file mode 100644 index 096356f6e0..0000000000 --- a/server/src/migrations/1693236627291-RenameMLEnableFlags.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm" - -export class RenameMLEnableFlags1693236627291 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - UPDATE system_config SET key = CASE - WHEN key = 'ffmpeg.classificationEnabled' THEN 'ffmpeg.classification.enabled' - WHEN key = 'ffmpeg.clipEnabled' THEN 'ffmpeg.clip.enabled' - WHEN key = 'ffmpeg.facialRecognitionEnabled' THEN 'ffmpeg.facialRecognition.enabled' - ELSE key - END - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - UPDATE system_config SET key = CASE - WHEN key = 'ffmpeg.classification.enabled' THEN 'ffmpeg.classificationEnabled' - WHEN key = 'ffmpeg.clip.enabled' THEN 'ffmpeg.clipEnabled' - WHEN key = 'ffmpeg.facialRecognition.enabled' THEN 'ffmpeg.facialRecognitionEnabled' - ELSE key - END - `); - } -} diff --git a/server/src/migrations/1693833336881-AddPersonFaceAssetId.ts b/server/src/migrations/1693833336881-AddPersonFaceAssetId.ts deleted file mode 100644 index 2e3c914eec..0000000000 --- a/server/src/migrations/1693833336881-AddPersonFaceAssetId.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm" - -export class AddPersonFaceAssetId1693833336881 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" ADD "faceAssetId" uuid`); - await queryRunner.query(`ALTER TABLE "person" ADD CONSTRAINT "FK_2bbabe31656b6778c6b87b61023" FOREIGN KEY ("faceAssetId") REFERENCES "assets"("id") ON DELETE SET NULL ON UPDATE NO ACTION`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" DROP CONSTRAINT "FK_2bbabe31656b6778c6b87b61023"`); - await queryRunner.query(`ALTER TABLE "person" DROP COLUMN "faceAssetId"`); - } - -} diff --git a/server/src/migrations/1694204416744-AddAssetDeletedAtColumn.ts b/server/src/migrations/1694204416744-AddAssetDeletedAtColumn.ts deleted file mode 100644 index 3b213b9f04..0000000000 --- a/server/src/migrations/1694204416744-AddAssetDeletedAtColumn.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAssetDeletedAtColumn1694204416744 implements MigrationInterface { - name = 'AddAssetDeletedAtColumn1694204416744' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "deletedAt" TIMESTAMP WITH TIME ZONE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "deletedAt"`); - } - -} diff --git a/server/src/migrations/1694525143117-AddLocalDateTime.ts b/server/src/migrations/1694525143117-AddLocalDateTime.ts deleted file mode 100644 index dd24c07c0b..0000000000 --- a/server/src/migrations/1694525143117-AddLocalDateTime.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddLocalDateTime1694525143117 implements MigrationInterface { - name = 'AddLocalDateTime1694525143117'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "localDateTime" TIMESTAMP WITH TIME ZONE`); - await queryRunner.query(`UPDATE "assets" SET "localDateTime" = "fileCreatedAt"`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "localDateTime" SET NOT NULL`); - await queryRunner.query( - `CREATE INDEX "IDX_day_of_month" ON assets (EXTRACT(DAY FROM "localDateTime" AT TIME ZONE 'UTC'))`, - ); - await queryRunner.query( - `CREATE INDEX "IDX_month" ON assets (EXTRACT(MONTH FROM "localDateTime" AT TIME ZONE 'UTC'))`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "localDateTime"`); - await queryRunner.query(`DROP INDEX "IDX_day_of_month"`); - await queryRunner.query(`DROP INDEX "IDX_month"`); - } -} diff --git a/server/src/migrations/1694638413248-AddDeletedAtToAlbums.ts b/server/src/migrations/1694638413248-AddDeletedAtToAlbums.ts deleted file mode 100644 index 64a34c3e88..0000000000 --- a/server/src/migrations/1694638413248-AddDeletedAtToAlbums.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddDeletedAtToAlbums1694638413248 implements MigrationInterface { - name = 'AddDeletedAtToAlbums1694638413248'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" ADD "deletedAt" TIMESTAMP WITH TIME ZONE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" DROP COLUMN "deletedAt"`); - } -} diff --git a/server/src/migrations/1694750975773-AddExifColorSpace.ts b/server/src/migrations/1694750975773-AddExifColorSpace.ts deleted file mode 100644 index d6b3a6b2b0..0000000000 --- a/server/src/migrations/1694750975773-AddExifColorSpace.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddExifColorSpace1694750975773 implements MigrationInterface { - name = 'AddExifColorSpace1694750975773' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "profileDescription" character varying`); - await queryRunner.query(`ALTER TABLE "exif" ADD "colorspace" character varying`); - await queryRunner.query(`ALTER TABLE "exif" ADD "bitsPerSample" integer`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "bitsPerSample"`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "colorspace"`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "profileDescription"`); - } - -} diff --git a/server/src/migrations/1694758412194-UpdateOpusCodecToLibopus.ts b/server/src/migrations/1694758412194-UpdateOpusCodecToLibopus.ts deleted file mode 100644 index 715b46550a..0000000000 --- a/server/src/migrations/1694758412194-UpdateOpusCodecToLibopus.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class UpdateOpusCodecToLibopus1694758412194 implements MigrationInterface { - name = 'UpdateOpusCodecToLibopus1694758412194' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - UPDATE system_config - SET value = '"libopus"' - WHERE key = 'ffmpeg.targetAudioCodec' AND value = '"opus"' - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - UPDATE system_config - SET value = '"opus"' - WHERE key = 'ffmpeg.targetAudioCodec' AND value = '"libopus"' - `); - } -} diff --git a/server/src/migrations/1695354433573-AddStackParentIdToAssets.ts b/server/src/migrations/1695354433573-AddStackParentIdToAssets.ts deleted file mode 100644 index d5150d3a81..0000000000 --- a/server/src/migrations/1695354433573-AddStackParentIdToAssets.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddStackParentIdToAssets1695354433573 implements MigrationInterface { - name = 'AddStackParentIdToAssets1695354433573' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "stackParentId" uuid`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "FK_b463c8edb01364bf2beba08ef19" FOREIGN KEY ("stackParentId") REFERENCES "assets"("id") ON DELETE SET NULL ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_b463c8edb01364bf2beba08ef19"`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "stackParentId"`); - } - -} diff --git a/server/src/migrations/1695660378655-RemoveInvalidCoordinates.ts b/server/src/migrations/1695660378655-RemoveInvalidCoordinates.ts deleted file mode 100644 index 20b179ceb6..0000000000 --- a/server/src/migrations/1695660378655-RemoveInvalidCoordinates.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveInvalidCoordinates1695660378655 implements MigrationInterface { - name = 'RemoveInvalidCoordinates1695660378655'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`UPDATE "exif" SET "latitude" = NULL WHERE "latitude" IN ('NaN', 'Infinity', '-Infinity')`); - await queryRunner.query( - `UPDATE "exif" SET "longitude" = NULL WHERE "longitude" IN ('NaN', 'Infinity', '-Infinity')`, - ); - } - - public async down(): Promise { - // Empty, data cannot be restored - } -} diff --git a/server/src/migrations/1696888644031-AddOriginalPathIndex.ts b/server/src/migrations/1696888644031-AddOriginalPathIndex.ts deleted file mode 100644 index 78e1c92ecb..0000000000 --- a/server/src/migrations/1696888644031-AddOriginalPathIndex.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddOriginalPathIndex1696888644031 implements MigrationInterface { - name = 'AddOriginalPathIndex1696888644031'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX "IDX_originalPath_libraryId" ON "assets" ("originalPath", "libraryId")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_originalPath_libraryId"`); - } -} diff --git a/server/src/migrations/1696968880063-AddMoveTable.ts b/server/src/migrations/1696968880063-AddMoveTable.ts deleted file mode 100644 index 7ba140d05b..0000000000 --- a/server/src/migrations/1696968880063-AddMoveTable.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddMoveTable1696968880063 implements MigrationInterface { - name = 'AddMoveTable1696968880063' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "move_history" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "entityId" character varying NOT NULL, "pathType" character varying NOT NULL, "oldPath" character varying NOT NULL, "newPath" character varying NOT NULL, CONSTRAINT "UQ_newPath" UNIQUE ("newPath"), CONSTRAINT "UQ_entityId_pathType" UNIQUE ("entityId", "pathType"), CONSTRAINT "PK_af608f132233acf123f2949678d" PRIMARY KEY ("id"))`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE "move_history"`); - } - -} diff --git a/server/src/migrations/1697272818851-UnassignFace.ts b/server/src/migrations/1697272818851-UnassignFace.ts deleted file mode 100644 index 49eebf4cc1..0000000000 --- a/server/src/migrations/1697272818851-UnassignFace.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UnassignFace1697272818851 implements MigrationInterface { - name = 'UnassignFace1697272818851'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_faces" DROP CONSTRAINT "PK_bf339a24070dac7e71304ec530a"`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD COLUMN "id" UUID DEFAULT uuid_generate_v4() NOT NULL`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD CONSTRAINT "PK_6df76ab2eb6f5b57b7c2f1fc684" PRIMARY KEY ("id")`); - await queryRunner.query(`ALTER TABLE "asset_faces" DROP CONSTRAINT "FK_95ad7106dd7b484275443f580f9"`); - await queryRunner.query(`ALTER TABLE "asset_faces" ALTER COLUMN "personId" DROP NOT NULL`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD CONSTRAINT "FK_95ad7106dd7b484275443f580f9" FOREIGN KEY ("personId") REFERENCES "person"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_faces" DROP CONSTRAINT "PK_6df76ab2eb6f5b57b7c2f1fc684"`); - await queryRunner.query(`ALTER TABLE "asset_faces" DROP COLUMN "id"`); - await queryRunner.query(`ALTER TABLE "asset_faces" DROP CONSTRAINT "FK_95ad7106dd7b484275443f580f9"`); - await queryRunner.query(`ALTER TABLE "asset_faces" ALTER COLUMN "personId" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD CONSTRAINT "FK_95ad7106dd7b484275443f580f9" FOREIGN KEY ("personId") REFERENCES "person"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD CONSTRAINT "PK_bf339a24070dac7e71304ec530a" PRIMARY KEY ("assetId", "personId")`); - } -} diff --git a/server/src/migrations/1698290827089-AddPasswordToSharedLinks.ts b/server/src/migrations/1698290827089-AddPasswordToSharedLinks.ts deleted file mode 100644 index b6906e3d05..0000000000 --- a/server/src/migrations/1698290827089-AddPasswordToSharedLinks.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddPasswordToSharedLinks1698290827089 implements MigrationInterface { - name = 'AddPasswordToSharedLinks1698290827089' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" ADD "password" character varying`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" DROP COLUMN "password"`); - } - -} diff --git a/server/src/migrations/1698693294632-AddActivity.ts b/server/src/migrations/1698693294632-AddActivity.ts deleted file mode 100644 index 5556ef2b20..0000000000 --- a/server/src/migrations/1698693294632-AddActivity.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddActivity1698693294632 implements MigrationInterface { - name = 'AddActivity1698693294632' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "activity" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "albumId" uuid NOT NULL, "userId" uuid NOT NULL, "assetId" uuid, "comment" text, "isLiked" boolean NOT NULL DEFAULT false, CONSTRAINT "CHK_2ab1e70f113f450eb40c1e3ec8" CHECK (("comment" IS NULL AND "isLiked" = true) OR ("comment" IS NOT NULL AND "isLiked" = false)), CONSTRAINT "PK_24625a1d6b1b089c8ae206fe467" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_activity_like" ON "activity" ("assetId", "userId", "albumId") WHERE ("isLiked" = true)`); - await queryRunner.query(`ALTER TABLE "activity" ADD CONSTRAINT "FK_8091ea76b12338cb4428d33d782" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "activity" ADD CONSTRAINT "FK_3571467bcbe021f66e2bdce96ea" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "activity" ADD CONSTRAINT "FK_1af8519996fbfb3684b58df280b" FOREIGN KEY ("albumId") REFERENCES "albums"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "activity" DROP CONSTRAINT "FK_1af8519996fbfb3684b58df280b"`); - await queryRunner.query(`ALTER TABLE "activity" DROP CONSTRAINT "FK_3571467bcbe021f66e2bdce96ea"`); - await queryRunner.query(`ALTER TABLE "activity" DROP CONSTRAINT "FK_8091ea76b12338cb4428d33d782"`); - await queryRunner.query(`DROP INDEX "IDX_activity_like"`); - await queryRunner.query(`DROP TABLE "activity"`); - } - -} diff --git a/server/src/migrations/1699268680508-DisableActivity.ts b/server/src/migrations/1699268680508-DisableActivity.ts deleted file mode 100644 index d860244f6d..0000000000 --- a/server/src/migrations/1699268680508-DisableActivity.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class DisableActivity1699268680508 implements MigrationInterface { - name = 'DisableActivity1699268680508' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" ADD "isActivityEnabled" boolean NOT NULL DEFAULT true`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" DROP COLUMN "isActivityEnabled"`); - } - -} diff --git a/server/src/migrations/1699322864544-UserNameConsolidation.ts b/server/src/migrations/1699322864544-UserNameConsolidation.ts deleted file mode 100644 index 431a2a92fc..0000000000 --- a/server/src/migrations/1699322864544-UserNameConsolidation.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddUsername1699322864544 implements MigrationInterface { - name = 'AddUsername1699322864544' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "name" character varying NOT NULL DEFAULT ''`); - await queryRunner.query(`UPDATE "users" SET "name" = CONCAT(COALESCE("firstName", ''), ' ', COALESCE("lastName", ''))`); - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "firstName"`); - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "lastName"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "name"`); - await queryRunner.query(`ALTER TABLE "users" ADD "lastName" character varying NOT NULL DEFAULT ''`); - await queryRunner.query(`ALTER TABLE "users" ADD "firstName" character varying NOT NULL DEFAULT ''`); - await queryRunner.query(`UPDATE "users" SET "lastName" = COALESCE("email", '')`); - await queryRunner.query(`UPDATE "users" SET "firstName" = COALESCE("email", '')`); - } - -} diff --git a/server/src/migrations/1699345863886-AddJobStatus.ts b/server/src/migrations/1699345863886-AddJobStatus.ts deleted file mode 100644 index c7df6387c0..0000000000 --- a/server/src/migrations/1699345863886-AddJobStatus.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddJobStatus1699345863886 implements MigrationInterface { - name = 'AddJobStatus1699345863886' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "asset_job_status" ("assetId" uuid NOT NULL, "facesRecognizedAt" TIMESTAMP WITH TIME ZONE, CONSTRAINT "PK_420bec36fc02813bddf5c8b73d4" PRIMARY KEY ("assetId"))`); - await queryRunner.query(`ALTER TABLE "asset_job_status" ADD CONSTRAINT "FK_420bec36fc02813bddf5c8b73d4" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_job_status" DROP CONSTRAINT "FK_420bec36fc02813bddf5c8b73d4"`); - await queryRunner.query(`DROP TABLE "asset_job_status"`); - } - -} diff --git a/server/src/migrations/1699562570201-AdddInTimelineToPartnersTable.ts b/server/src/migrations/1699562570201-AdddInTimelineToPartnersTable.ts deleted file mode 100644 index 59c2f229eb..0000000000 --- a/server/src/migrations/1699562570201-AdddInTimelineToPartnersTable.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AdddInTimelineToPartnersTable1699562570201 implements MigrationInterface { - name = 'AdddInTimelineToPartnersTable1699562570201' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "partners" ADD "inTimeline" boolean NOT NULL DEFAULT false`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "partners" DROP COLUMN "inTimeline"`); - } - -} diff --git a/server/src/migrations/1699727044012-EditFaceAssetForeignKey.ts b/server/src/migrations/1699727044012-EditFaceAssetForeignKey.ts deleted file mode 100644 index fdff7ea062..0000000000 --- a/server/src/migrations/1699727044012-EditFaceAssetForeignKey.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class EditFaceAssetForeignKey1699727044012 implements MigrationInterface { - name = 'EditFaceAssetForeignKey1699727044012' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" DROP CONSTRAINT "FK_2bbabe31656b6778c6b87b61023"`); - await queryRunner.query(`UPDATE person SET "faceAssetId" = asset_faces."id" FROM asset_faces WHERE person."faceAssetId" = asset_faces."assetId" AND person."id" = asset_faces."personId"`) - await queryRunner.query(`ALTER TABLE "person" ADD CONSTRAINT "FK_2bbabe31656b6778c6b87b61023" FOREIGN KEY ("faceAssetId") REFERENCES "asset_faces"("id") ON DELETE SET NULL ON UPDATE NO ACTION`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" DROP CONSTRAINT "FK_2bbabe31656b6778c6b87b61023"`); - await queryRunner.query(`UPDATE person SET "faceAssetId" = assets."id" FROM assets, asset_faces WHERE person."faceAssetId" = asset_faces."id" AND asset_faces."assetId" = assets."id"`); - await queryRunner.query(`ALTER TABLE "person" ADD CONSTRAINT "FK_2bbabe31656b6778c6b87b61023" FOREIGN KEY ("faceAssetId") REFERENCES "assets"("id") ON DELETE SET NULL ON UPDATE NO ACTION`); - } - -} diff --git a/server/src/migrations/1699889987493-AddAvatarColor.ts b/server/src/migrations/1699889987493-AddAvatarColor.ts deleted file mode 100644 index b075a5d2af..0000000000 --- a/server/src/migrations/1699889987493-AddAvatarColor.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAvatarColor1699889987493 implements MigrationInterface { - name = 'AddAvatarColor1699889987493' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "avatarColor" character varying`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "avatarColor"`); - } - -} diff --git a/server/src/migrations/1700345818045-SystemMetadata.ts b/server/src/migrations/1700345818045-SystemMetadata.ts deleted file mode 100644 index 0bd9162db7..0000000000 --- a/server/src/migrations/1700345818045-SystemMetadata.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class SystemMetadata1700345818045 implements MigrationInterface { - name = 'SystemMetadata1700345818045' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "system_metadata" ("key" character varying NOT NULL, "value" jsonb NOT NULL DEFAULT '{}', CONSTRAINT "PK_fa94f6857470fb5b81ec6084465" PRIMARY KEY ("key"))`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE "system_metadata"`); - } - -} diff --git a/server/src/migrations/1700362016675-Geodata.ts b/server/src/migrations/1700362016675-Geodata.ts deleted file mode 100644 index fa948e0896..0000000000 --- a/server/src/migrations/1700362016675-Geodata.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class Geodata1700362016675 implements MigrationInterface { - name = 'Geodata1700362016675' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS cube`) - await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS earthdistance`) - await queryRunner.query(`CREATE TABLE "geodata_admin2" ("key" character varying NOT NULL, "name" character varying NOT NULL, CONSTRAINT "PK_1e3886455dbb684d6f6b4756726" PRIMARY KEY ("key"))`); - await queryRunner.query(`CREATE TABLE "geodata_admin1" ("key" character varying NOT NULL, "name" character varying NOT NULL, CONSTRAINT "PK_3fe3a89c5aac789d365871cb172" PRIMARY KEY ("key"))`); - await queryRunner.query(`CREATE TABLE "geodata_places" ("id" integer NOT NULL, "name" character varying(200) NOT NULL, "longitude" double precision NOT NULL, "latitude" double precision NOT NULL, "countryCode" character(2) NOT NULL, "admin1Code" character varying(20), "admin2Code" character varying(80), "admin1Key" character varying GENERATED ALWAYS AS ("countryCode" || '.' || "admin1Code") STORED, "admin2Key" character varying GENERATED ALWAYS AS ("countryCode" || '.' || "admin1Code" || '.' || "admin2Code") STORED, "modificationDate" date NOT NULL, CONSTRAINT "PK_c29918988912ef4036f3d7fbff4" PRIMARY KEY ("id"))`); - await queryRunner.query(`ALTER TABLE "geodata_places" ADD "earthCoord" earth GENERATED ALWAYS AS (ll_to_earth(latitude, longitude)) STORED`) - await queryRunner.query(`CREATE INDEX "IDX_geodata_gist_earthcoord" ON "geodata_places" USING gist ("earthCoord");`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_geodata_gist_earthcoord"`); - await queryRunner.query(`DROP TABLE "geodata_places"`); - await queryRunner.query(`DROP TABLE "geodata_admin1"`); - await queryRunner.query(`DROP TABLE "geodata_admin2"`); - await queryRunner.query(`DROP EXTENSION cube`); - await queryRunner.query(`DROP EXTENSION earthdistance`); - } - -} diff --git a/server/src/migrations/1700713871511-UsePgVectors.ts b/server/src/migrations/1700713871511-UsePgVectors.ts deleted file mode 100644 index 4511e1001b..0000000000 --- a/server/src/migrations/1700713871511-UsePgVectors.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { getVectorExtension } from 'src/repositories/database.repository'; -import { getCLIPModelInfo } from 'src/utils/misc'; -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UsePgVectors1700713871511 implements MigrationInterface { - name = 'UsePgVectors1700713871511'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`SET search_path TO "$user", public, vectors`); - await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS ${await getVectorExtension(queryRunner)}`); - const faceDimQuery = await queryRunner.query(` - SELECT CARDINALITY(embedding::real[]) as dimsize - FROM asset_faces - LIMIT 1`); - const faceDimSize = faceDimQuery?.[0]?.['dimsize'] ?? 512; - - const clipModelNameQuery = await queryRunner.query( - `SELECT value FROM system_config WHERE key = 'machineLearning.clip.modelName'`, - ); - const clipModelName: string = clipModelNameQuery?.[0]?.['value'] ?? 'ViT-B-32__openai'; - const clipDimSize = getCLIPModelInfo(clipModelName.replaceAll('"', '')).dimSize; - - await queryRunner.query(` - ALTER TABLE asset_faces - ALTER COLUMN embedding SET NOT NULL, - ALTER COLUMN embedding TYPE vector(${faceDimSize})`); - - await queryRunner.query(` - CREATE TABLE smart_search ( - "assetId" uuid PRIMARY KEY NOT NULL REFERENCES assets(id) ON DELETE CASCADE, - embedding vector(${clipDimSize}) NOT NULL )`); - - await queryRunner.query(` - INSERT INTO smart_search("assetId", embedding) - SELECT si."assetId", si."clipEmbedding" - FROM smart_info si - WHERE "clipEmbedding" IS NOT NULL - AND CARDINALITY("clipEmbedding"::real[]) = ${clipDimSize} - AND array_position(si."clipEmbedding", NULL) IS NULL`); - - await queryRunner.query(`ALTER TABLE smart_info DROP COLUMN IF EXISTS "clipEmbedding"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE asset_faces ALTER COLUMN embedding TYPE real array`); - await queryRunner.query(`ALTER TABLE smart_info ADD COLUMN IF NOT EXISTS "clipEmbedding" TYPE real array`); - await queryRunner.query(` - INSERT INTO smart_info - ("assetId", "clipEmbedding") - SELECT s."assetId", s.embedding - FROM smart_search s - ON CONFLICT (s."assetId") DO UPDATE SET "clipEmbedding" = s.embedding`); - await queryRunner.query(`DROP TABLE IF EXISTS smart_search`); - } -} diff --git a/server/src/migrations/1700713994428-AddCLIPEmbeddingIndex.ts b/server/src/migrations/1700713994428-AddCLIPEmbeddingIndex.ts deleted file mode 100644 index 43809d6364..0000000000 --- a/server/src/migrations/1700713994428-AddCLIPEmbeddingIndex.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { getVectorExtension } from 'src/repositories/database.repository'; -import { vectorIndexQuery } from 'src/utils/database'; -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddCLIPEmbeddingIndex1700713994428 implements MigrationInterface { - name = 'AddCLIPEmbeddingIndex1700713994428'; - - public async up(queryRunner: QueryRunner): Promise { - const vectorExtension = await getVectorExtension(queryRunner); - await queryRunner.query(`SET search_path TO "$user", public, vectors`); - - await queryRunner.query(vectorIndexQuery({ vectorExtension, table: 'smart_search', indexName: 'clip_index' })); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX IF EXISTS clip_index`); - } -} diff --git a/server/src/migrations/1700714033632-AddFaceEmbeddingIndex.ts b/server/src/migrations/1700714033632-AddFaceEmbeddingIndex.ts deleted file mode 100644 index 5ee91afbcc..0000000000 --- a/server/src/migrations/1700714033632-AddFaceEmbeddingIndex.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { getVectorExtension } from 'src/repositories/database.repository'; -import { vectorIndexQuery } from 'src/utils/database'; -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddFaceEmbeddingIndex1700714033632 implements MigrationInterface { - name = 'AddFaceEmbeddingIndex1700714033632'; - - public async up(queryRunner: QueryRunner): Promise { - const vectorExtension = await getVectorExtension(queryRunner); - await queryRunner.query(`SET search_path TO "$user", public, vectors`); - - await queryRunner.query(vectorIndexQuery({ vectorExtension, table: 'asset_faces', indexName: 'face_index' })); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX IF EXISTS face_index`); - } -} diff --git a/server/src/migrations/1700714072055-AddSmartInfoTagsIndex.ts b/server/src/migrations/1700714072055-AddSmartInfoTagsIndex.ts deleted file mode 100644 index b850d3da09..0000000000 --- a/server/src/migrations/1700714072055-AddSmartInfoTagsIndex.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddSmartInfoTagsIndex1700714072055 implements MigrationInterface { - name = 'AddSmartInfoTagsIndex1700714072055'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX IF NOT EXISTS si_tags ON smart_info USING GIN (tags);`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX IF EXISTS si_tags;`); - } -} diff --git a/server/src/migrations/1700714140297-CreateSmartInfoTextSearchIndex.ts b/server/src/migrations/1700714140297-CreateSmartInfoTextSearchIndex.ts deleted file mode 100644 index b42291f6fd..0000000000 --- a/server/src/migrations/1700714140297-CreateSmartInfoTextSearchIndex.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateSmartInfoTextSearchIndex1700714140297 implements MigrationInterface { - name = 'CreateSmartInfoTextSearchIndex1700714140297'; - - public async up(queryRunner: QueryRunner): Promise { - // https://dba.stackexchange.com/a/164081 - await queryRunner.query(` - CREATE OR REPLACE FUNCTION f_concat_ws(text, text[]) - RETURNS text - LANGUAGE sql IMMUTABLE PARALLEL SAFE AS - 'SELECT array_to_string($2, $1)'`); - - await queryRunner.query(` - ALTER TABLE smart_info ADD "smartInfoTextSearchableColumn" tsvector - GENERATED ALWAYS AS ( - TO_TSVECTOR( - 'english', - f_concat_ws( - ' '::text, - COALESCE(tags, array[]::text[]) || COALESCE(objects, array[]::text[]) - ) - ) - ) - STORED NOT NULL`); - - await queryRunner.query(` - CREATE INDEX smart_info_text_searchable_idx - ON smart_info - USING GIN ("smartInfoTextSearchableColumn")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP FUNCTION IF EXISTS immutable_concat_ws`); - await queryRunner.query(`ALTER TABLE smart_info DROP IF EXISTS "smartInfoTextSearchableColumn"`); - } -} diff --git a/server/src/migrations/1700752078178-AddAssetFaceIndicies.ts b/server/src/migrations/1700752078178-AddAssetFaceIndicies.ts deleted file mode 100644 index 38dd915139..0000000000 --- a/server/src/migrations/1700752078178-AddAssetFaceIndicies.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAssetFaceIndicies1700752078178 implements MigrationInterface { - name = 'AddAssetFaceIndicies1700752078178' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX "IDX_bf339a24070dac7e71304ec530" ON "asset_faces" ("personId", "assetId") `); - await queryRunner.query(`CREATE INDEX "IDX_b463c8edb01364bf2beba08ef1" ON "assets" ("stackParentId") `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_b463c8edb01364bf2beba08ef1"`); - await queryRunner.query(`DROP INDEX "IDX_bf339a24070dac7e71304ec530"`); - } - -} diff --git a/server/src/migrations/1701665867595-AddExifCityIndex.ts b/server/src/migrations/1701665867595-AddExifCityIndex.ts deleted file mode 100644 index 0899ea1e6b..0000000000 --- a/server/src/migrations/1701665867595-AddExifCityIndex.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddExifCityIndex1701665867595 implements MigrationInterface { - name = 'AddExifCityIndex1701665867595' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX "exif_city" ON "exif" ("city") `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "exif_city"`); - } - -} diff --git a/server/src/migrations/1702084989965-AddWebSocketAttachmentTable.ts b/server/src/migrations/1702084989965-AddWebSocketAttachmentTable.ts deleted file mode 100644 index c2fc0b222f..0000000000 --- a/server/src/migrations/1702084989965-AddWebSocketAttachmentTable.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddWebSocketAttachmentTable1702084989965 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - 'CREATE TABLE IF NOT EXISTS "socket_io_attachments" (id bigserial UNIQUE, created_at timestamptz DEFAULT NOW(), payload bytea);', - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE "socket_io_attachments"`); - } -} diff --git a/server/src/migrations/1702257380990-DropNullIslandLatLong.ts b/server/src/migrations/1702257380990-DropNullIslandLatLong.ts deleted file mode 100644 index 173b2c5950..0000000000 --- a/server/src/migrations/1702257380990-DropNullIslandLatLong.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class DropNullIslandLatLong1702257380990 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - 'UPDATE "exif" SET latitude = NULL, longitude = NULL WHERE latitude = 0 AND longitude = 0;', - ); - } - - public async down(): Promise { - // There's no way to know which assets used to have 0/0 lat-long if we've - // already run this migration. - } -} diff --git a/server/src/migrations/1702938928766-NullifyFutureBirthDatesAndAddCheckConstraint.ts b/server/src/migrations/1702938928766-NullifyFutureBirthDatesAndAddCheckConstraint.ts deleted file mode 100644 index c646287c81..0000000000 --- a/server/src/migrations/1702938928766-NullifyFutureBirthDatesAndAddCheckConstraint.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class NullifyFutureBirthDatesAndAddCheckConstraint1702938928766 implements MigrationInterface { - name = 'NullifyFutureBirthDatesAndAddCheckConstraint1702938928766' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`UPDATE "person" SET "birthDate" = NULL WHERE "birthDate" > CURRENT_DATE;`); - await queryRunner.query(`ALTER TABLE "person" ADD CONSTRAINT "CHK_b0f82b0ed662bfc24fbb58bb45" CHECK ("birthDate" <= CURRENT_DATE)`); - } - - public async down(queryRunner: QueryRunner): Promise { - // The down method cannot revert the nullified dates - await queryRunner.query(`ALTER TABLE "person" DROP CONSTRAINT "CHK_b0f82b0ed662bfc24fbb58bb45"`); - } - -} diff --git a/server/src/migrations/1702942303661-FixRemovedAssetsSharedLink.ts b/server/src/migrations/1702942303661-FixRemovedAssetsSharedLink.ts deleted file mode 100644 index a55b12fa74..0000000000 --- a/server/src/migrations/1702942303661-FixRemovedAssetsSharedLink.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class FixRemovedAssetsSharedLink1702942303661 implements MigrationInterface { - name = 'FixRemovedAssetsSharedLink1702942303661' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_link__asset" DROP CONSTRAINT "FK_c9fab4aa97ffd1b034f3d6581ab"`); - await queryRunner.query(`ALTER TABLE "shared_link__asset" ADD CONSTRAINT "FK_c9fab4aa97ffd1b034f3d6581ab" FOREIGN KEY ("sharedLinksId") REFERENCES "shared_links"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_link__asset" DROP CONSTRAINT "FK_c9fab4aa97ffd1b034f3d6581ab"`); - await queryRunner.query(`ALTER TABLE "shared_link__asset" ADD CONSTRAINT "FK_c9fab4aa97ffd1b034f3d6581ab" FOREIGN KEY ("sharedLinksId") REFERENCES "shared_links"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - -} diff --git a/server/src/migrations/1703035138085-AddAutoStackId.ts b/server/src/migrations/1703035138085-AddAutoStackId.ts deleted file mode 100644 index d8c83ac565..0000000000 --- a/server/src/migrations/1703035138085-AddAutoStackId.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAutoStackId1703035138085 implements MigrationInterface { - name = 'AddAutoStackId1703035138085' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "autoStackId" character varying`); - await queryRunner.query(`CREATE INDEX "IDX_auto_stack_id" ON "exif" ("autoStackId") `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_auto_stack_id"`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "autoStackId"`); - } - -} diff --git a/server/src/migrations/1703288449127-DefaultStorageTemplateOnForExistingInstallations.ts b/server/src/migrations/1703288449127-DefaultStorageTemplateOnForExistingInstallations.ts deleted file mode 100644 index 4ea88db8eb..0000000000 --- a/server/src/migrations/1703288449127-DefaultStorageTemplateOnForExistingInstallations.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm" - -export class DefaultStorageTemplateOnForExistingInstallations1703288449127 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise { - const adminCount = await queryRunner.query(`SELECT COUNT(*) FROM users WHERE "isAdmin" = true`) - if(adminCount[0].count > 0) { - await queryRunner.query(`INSERT INTO system_config (key, value) VALUES ('storageTemplate.enabled', 'true')`) - } - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DELETE FROM system_config WHERE key = 'storageTemplate.enabled'`) - } - -} diff --git a/server/src/migrations/1704382918223-AddQuotaColumnsToUser.ts b/server/src/migrations/1704382918223-AddQuotaColumnsToUser.ts deleted file mode 100644 index 5d6477b8fb..0000000000 --- a/server/src/migrations/1704382918223-AddQuotaColumnsToUser.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddQuotaColumnsToUser1704382918223 implements MigrationInterface { - name = 'AddQuotaColumnsToUser1704382918223' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "quotaSizeInBytes" bigint`); - await queryRunner.query(`ALTER TABLE "users" ADD "quotaUsageInBytes" bigint NOT NULL DEFAULT '0'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "quotaUsageInBytes"`); - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "quotaSizeInBytes"`); - } - -} diff --git a/server/src/migrations/1704571051932-DefaultOnboardingForExistingInstallations.ts b/server/src/migrations/1704571051932-DefaultOnboardingForExistingInstallations.ts deleted file mode 100644 index cd737b2a62..0000000000 --- a/server/src/migrations/1704571051932-DefaultOnboardingForExistingInstallations.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class DefaultOnboardingForExistingInstallations1704571051932 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - const adminCount = await queryRunner.query(`SELECT COUNT(*) FROM users WHERE "isAdmin" = true`); - if (adminCount[0].count > 0) { - await queryRunner.query(`INSERT INTO system_metadata (key, value) VALUES ($1, $2)`, [ - 'admin-onboarding', - String.raw`"{\"isOnboarded\":true}"`, - ]); - } - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DELETE FROM system_metadata WHERE key = 'admin-onboarding'`); - } -} diff --git a/server/src/migrations/1704943345360-SetAssetFaceNullOnPersonDelete.ts b/server/src/migrations/1704943345360-SetAssetFaceNullOnPersonDelete.ts deleted file mode 100644 index 7b03ee9afb..0000000000 --- a/server/src/migrations/1704943345360-SetAssetFaceNullOnPersonDelete.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm" - -export class SetAssetFaceNullOnPersonDelete1704943345360 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE "asset_faces" - DROP CONSTRAINT "FK_95ad7106dd7b484275443f580f9", - ADD CONSTRAINT "FK_95ad7106dd7b484275443f580f9" - FOREIGN KEY ("personId") REFERENCES "person"("id") - ON DELETE SET NULL ON UPDATE CASCADE - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE "asset_faces" - DROP CONSTRAINT "FK_95ad7106dd7b484275443f580f9", - ADD CONSTRAINT "FK_95ad7106dd7b484275443f580f9" - FOREIGN KEY ("personId") REFERENCES "person"("id") - ON DELETE CASCADE ON UPDATE CASCADE - `); - } - -} diff --git a/server/src/migrations/1705094221536-AddMetadataExtractedAt.ts b/server/src/migrations/1705094221536-AddMetadataExtractedAt.ts deleted file mode 100644 index e76d095c10..0000000000 --- a/server/src/migrations/1705094221536-AddMetadataExtractedAt.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddMetadataExtractedAt1705094221536 implements MigrationInterface { - name = 'AddMetadataExtractedAt1705094221536'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_job_status" ADD "metadataExtractedAt" TIMESTAMP WITH TIME ZONE`); - await queryRunner.query(` - UPDATE "asset_job_status" - SET "metadataExtractedAt" = NOW() - FROM "exif" - WHERE "exif"."assetId" = "asset_job_status"."assetId"; -`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_job_status" DROP COLUMN "metadataExtractedAt"`); - } -} diff --git a/server/src/migrations/1705306747072-AddOriginalFileNameIndex.ts b/server/src/migrations/1705306747072-AddOriginalFileNameIndex.ts deleted file mode 100644 index c62c01f50c..0000000000 --- a/server/src/migrations/1705306747072-AddOriginalFileNameIndex.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddOriginalFileNameIndex1705306747072 implements MigrationInterface { - name = 'AddOriginalFileNameIndex1705306747072'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX "IDX_4d66e76dada1ca180f67a205dc" ON "assets" ("originalFileName") `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_4d66e76dada1ca180f67a205dc"`); - } -} diff --git a/server/src/migrations/1705363967169-CreateAssetStackTable.ts b/server/src/migrations/1705363967169-CreateAssetStackTable.ts deleted file mode 100644 index d1591797ff..0000000000 --- a/server/src/migrations/1705363967169-CreateAssetStackTable.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateAssetStackTable1705197515600 implements MigrationInterface { - name = 'CreateAssetStackTable1705197515600'; - - public async up(queryRunner: QueryRunner): Promise { - // create table - await queryRunner.query( - `CREATE TABLE "asset_stack" ( - "id" uuid NOT NULL DEFAULT uuid_generate_v4(), - "primaryAssetId" uuid NOT NULL, - CONSTRAINT "REL_91704e101438fd0653f582426d" UNIQUE ("primaryAssetId"), - CONSTRAINT "PK_74a27e7fcbd5852463d0af3034b" PRIMARY KEY ("id"))`, - ); - - // create stacks - await queryRunner.query( - `INSERT INTO "asset_stack" ("primaryAssetId") - SELECT DISTINCT("stackParentId" ) - FROM "assets" - WHERE "stackParentId" IS NOT NULL;`, - ); - - // add "stackId" - await queryRunner.query(`ALTER TABLE "assets" ADD COLUMN "stackId" uuid`); - - // set "stackId" for parents - await queryRunner.query( - `UPDATE "assets" - SET "stackId" = "asset_stack"."id" - FROM "asset_stack" - WHERE "assets"."id" = "asset_stack"."primaryAssetId"`, - ); - - // set "stackId" for children - await queryRunner.query( - `UPDATE "assets" - SET "stackId" = "asset_stack"."id" - FROM "asset_stack" - WHERE "assets"."stackParentId" = "asset_stack"."primaryAssetId"`, - ); - - // update constraints - await queryRunner.query(`DROP INDEX "IDX_b463c8edb01364bf2beba08ef1"`); - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_b463c8edb01364bf2beba08ef19"`); - await queryRunner.query( - `ALTER TABLE "assets" ADD CONSTRAINT "FK_f15d48fa3ea5e4bda05ca8ab207" FOREIGN KEY ("stackId") REFERENCES "asset_stack"("id") ON DELETE SET NULL ON UPDATE CASCADE`, - ); - await queryRunner.query( - `ALTER TABLE "asset_stack" ADD CONSTRAINT "FK_91704e101438fd0653f582426dc" FOREIGN KEY ("primaryAssetId") REFERENCES "assets"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - - // drop "stackParentId" - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "stackParentId"`); - } - - public async down(queryRunner: QueryRunner): Promise { - // add "stackParentId" - await queryRunner.query(`ALTER TABLE "assets" ADD COLUMN "stackParentId" uuid`); - - // set "stackParentId" for parents - await queryRunner.query( - `UPDATE "assets" - SET "stackParentId" = "asset_stack"."primaryAssetId" - FROM "asset_stack" - WHERE "assets"."stackId" = "asset_stack"."id" and "assets"."id" != "asset_stack"."primaryAssetId"`, - ); - - // update constraints - await queryRunner.query( - `ALTER TABLE "assets" ADD CONSTRAINT "FK_b463c8edb01364bf2beba08ef19" FOREIGN KEY ("stackParentId") REFERENCES "assets"("id") ON DELETE SET NULL ON UPDATE CASCADE`, - ); - await queryRunner.query(`CREATE INDEX "IDX_b463c8edb01364bf2beba08ef1" ON "assets" ("stackParentId") `); - await queryRunner.query(`ALTER TABLE "asset_stack" DROP CONSTRAINT "FK_91704e101438fd0653f582426dc"`); - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_f15d48fa3ea5e4bda05ca8ab207"`); - - // drop table - await queryRunner.query(`DROP TABLE "asset_stack"`); - - // drop "stackId" - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "stackId"`); - } -} diff --git a/server/src/migrations/1707000751533-AddVectorsToSearchPath.ts b/server/src/migrations/1707000751533-AddVectorsToSearchPath.ts deleted file mode 100644 index 11c84cf970..0000000000 --- a/server/src/migrations/1707000751533-AddVectorsToSearchPath.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddVectorsToSearchPath1707000751533 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - const res = await queryRunner.query(`SELECT current_database() as db`); - const databaseName = res[0]['db']; - await queryRunner.query(`ALTER DATABASE "${databaseName}" SET search_path TO "$user", public, vectors`); - } - - public async down(queryRunner: QueryRunner): Promise { - const databaseName = await queryRunner.query(`SELECT current_database()`); - await queryRunner.query(`ALTER DATABASE "${databaseName}" SET search_path TO "$user", public`); - } -} diff --git a/server/src/migrations/1708059341865-GeodataLocationSearch.ts b/server/src/migrations/1708059341865-GeodataLocationSearch.ts deleted file mode 100644 index af2d5dc5f3..0000000000 --- a/server/src/migrations/1708059341865-GeodataLocationSearch.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class GeodataLocationSearch1708059341865 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS pg_trgm`); - await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS unaccent`); - - // https://stackoverflow.com/a/11007216 - await queryRunner.query(` - CREATE OR REPLACE FUNCTION f_unaccent(text) - RETURNS text - LANGUAGE sql IMMUTABLE PARALLEL SAFE STRICT - RETURN unaccent('unaccent', $1)`); - - await queryRunner.query(`ALTER TABLE geodata_places ADD COLUMN "admin1Name" varchar`); - await queryRunner.query(`ALTER TABLE geodata_places ADD COLUMN "admin2Name" varchar`); - - await queryRunner.query(` - UPDATE geodata_places - SET "admin1Name" = admin1.name - FROM geodata_admin1 admin1 - WHERE admin1.key = "admin1Key"`); - - await queryRunner.query(` - UPDATE geodata_places - SET "admin2Name" = admin2.name - FROM geodata_admin2 admin2 - WHERE admin2.key = "admin2Key"`); - - await queryRunner.query(`DROP TABLE geodata_admin1 CASCADE`); - await queryRunner.query(`DROP TABLE geodata_admin2 CASCADE`); - - await queryRunner.query(` - ALTER TABLE geodata_places - DROP COLUMN "admin1Key", - DROP COLUMN "admin2Key"`); - - await queryRunner.query(` - CREATE INDEX idx_geodata_places_name - ON geodata_places - USING gin (f_unaccent(name) gin_trgm_ops)`); - - await queryRunner.query(` - CREATE INDEX idx_geodata_places_admin1_name - ON geodata_places - USING gin (f_unaccent("admin1Name") gin_trgm_ops)`); - - await queryRunner.query(` - CREATE INDEX idx_geodata_places_admin2_name - ON geodata_places - USING gin (f_unaccent("admin2Name") gin_trgm_ops)`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - CREATE TABLE "geodata_admin1" ( - "key" character varying NOT NULL, - "name" character varying NOT NULL, - CONSTRAINT "PK_3fe3a89c5aac789d365871cb172" PRIMARY KEY ("key") - )`); - - await queryRunner.query(` - CREATE TABLE "geodata_admin2" ( - "key" character varying NOT NULL, - "name" character varying NOT NULL, - CONSTRAINT "PK_1e3886455dbb684d6f6b4756726" PRIMARY KEY ("key") - )`); - - await queryRunner.query(` - ALTER TABLE geodata_places - ADD COLUMN "admin1Key" character varying - GENERATED ALWAYS AS ("countryCode" || '.' || "admin1Code") STORED, - ADD COLUMN "admin2Key" character varying - GENERATED ALWAYS AS ("countryCode" || '.' || "admin1Code" || '.' || "admin2Code") STORED`); - - await queryRunner.query( - ` - INSERT INTO "geodata_admin1" - SELECT DISTINCT - "admin1Key" AS "key", - "admin1Name" AS "name" - FROM geodata_places - WHERE "admin1Name" IS NOT NULL`, - ); - - await queryRunner.query( - ` - INSERT INTO "geodata_admin2" - SELECT DISTINCT - "admin2Key" AS "key", - "admin2Name" AS "name" - FROM geodata_places - WHERE "admin2Name" IS NOT NULL`, - ); - - await queryRunner.query(` - UPDATE geodata_places - SET "admin1Name" = admin1.name - FROM geodata_admin1 admin1 - WHERE admin1.key = "admin1Key"`); - - await queryRunner.query(` - UPDATE geodata_places - SET "admin2Name" = admin2.name - FROM geodata_admin2 admin2 - WHERE admin2.key = "admin2Key";`); - } -} diff --git a/server/src/migrations/1708116312820-GeonamesEnhancement.ts b/server/src/migrations/1708116312820-GeonamesEnhancement.ts deleted file mode 100644 index 0cea9a0411..0000000000 --- a/server/src/migrations/1708116312820-GeonamesEnhancement.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class GeonamesEnhancement1708116312820 implements MigrationInterface { - name = 'GeonamesEnhancement1708116312820' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE geodata_places ADD COLUMN "alternateNames" varchar`); - await queryRunner.query(` - CREATE INDEX idx_geodata_places_admin2_alternate_names - ON geodata_places - USING gin (f_unaccent("alternateNames") gin_trgm_ops)`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE geodata_places DROP COLUMN "alternateNames"`); - } - -} diff --git a/server/src/migrations/1708227417898-AddFileCreatedAtIndex.ts b/server/src/migrations/1708227417898-AddFileCreatedAtIndex.ts deleted file mode 100644 index f7ca40cd46..0000000000 --- a/server/src/migrations/1708227417898-AddFileCreatedAtIndex.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddFileCreatedAtIndex1708227417898 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX idx_asset_file_created_at ON assets ("fileCreatedAt")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX idx_asset_file_created_at`); - } -} diff --git a/server/src/migrations/1708425975121-RemoveExternalPath.ts b/server/src/migrations/1708425975121-RemoveExternalPath.ts deleted file mode 100644 index 6c43e351f9..0000000000 --- a/server/src/migrations/1708425975121-RemoveExternalPath.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveExternalPath1708425975121 implements MigrationInterface { - name = 'RemoveExternalPath1708425975121'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "externalPath"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "externalPath" character varying`); - } -} diff --git a/server/src/migrations/1709150004123-RemoveLibraryWatchPollingOption.ts b/server/src/migrations/1709150004123-RemoveLibraryWatchPollingOption.ts deleted file mode 100644 index 8b7ff3a675..0000000000 --- a/server/src/migrations/1709150004123-RemoveLibraryWatchPollingOption.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveLibraryWatchPollingOption1709150004123 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DELETE FROM "system_config" WHERE key = 'library.watch.usePolling'`); - await queryRunner.query(`DELETE FROM "system_config" WHERE key = 'library.watch.interval'`); - } - - public async down(): Promise { - // noop - } -} diff --git a/server/src/migrations/1709608140355-AddAssetOriginalPathTrigramIndex.ts b/server/src/migrations/1709608140355-AddAssetOriginalPathTrigramIndex.ts deleted file mode 100644 index 1d4f13410a..0000000000 --- a/server/src/migrations/1709608140355-AddAssetOriginalPathTrigramIndex.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddAssetOriginalPathTrigramIndex1709608140355 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - CREATE INDEX idx_originalFileName_trigram - ON assets - USING gin (f_unaccent("originalFileName") gin_trgm_ops)`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "idx_originalFileName_trigram"`); - } -} diff --git a/server/src/migrations/1709763765506-AddExtensionToOriginalFileName.ts b/server/src/migrations/1709763765506-AddExtensionToOriginalFileName.ts deleted file mode 100644 index d1f73b4e3b..0000000000 --- a/server/src/migrations/1709763765506-AddExtensionToOriginalFileName.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddExtensionToOriginalFileName1709763765506 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - WITH extension AS (WITH cte AS (SELECT a.id, STRING_TO_ARRAY(a."originalPath", '.')::TEXT[] AS arr - FROM assets a) - SELECT cte.id, cte.arr[ARRAY_UPPER(cte.arr, 1)] AS "ext" - FROM cte) - UPDATE assets - SET "originalFileName" = assets."originalFileName" || '.' || extension."ext" - FROM extension - WHERE assets.id = extension.id; - `); - } - - public async down(): Promise { - // noop - } -} diff --git a/server/src/migrations/1709825430031-CascadeSharedLinksDelete.ts b/server/src/migrations/1709825430031-CascadeSharedLinksDelete.ts deleted file mode 100644 index 9689741929..0000000000 --- a/server/src/migrations/1709825430031-CascadeSharedLinksDelete.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class CascadeSharedLinksDelete1709825430031 implements MigrationInterface { - name = 'CascadeSharedLinksDelete1709825430031' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" DROP CONSTRAINT "FK_66fe3837414c5a9f1c33ca49340"`); - await queryRunner.query(`ALTER TABLE "shared_links" ADD CONSTRAINT "FK_66fe3837414c5a9f1c33ca49340" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "shared_links" DROP CONSTRAINT "FK_66fe3837414c5a9f1c33ca49340"`); - await queryRunner.query(`ALTER TABLE "shared_links" ADD CONSTRAINT "FK_66fe3837414c5a9f1c33ca49340" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - -} diff --git a/server/src/migrations/1709870213078-AddUserStatus.ts b/server/src/migrations/1709870213078-AddUserStatus.ts deleted file mode 100644 index 858f51258f..0000000000 --- a/server/src/migrations/1709870213078-AddUserStatus.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddUserStatus1709870213078 implements MigrationInterface { - name = 'AddUserStatus1709870213078' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "status" character varying NOT NULL DEFAULT 'active'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "status"`); - } - -} diff --git a/server/src/migrations/1710182081326-AscendingOrderAlbum.ts b/server/src/migrations/1710182081326-AscendingOrderAlbum.ts deleted file mode 100644 index b672ff2b20..0000000000 --- a/server/src/migrations/1710182081326-AscendingOrderAlbum.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AscendingOrderAlbum1710182081326 implements MigrationInterface { - name = 'AscendingOrderAlbum1710182081326' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" ADD "order" character varying NOT NULL DEFAULT 'desc'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums" DROP COLUMN "order"`); - } - -} diff --git a/server/src/migrations/1710293990203-AddAssetRelationIndices.ts b/server/src/migrations/1710293990203-AddAssetRelationIndices.ts deleted file mode 100644 index dd0abf7fd5..0000000000 --- a/server/src/migrations/1710293990203-AddAssetRelationIndices.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAssetRelationIndices1710293990203 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX "IDX_asset_id_stackId" on assets ("id", "stackId")`); - await queryRunner.query(`CREATE INDEX "IDX_tag_asset_assetsId_tagsId" on tag_asset ("assetsId", "tagsId")`); - await queryRunner.query(`CREATE INDEX "IDX_asset_faces_assetId_personId" on asset_faces ("assetId", "personId")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_asset_id_stackId" on assets ("id", "stackId")`); - await queryRunner.query(`DROP INDEX "IDX_tag_asset_assetsId_tagsId" on tag_asset ("assetsId", "tagsId")`); - await queryRunner.query(`DROP INDEX "IDX_asset_faces_assetId_personId" on asset_faces ("assetId", "personId")`); - } -} diff --git a/server/src/migrations/1711257900274-RenameWebpJpegPaths.ts b/server/src/migrations/1711257900274-RenameWebpJpegPaths.ts deleted file mode 100644 index ab6f2a4e9f..0000000000 --- a/server/src/migrations/1711257900274-RenameWebpJpegPaths.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RenameWebpJpegPaths1711257900274 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.renameColumn('assets', 'webpPath', 'thumbnailPath'); - await queryRunner.renameColumn('assets', 'resizePath', 'previewPath'); - await queryRunner.query(` - UPDATE system_config - SET key = 'image.previewSize' - WHERE key = 'thumbnail.jpegSize'`); - await queryRunner.query( - `UPDATE system_config - SET key = 'image.thumbnailSize' - WHERE key = 'thumbnail.webpSize'`, - ); - await queryRunner.query( - `UPDATE system_config - SET key = 'image.quality' - WHERE key = 'thumbnail.quality'`, - ); - await queryRunner.query( - `UPDATE system_config - SET key = 'image.colorspace' - WHERE key = 'thumbnail.colorspace'`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.renameColumn('assets', 'thumbnailPath', 'webpPath'); - await queryRunner.renameColumn('assets', 'previewPath', 'resizePath'); - await queryRunner.query(` - UPDATE system_config - SET key = 'thumbnail.jpegSize' - WHERE key = 'image.previewSize'`); - await queryRunner.query( - `UPDATE system_config - SET key = 'thumbnail.webpSize' - WHERE key = 'image.thumbnailSize'`, - ); - await queryRunner.query( - `UPDATE system_config - SET key = 'thumbnail.quality' - WHERE key = 'image.quality'`, - ); - await queryRunner.query( - `UPDATE system_config - SET key = 'thumbnail.colorspace' - WHERE key = 'image.colorspace'`, - ); - } -} diff --git a/server/src/migrations/1711637874206-AddMemoryTable.ts b/server/src/migrations/1711637874206-AddMemoryTable.ts deleted file mode 100644 index b1c5b437d7..0000000000 --- a/server/src/migrations/1711637874206-AddMemoryTable.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddMemoryTable1711637874206 implements MigrationInterface { - name = 'AddMemoryTable1711637874206' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "memories" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP WITH TIME ZONE, "ownerId" uuid NOT NULL, "type" character varying NOT NULL, "data" jsonb NOT NULL, "isSaved" boolean NOT NULL DEFAULT false, "memoryAt" TIMESTAMP WITH TIME ZONE NOT NULL, "seenAt" TIMESTAMP WITH TIME ZONE, CONSTRAINT "PK_aaa0692d9496fe827b0568612f8" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE TABLE "memories_assets_assets" ("memoriesId" uuid NOT NULL, "assetsId" uuid NOT NULL, CONSTRAINT "PK_fcaf7112a013d1703c011c6793d" PRIMARY KEY ("memoriesId", "assetsId"))`); - await queryRunner.query(`CREATE INDEX "IDX_984e5c9ab1f04d34538cd32334" ON "memories_assets_assets" ("memoriesId") `); - await queryRunner.query(`CREATE INDEX "IDX_6942ecf52d75d4273de19d2c16" ON "memories_assets_assets" ("assetsId") `); - await queryRunner.query(`ALTER TABLE "memories" ADD CONSTRAINT "FK_575842846f0c28fa5da46c99b19" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "memories_assets_assets" ADD CONSTRAINT "FK_984e5c9ab1f04d34538cd32334e" FOREIGN KEY ("memoriesId") REFERENCES "memories"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "memories_assets_assets" ADD CONSTRAINT "FK_6942ecf52d75d4273de19d2c16f" FOREIGN KEY ("assetsId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "memories_assets_assets" DROP CONSTRAINT "FK_6942ecf52d75d4273de19d2c16f"`); - await queryRunner.query(`ALTER TABLE "memories_assets_assets" DROP CONSTRAINT "FK_984e5c9ab1f04d34538cd32334e"`); - await queryRunner.query(`ALTER TABLE "memories" DROP CONSTRAINT "FK_575842846f0c28fa5da46c99b19"`); - await queryRunner.query(`DROP INDEX "IDX_6942ecf52d75d4273de19d2c16"`); - await queryRunner.query(`DROP INDEX "IDX_984e5c9ab1f04d34538cd32334"`); - await queryRunner.query(`DROP TABLE "memories_assets_assets"`); - await queryRunner.query(`DROP TABLE "memories"`); - } - -} diff --git a/server/src/migrations/1711989989911-AddAssetDuplicateColumns.ts b/server/src/migrations/1711989989911-AddAssetDuplicateColumns.ts deleted file mode 100644 index d295ec2d7c..0000000000 --- a/server/src/migrations/1711989989911-AddAssetDuplicateColumns.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateAssetDuplicateColumns1711989989911 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE assets ADD COLUMN "duplicateId" uuid`); - await queryRunner.query(`ALTER TABLE asset_job_status ADD COLUMN "duplicatesDetectedAt" timestamptz`); - await queryRunner.query(`CREATE INDEX "IDX_assets_duplicateId" ON assets ("duplicateId")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE assets DROP COLUMN "duplicateId"`); - await queryRunner.query(`ALTER TABLE asset_job_status DROP COLUMN "duplicatesDetectedAt"`); - } -} diff --git a/server/src/migrations/1713337511945-AddAlbumUserRole.ts b/server/src/migrations/1713337511945-AddAlbumUserRole.ts deleted file mode 100644 index a8d0d3d685..0000000000 --- a/server/src/migrations/1713337511945-AddAlbumUserRole.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAlbumUserRole1713337511945 implements MigrationInterface { - name = 'AddAlbumUserRole1713337511945' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" ADD "role" character varying NOT NULL DEFAULT 'editor'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums_shared_users_users" DROP COLUMN "role"`); - } - -} diff --git a/server/src/migrations/1713490844785-RenameSessionsTable.ts b/server/src/migrations/1713490844785-RenameSessionsTable.ts deleted file mode 100644 index b1b35e8ae6..0000000000 --- a/server/src/migrations/1713490844785-RenameSessionsTable.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RenameSessionsTable1713490844785 implements MigrationInterface { - name = 'RenameSessionsTable1713490844785'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "user_token" RENAME TO "sessions"`); - await queryRunner.query(`ALTER TABLE "sessions" RENAME CONSTRAINT "FK_d37db50eecdf9b8ce4eedd2f918" to "FK_57de40bc620f456c7311aa3a1e6"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "sessions" RENAME CONSTRAINT "FK_57de40bc620f456c7311aa3a1e6" to "FK_d37db50eecdf9b8ce4eedd2f918"`); - await queryRunner.query(`ALTER TABLE "sessions" RENAME TO "user_token"`); - } -} diff --git a/server/src/migrations/1714698592332-RemoveIsReadOnly.ts b/server/src/migrations/1714698592332-RemoveIsReadOnly.ts deleted file mode 100644 index bc56f8dac7..0000000000 --- a/server/src/migrations/1714698592332-RemoveIsReadOnly.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class RemoveIsReadOnly1714698592332 implements MigrationInterface { - name = 'RemoveIsReadOnly1714698592332' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "isReadOnly"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ADD "isReadOnly" boolean NOT NULL DEFAULT false`); - } - -} diff --git a/server/src/migrations/1715435221124-MotionAssetExtensionMP4.ts b/server/src/migrations/1715435221124-MotionAssetExtensionMP4.ts deleted file mode 100644 index f037ba1fb0..0000000000 --- a/server/src/migrations/1715435221124-MotionAssetExtensionMP4.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class MotionAssetExtensionMP41715435221124 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `UPDATE "assets" SET "originalFileName" = regexp_replace("originalFileName", '\\.[a-zA-Z0-9]+$', '.mp4') WHERE "originalPath" LIKE '%.mp4' AND "isVisible" = false`, - ); - } - - public async down(): Promise {} -} diff --git a/server/src/migrations/1715623169039-RemoveTextSearchColumn.ts b/server/src/migrations/1715623169039-RemoveTextSearchColumn.ts deleted file mode 100644 index 50e99f0b2a..0000000000 --- a/server/src/migrations/1715623169039-RemoveTextSearchColumn.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class RemoveTextSearchColumn1715623169039 implements MigrationInterface { - name = 'RemoveTextSearchColumn1715623169039' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "exifTextSearchableColumn"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "exifTextSearchableColumn" tsvector GENERATED ALWAYS AS (TO_TSVECTOR('english', - COALESCE(make, '') || ' ' || - COALESCE(model, '') || ' ' || - COALESCE(orientation, '') || ' ' || - COALESCE("lensModel", '') || ' ' || - COALESCE("city", '') || ' ' || - COALESCE("state", '') || ' ' || - COALESCE("country", ''))) STORED NOT NULL`); - } - -} diff --git a/server/src/migrations/1715787369686-RemoveSystemConfigTable.ts b/server/src/migrations/1715787369686-RemoveSystemConfigTable.ts deleted file mode 100644 index c16eec7160..0000000000 --- a/server/src/migrations/1715787369686-RemoveSystemConfigTable.ts +++ /dev/null @@ -1,31 +0,0 @@ -import _ from 'lodash'; -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveSystemConfigTable1715787369686 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - const overrides = await queryRunner.query('SELECT "key", "value" FROM "system_config"'); - if (overrides.length === 0) { - return; - } - - const config = {}; - for (const { key, value } of overrides) { - _.set(config, key, JSON.parse(value)); - } - - await queryRunner.query(`INSERT INTO "system_metadata" ("key", "value") VALUES ($1, $2)`, [ - 'system-config', - // yup, we're double-stringifying it - JSON.stringify(JSON.stringify(config)), - ]); - - await queryRunner.query(`DROP TABLE "system_config"`); - } - - public async down(queryRunner: QueryRunner): Promise { - // no data restore, you just get the table back - await queryRunner.query( - `CREATE TABLE "system_config" ("key" character varying NOT NULL, "value" character varying, CONSTRAINT "PK_aab69295b445016f56731f4d535" PRIMARY KEY ("key"))`, - ); - } -} diff --git a/server/src/migrations/1715798702876-RemoveLibraryIsVisible.ts b/server/src/migrations/1715798702876-RemoveLibraryIsVisible.ts deleted file mode 100644 index 45f5248c1a..0000000000 --- a/server/src/migrations/1715798702876-RemoveLibraryIsVisible.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class RemoveLibraryIsVisible1715798702876 implements MigrationInterface { - name = 'RemoveLibraryIsVisible1715798702876' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "libraries" DROP COLUMN "isVisible"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "libraries" ADD "isVisible" boolean NOT NULL DEFAULT true`); - } - -} diff --git a/server/src/migrations/1715804005643-RemoveLibraryType.ts b/server/src/migrations/1715804005643-RemoveLibraryType.ts deleted file mode 100644 index cd4dc574f2..0000000000 --- a/server/src/migrations/1715804005643-RemoveLibraryType.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveLibraryType1715804005643 implements MigrationInterface { - name = 'RemoveLibraryType1715804005643'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_9977c3c1de01c3d848039a6b90c"`); - await queryRunner.query(`DROP INDEX "UQ_assets_owner_library_checksum"`); - await queryRunner.query(`DROP INDEX "IDX_originalPath_libraryId"`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "libraryId" DROP NOT NULL`); - await queryRunner.query(` - UPDATE "assets" - SET "libraryId" = NULL - FROM "libraries" - WHERE "assets"."libraryId" = "libraries"."id" - AND "libraries"."type" = 'UPLOAD' -`); - await queryRunner.query(`DELETE FROM "libraries" WHERE "type" = 'UPLOAD'`); - await queryRunner.query(`ALTER TABLE "libraries" DROP COLUMN "type"`); - await queryRunner.query(`CREATE INDEX "IDX_originalPath_libraryId" ON "assets" ("originalPath", "libraryId")`); - await queryRunner.query(`CREATE UNIQUE INDEX "UQ_assets_owner_checksum" ON "assets" ("ownerId", "checksum") WHERE "libraryId" IS NULL`); - await queryRunner.query(`CREATE UNIQUE INDEX "UQ_assets_owner_library_checksum" ON "assets" ("ownerId", "libraryId", "checksum") WHERE "libraryId" IS NOT NULL`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "FK_9977c3c1de01c3d848039a6b90c" FOREIGN KEY ("libraryId") REFERENCES "libraries"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(): Promise { - // not implemented - } -} diff --git a/server/src/migrations/1715890481637-FixJsonB.ts b/server/src/migrations/1715890481637-FixJsonB.ts deleted file mode 100644 index afb39565a3..0000000000 --- a/server/src/migrations/1715890481637-FixJsonB.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class FixJsonB1715890481637 implements MigrationInterface { - name = 'FixJsonB1715890481637'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "system_metadata" ALTER COLUMN "value" DROP DEFAULT`); - const records = await queryRunner.query('SELECT "key", "value" FROM "system_metadata"'); - for (const { key, value } of records) { - await queryRunner.query(`UPDATE "system_metadata" SET "value" = $1 WHERE "key" = $2`, [value, key]); - } - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "system_metadata" ALTER COLUMN "value" SET DEFAULT '{}'`); - const records = await queryRunner.query('SELECT "key", "value" FROM "system_metadata"'); - for (const { key, value } of records) { - await queryRunner.query(`UPDATE "system_metadata" SET "value" = $1 WHERE "key" = $2`, [ - JSON.stringify(JSON.stringify(value)), - key, - ]); - } - } -} diff --git a/server/src/migrations/1716312279245-UserMetadata.ts b/server/src/migrations/1716312279245-UserMetadata.ts deleted file mode 100644 index b118a8d42a..0000000000 --- a/server/src/migrations/1716312279245-UserMetadata.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UserMetadata1716312279245 implements MigrationInterface { - name = 'UserMetadata1716312279245'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE TABLE "user_metadata" ("userId" uuid NOT NULL, "key" character varying NOT NULL, "value" jsonb NOT NULL, CONSTRAINT "PK_5931462150b3438cbc83277fe5a" PRIMARY KEY ("userId", "key"))`, - ); - const users = await queryRunner.query('SELECT "id", "memoriesEnabled", "avatarColor" FROM "users"'); - for (const { id, memoriesEnabled, avatarColor } of users) { - const preferences: any = {}; - if (!memoriesEnabled) { - preferences.memories = { enabled: false }; - } - - if (avatarColor) { - preferences.avatar = { color: avatarColor }; - } - - if (Object.keys(preferences).length === 0) { - continue; - } - - await queryRunner.query('INSERT INTO "user_metadata" ("userId", "key", "value") VALUES ($1, $2, $3)', [ - id, - 'preferences', - preferences, - ]); - } - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "memoriesEnabled"`); - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "avatarColor"`); - await queryRunner.query( - `ALTER TABLE "user_metadata" ADD CONSTRAINT "FK_6afb43681a21cf7815932bc38ac" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "user_metadata" DROP CONSTRAINT "FK_6afb43681a21cf7815932bc38ac"`); - await queryRunner.query(`ALTER TABLE "users" ADD "avatarColor" character varying`); - await queryRunner.query(`ALTER TABLE "users" ADD "memoriesEnabled" boolean NOT NULL DEFAULT true`); - const items = await queryRunner.query( - `SELECT "userId" as "id", "value" FROM "user_metadata" WHERE "key"='preferences'`, - ); - for (const { id, value } of items) { - if (!value) { - continue; - } - - if (value.avatar?.color) { - await queryRunner.query(`UPDATE "users" SET "avatarColor" = $1 WHERE "id" = $2`, [value.avatar.color, id]); - } - - if (value.memories?.enabled === false) { - await queryRunner.query(`UPDATE "users" SET "memoriesEnabled" = false WHERE "id" = $1`, [id]); - } - } - await queryRunner.query(`DROP TABLE "user_metadata"`); - } -} diff --git a/server/src/migrations/1718486162779-AddFaceSearchRelation.ts b/server/src/migrations/1718486162779-AddFaceSearchRelation.ts deleted file mode 100644 index 2bd1acad34..0000000000 --- a/server/src/migrations/1718486162779-AddFaceSearchRelation.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { DatabaseExtension } from 'src/enum'; -import { getVectorExtension } from 'src/repositories/database.repository'; -import { vectorIndexQuery } from 'src/utils/database'; -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddFaceSearchRelation1718486162779 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - const vectorExtension = await getVectorExtension(queryRunner); - if (vectorExtension === DatabaseExtension.Vectors) { - await queryRunner.query(`SET search_path TO "$user", public, vectors`); - } - - const hasEmbeddings = async (tableName: string): Promise => { - const columns = await queryRunner.query( - `SELECT column_name as name - FROM information_schema.columns - WHERE table_name = '${tableName}'`, - ); - return columns.some((column: { name: string }) => column.name === 'embedding'); - }; - - const hasAssetEmbeddings = await hasEmbeddings('smart_search'); - if (!hasAssetEmbeddings) { - await queryRunner.query(`TRUNCATE smart_search`); - await queryRunner.query(`ALTER TABLE smart_search ADD COLUMN IF NOT EXISTS embedding vector(512) NOT NULL`); - } - - await queryRunner.query(` - CREATE TABLE face_search ( - "faceId" uuid PRIMARY KEY REFERENCES asset_faces(id) ON DELETE CASCADE, - embedding vector(512) NOT NULL )`); - - await queryRunner.query(`ALTER TABLE face_search ALTER COLUMN embedding SET STORAGE EXTERNAL`); - await queryRunner.query(`ALTER TABLE smart_search ALTER COLUMN embedding SET STORAGE EXTERNAL`); - - const hasFaceEmbeddings = await hasEmbeddings('asset_faces'); - if (hasFaceEmbeddings) { - await queryRunner.query(` - INSERT INTO face_search("faceId", embedding) - SELECT id, embedding - FROM asset_faces faces`); - } - - await queryRunner.query(`ALTER TABLE asset_faces DROP COLUMN IF EXISTS embedding`); - - await queryRunner.query(`ALTER TABLE face_search ALTER COLUMN embedding SET DATA TYPE real[]`); - await queryRunner.query(`ALTER TABLE face_search ALTER COLUMN embedding SET DATA TYPE vector(512)`); - - await queryRunner.query(vectorIndexQuery({ vectorExtension, table: 'smart_search', indexName: 'clip_index' })); - await queryRunner.query(vectorIndexQuery({ vectorExtension, table: 'face_search', indexName: 'face_index' })); - } - - public async down(queryRunner: QueryRunner): Promise { - const vectorExtension = await getVectorExtension(queryRunner); - if (vectorExtension === DatabaseExtension.Vectors) { - await queryRunner.query(`SET search_path TO "$user", public, vectors`); - } - - await queryRunner.query(`ALTER TABLE asset_faces ADD COLUMN "embedding" vector(512)`); - await queryRunner.query(`ALTER TABLE face_search ALTER COLUMN embedding SET STORAGE DEFAULT`); - await queryRunner.query(`ALTER TABLE smart_search ALTER COLUMN embedding SET STORAGE DEFAULT`); - await queryRunner.query(` - UPDATE asset_faces - SET embedding = fs.embedding - FROM face_search fs - WHERE id = fs."faceId"`); - await queryRunner.query(`DROP TABLE face_search`); - - await queryRunner.query(vectorIndexQuery({ vectorExtension, table: 'asset_faces', indexName: 'face_index' })); - } -} diff --git a/server/src/migrations/1719359859887-FixLivePhotoVideoRelation.ts b/server/src/migrations/1719359859887-FixLivePhotoVideoRelation.ts deleted file mode 100644 index 9bf2a2b8d7..0000000000 --- a/server/src/migrations/1719359859887-FixLivePhotoVideoRelation.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class FixLivePhotoVideoRelation1719359859887 implements MigrationInterface { - name = 'FixLivePhotoVideoRelation1719359859887' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_16294b83fa8c0149719a1f631ef"`); - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "UQ_16294b83fa8c0149719a1f631ef"`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "FK_16294b83fa8c0149719a1f631ef" FOREIGN KEY ("livePhotoVideoId") REFERENCES "assets"("id") ON DELETE SET NULL ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP CONSTRAINT "FK_16294b83fa8c0149719a1f631ef"`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "UQ_16294b83fa8c0149719a1f631ef" UNIQUE ("livePhotoVideoId")`); - await queryRunner.query(`ALTER TABLE "assets" ADD CONSTRAINT "FK_16294b83fa8c0149719a1f631ef" FOREIGN KEY ("livePhotoVideoId") REFERENCES "assets"("id") ON DELETE SET NULL ON UPDATE CASCADE`); - } - -} diff --git a/server/src/migrations/1720207981949-AddStackOwner.ts b/server/src/migrations/1720207981949-AddStackOwner.ts deleted file mode 100644 index 61394cc985..0000000000 --- a/server/src/migrations/1720207981949-AddStackOwner.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddStackOwner1720207981949 implements MigrationInterface { - name = 'AddStackOwner1720207981949' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_stack" ADD "ownerId" uuid`); - await queryRunner.query(` - UPDATE "asset_stack" stack - SET "ownerId" = asset."ownerId" - FROM "assets" asset - WHERE stack."primaryAssetId" = asset."id" - `) - await queryRunner.query('ALTER TABLE "asset_stack" ALTER COLUMN "ownerId" SET NOT NULL') - await queryRunner.query(`ALTER TABLE "asset_stack" ADD CONSTRAINT "FK_c05079e542fd74de3b5ecb5c1c8" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_stack" DROP CONSTRAINT "FK_c05079e542fd74de3b5ecb5c1c8"`); - await queryRunner.query(`ALTER TABLE "asset_stack" DROP COLUMN "ownerId"`); - } -} diff --git a/server/src/migrations/1720375641148-natural-earth-countries.ts b/server/src/migrations/1720375641148-natural-earth-countries.ts deleted file mode 100644 index 8c58321dca..0000000000 --- a/server/src/migrations/1720375641148-natural-earth-countries.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class NaturalEarthCountries1720375641148 implements MigrationInterface { - name = 'NaturalEarthCountries1720375641148' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "naturalearth_countries" ("id" SERIAL NOT NULL, "admin" character varying(50) NOT NULL, "admin_a3" character varying(3) NOT NULL, "type" character varying(50) NOT NULL, "coordinates" polygon NOT NULL, CONSTRAINT "PK_21a6d86d1ab5d841648212e5353" PRIMARY KEY ("id"))`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE "naturalearth_countries"`); - } - -} diff --git a/server/src/migrations/1721249222549-AddSourceColumnToAssetFace.ts b/server/src/migrations/1721249222549-AddSourceColumnToAssetFace.ts deleted file mode 100644 index 7f185077ff..0000000000 --- a/server/src/migrations/1721249222549-AddSourceColumnToAssetFace.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddSourceColumnToAssetFace1721249222549 implements MigrationInterface { - name = 'AddSourceColumnToAssetFace1721249222549' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TYPE sourceType AS ENUM ('machine-learning', 'exif');`); - await queryRunner.query(`ALTER TABLE "asset_faces" ADD "sourceType" sourceType NOT NULL DEFAULT 'machine-learning'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_faces" DROP COLUMN "sourceType"`); - await queryRunner.query(`DROP TYPE sourceType`); - } - -} diff --git a/server/src/migrations/1722753178937-AddExifRating.ts b/server/src/migrations/1722753178937-AddExifRating.ts deleted file mode 100644 index 52e8fb71e8..0000000000 --- a/server/src/migrations/1722753178937-AddExifRating.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddRating1722753178937 implements MigrationInterface { - name = 'AddRating1722753178937' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" ADD "rating" integer`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "rating"`); - } - -} diff --git a/server/src/migrations/1723719333525-AddApiKeyPermissions.ts b/server/src/migrations/1723719333525-AddApiKeyPermissions.ts deleted file mode 100644 index d585d98bcb..0000000000 --- a/server/src/migrations/1723719333525-AddApiKeyPermissions.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddApiKeyPermissions1723719333525 implements MigrationInterface { - name = 'AddApiKeyPermissions1723719333525'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "api_keys" ADD "permissions" character varying array NOT NULL DEFAULT '{all}'`); - await queryRunner.query(`ALTER TABLE "api_keys" ALTER COLUMN "permissions" DROP DEFAULT`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "api_keys" DROP COLUMN "permissions"`); - } -} diff --git a/server/src/migrations/1724080823160-AddThumbnailJobStatus.ts b/server/src/migrations/1724080823160-AddThumbnailJobStatus.ts deleted file mode 100644 index a71ddfbcf3..0000000000 --- a/server/src/migrations/1724080823160-AddThumbnailJobStatus.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddThumbnailJobStatus1724080823160 implements MigrationInterface { - name = 'AddThumbnailJobStatus1724080823160'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_job_status" ADD "previewAt" TIMESTAMP WITH TIME ZONE`); - await queryRunner.query(`ALTER TABLE "asset_job_status" ADD "thumbnailAt" TIMESTAMP WITH TIME ZONE`); - await queryRunner.query(`UPDATE "asset_job_status" SET "previewAt" = NOW() FROM "assets" WHERE "assetId" = "assets"."id" AND "assets"."previewPath" IS NOT NULL`); - await queryRunner.query(`UPDATE "asset_job_status" SET "thumbnailAt" = NOW() FROM "assets" WHERE "assetId" = "assets"."id" AND "assets"."thumbnailPath" IS NOT NULL`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "asset_job_status" DROP COLUMN "thumbnailAt"`); - await queryRunner.query(`ALTER TABLE "asset_job_status" DROP COLUMN "previewAt"`); - } -} diff --git a/server/src/migrations/1724101822106-AddAssetFilesTable.ts b/server/src/migrations/1724101822106-AddAssetFilesTable.ts deleted file mode 100644 index bb086b084e..0000000000 --- a/server/src/migrations/1724101822106-AddAssetFilesTable.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAssetFilesTable1724101822106 implements MigrationInterface { - name = 'AddAssetFilesTable1724101822106' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "asset_files" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "assetId" uuid NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "type" character varying NOT NULL, "path" character varying NOT NULL, CONSTRAINT "UQ_assetId_type" UNIQUE ("assetId", "type"), CONSTRAINT "PK_c41dc3e9ef5e1c57ca5a08a0004" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_asset_files_assetId" ON "asset_files" ("assetId") `); - await queryRunner.query(`ALTER TABLE "asset_files" ADD CONSTRAINT "FK_e3e103a5f1d8bc8402999286040" FOREIGN KEY ("assetId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - - // preview path migration - await queryRunner.query(`INSERT INTO "asset_files" ("assetId", "type", "path") SELECT "id", 'preview', "previewPath" FROM "assets" WHERE "previewPath" IS NOT NULL AND "previewPath" != ''`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "previewPath"`); - - // thumbnail path migration - await queryRunner.query(`INSERT INTO "asset_files" ("assetId", "type", "path") SELECT "id", 'thumbnail', "thumbnailPath" FROM "assets" WHERE "thumbnailPath" IS NOT NULL AND "thumbnailPath" != ''`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "thumbnailPath"`); - } - - public async down(queryRunner: QueryRunner): Promise { - // undo preview path migration - await queryRunner.query(`ALTER TABLE "assets" ADD "previewPath" character varying`); - await queryRunner.query(`UPDATE "assets" SET "previewPath" = "asset_files".path FROM "asset_files" WHERE "assets".id = "asset_files".assetId AND "asset_files".type = 'preview'`); - - // undo thumbnail path migration - await queryRunner.query(`ALTER TABLE "assets" ADD "thumbnailPath" character varying DEFAULT ''`); - await queryRunner.query(`UPDATE "assets" SET "thumbnailPath" = "asset_files".path FROM "asset_files" WHERE "assets".id = "asset_files".assetId AND "asset_files".type = 'thumbnail'`); - - await queryRunner.query(`ALTER TABLE "asset_files" DROP CONSTRAINT "FK_e3e103a5f1d8bc8402999286040"`); - await queryRunner.query(`DROP INDEX "IDX_asset_files_assetId"`); - await queryRunner.query(`DROP TABLE "asset_files"`); - } - -} diff --git a/server/src/migrations/1724790460210-NestedTagTable.ts b/server/src/migrations/1724790460210-NestedTagTable.ts deleted file mode 100644 index d468ff6ba4..0000000000 --- a/server/src/migrations/1724790460210-NestedTagTable.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class NestedTagTable1724790460210 implements MigrationInterface { - name = 'NestedTagTable1724790460210' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query('TRUNCATE TABLE "tags" CASCADE'); - await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "FK_92e67dc508c705dd66c94615576"`); - await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "UQ_tag_name_userId"`); - await queryRunner.query(`CREATE TABLE "tags_closure" ("id_ancestor" uuid NOT NULL, "id_descendant" uuid NOT NULL, CONSTRAINT "PK_eab38eb12a3ec6df8376c95477c" PRIMARY KEY ("id_ancestor", "id_descendant"))`); - await queryRunner.query(`CREATE INDEX "IDX_15fbcbc67663c6bfc07b354c22" ON "tags_closure" ("id_ancestor") `); - await queryRunner.query(`CREATE INDEX "IDX_b1a2a7ed45c29179b5ad51548a" ON "tags_closure" ("id_descendant") `); - await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "renameTagId"`); - await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "type"`); - await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "name"`); - await queryRunner.query(`ALTER TABLE "tags" ADD "value" character varying NOT NULL`); - await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "UQ_d090e09fe86ebe2ec0aec27b451" UNIQUE ("value")`); - await queryRunner.query(`ALTER TABLE "tags" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`); - await queryRunner.query(`ALTER TABLE "tags" ADD "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`); - await queryRunner.query(`ALTER TABLE "tags" ADD "color" character varying`); - await queryRunner.query(`ALTER TABLE "tags" ADD "parentId" uuid`); - await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "FK_9f9590cc11561f1f48ff034ef99" FOREIGN KEY ("parentId") REFERENCES "tags"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "FK_92e67dc508c705dd66c94615576" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "tags_closure" ADD CONSTRAINT "FK_15fbcbc67663c6bfc07b354c22c" FOREIGN KEY ("id_ancestor") REFERENCES "tags"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "tags_closure" ADD CONSTRAINT "FK_b1a2a7ed45c29179b5ad51548a1" FOREIGN KEY ("id_descendant") REFERENCES "tags"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "tag_asset" DROP CONSTRAINT "FK_e99f31ea4cdf3a2c35c7287eb42"`); - await queryRunner.query(`ALTER TABLE "tag_asset" DROP CONSTRAINT "FK_f8e8a9e893cb5c54907f1b798e9"`); - await queryRunner.query(`ALTER TABLE "tag_asset" ADD CONSTRAINT "FK_f8e8a9e893cb5c54907f1b798e9" FOREIGN KEY ("assetsId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "tag_asset" ADD CONSTRAINT "FK_e99f31ea4cdf3a2c35c7287eb42" FOREIGN KEY ("tagsId") REFERENCES "tags"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "tag_asset" DROP CONSTRAINT "FK_e99f31ea4cdf3a2c35c7287eb42"`); - await queryRunner.query(`ALTER TABLE "tag_asset" DROP CONSTRAINT "FK_f8e8a9e893cb5c54907f1b798e9"`); - await queryRunner.query(`ALTER TABLE "tag_asset" ADD CONSTRAINT "FK_f8e8a9e893cb5c54907f1b798e9" FOREIGN KEY ("assetsId") REFERENCES "assets"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "tag_asset" ADD CONSTRAINT "FK_e99f31ea4cdf3a2c35c7287eb42" FOREIGN KEY ("tagsId") REFERENCES "tags"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "tags_closure" DROP CONSTRAINT "FK_b1a2a7ed45c29179b5ad51548a1"`); - await queryRunner.query(`ALTER TABLE "tags_closure" DROP CONSTRAINT "FK_15fbcbc67663c6bfc07b354c22c"`); - await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "FK_92e67dc508c705dd66c94615576"`); - await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "FK_9f9590cc11561f1f48ff034ef99"`); - await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "parentId"`); - await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "color"`); - await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "updatedAt"`); - await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "createdAt"`); - await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "UQ_d090e09fe86ebe2ec0aec27b451"`); - await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "value"`); - await queryRunner.query(`ALTER TABLE "tags" ADD "name" character varying NOT NULL`); - await queryRunner.query(`ALTER TABLE "tags" ADD "type" character varying NOT NULL`); - await queryRunner.query(`ALTER TABLE "tags" ADD "renameTagId" uuid`); - await queryRunner.query(`DROP INDEX "IDX_b1a2a7ed45c29179b5ad51548a"`); - await queryRunner.query(`DROP INDEX "IDX_15fbcbc67663c6bfc07b354c22"`); - await queryRunner.query(`DROP TABLE "tags_closure"`); - await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "UQ_tag_name_userId" UNIQUE ("name", "userId")`); - await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "FK_92e67dc508c705dd66c94615576" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } - -} diff --git a/server/src/migrations/1725023079109-FixTagUniqueness.ts b/server/src/migrations/1725023079109-FixTagUniqueness.ts deleted file mode 100644 index 859712621c..0000000000 --- a/server/src/migrations/1725023079109-FixTagUniqueness.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class FixTagUniqueness1725023079109 implements MigrationInterface { - name = 'FixTagUniqueness1725023079109' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "UQ_d090e09fe86ebe2ec0aec27b451"`); - await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "UQ_79d6f16e52bb2c7130375246793" UNIQUE ("userId", "value")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "tags" DROP CONSTRAINT "UQ_79d6f16e52bb2c7130375246793"`); - await queryRunner.query(`ALTER TABLE "tags" ADD CONSTRAINT "UQ_d090e09fe86ebe2ec0aec27b451" UNIQUE ("value")`); - } - -} diff --git a/server/src/migrations/1725258039306-UpsertMissingAssetJobStatus.ts b/server/src/migrations/1725258039306-UpsertMissingAssetJobStatus.ts deleted file mode 100644 index 8eb47db438..0000000000 --- a/server/src/migrations/1725258039306-UpsertMissingAssetJobStatus.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UpsertMissingAssetJobStatus1725258039306 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `INSERT INTO "asset_job_status" ("assetId", "facesRecognizedAt", "metadataExtractedAt", "duplicatesDetectedAt", "previewAt", "thumbnailAt") SELECT "assetId", NULL, NULL, NULL, NULL, NULL FROM "asset_files" f WHERE "f"."path" IS NOT NULL ON CONFLICT DO NOTHING`, - ); - - await queryRunner.query( - `UPDATE "asset_job_status" SET "previewAt" = NOW() FROM "asset_files" f WHERE "previewAt" IS NULL AND "asset_job_status"."assetId" = "f"."assetId" AND "f"."type" = 'preview' AND "f"."path" IS NOT NULL`, - ); - - await queryRunner.query( - `UPDATE "asset_job_status" SET "thumbnailAt" = NOW() FROM "asset_files" f WHERE "thumbnailAt" IS NULL AND "asset_job_status"."assetId" = "f"."assetId" AND "f"."type" = 'thumbnail' AND "f"."path" IS NOT NULL`, - ); - } - - public async down(): Promise { - // do nothing - } -} diff --git a/server/src/migrations/1725327902980-RemoveThumbailAtForMissingThumbnails.ts b/server/src/migrations/1725327902980-RemoveThumbailAtForMissingThumbnails.ts deleted file mode 100644 index 98a3fe403a..0000000000 --- a/server/src/migrations/1725327902980-RemoveThumbailAtForMissingThumbnails.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveThumbailAtForMissingThumbnails1725327902980 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `UPDATE "asset_job_status" j SET "thumbnailAt" = NULL WHERE j."thumbnailAt" IS NOT NULL AND NOT EXISTS ( SELECT 1 FROM asset_files f WHERE j."assetId" = f."assetId" AND f."type" = 'thumbnail' AND f."path" IS NOT NULL )`, - ); - } - - public async down(): Promise { - // do nothing - } -} diff --git a/server/src/migrations/1725730782681-RemoveHiddenAssetsFromAlbums.ts b/server/src/migrations/1725730782681-RemoveHiddenAssetsFromAlbums.ts deleted file mode 100644 index 2dfb5b7978..0000000000 --- a/server/src/migrations/1725730782681-RemoveHiddenAssetsFromAlbums.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveHiddenAssetsFromAlbums1725730782681 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `DELETE FROM "albums_assets_assets" WHERE "assetsId" IN (SELECT "id" FROM "assets" WHERE "isVisible" = false)`, - ); - } - - public async down(): Promise { - // noop - } -} diff --git a/server/src/migrations/1726491047923-AddprofileChangedAt.ts b/server/src/migrations/1726491047923-AddprofileChangedAt.ts deleted file mode 100644 index bcf568426a..0000000000 --- a/server/src/migrations/1726491047923-AddprofileChangedAt.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddprofileChangedAt1726491047923 implements MigrationInterface { - name = 'AddprofileChangedAt1726491047923' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" ADD "profileChangedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "profileChangedAt"`); - } - -} diff --git a/server/src/migrations/1726593009549-AddAssetStatus.ts b/server/src/migrations/1726593009549-AddAssetStatus.ts deleted file mode 100644 index 5b243b05b5..0000000000 --- a/server/src/migrations/1726593009549-AddAssetStatus.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddAssetStatus1726593009549 implements MigrationInterface { - name = 'AddAssetStatus1726593009549' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TYPE "assets_status_enum" AS ENUM('active', 'trashed', 'deleted')`); - await queryRunner.query(`ALTER TABLE "assets" ADD "status" "assets_status_enum" NOT NULL DEFAULT 'active'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "status"`); - await queryRunner.query(`DROP TYPE "assets_status_enum"`); - } - -} diff --git a/server/src/migrations/1727471863507-SeparateQualityForThumbnailAndPreview.ts b/server/src/migrations/1727471863507-SeparateQualityForThumbnailAndPreview.ts deleted file mode 100644 index e02203997f..0000000000 --- a/server/src/migrations/1727471863507-SeparateQualityForThumbnailAndPreview.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class SeparateQualityForThumbnailAndPreview1727471863507 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - update system_metadata - set value = jsonb_set(value, '{image}', jsonb_strip_nulls( - jsonb_build_object( - 'preview', jsonb_build_object( - 'format', value->'image'->'previewFormat', - 'quality', value->'image'->'quality', - 'size', value->'image'->'previewSize'), - 'thumbnail', jsonb_build_object( - 'format', value->'image'->'thumbnailFormat', - 'quality', value->'image'->'quality', - 'size', value->'image'->'thumbnailSize'), - 'extractEmbedded', value->'extractEmbedded', - 'colorspace', value->'colorspace' - ))) - where key = 'system-config'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - update system_metadata - set value = jsonb_set(value, '{image}', jsonb_strip_nulls(jsonb_build_object( - 'previewFormat', value->'image'->'preview'->'format', - 'previewSize', value->'image'->'preview'->'size', - 'thumbnailFormat', value->'image'->'thumbnail'->'format', - 'thumbnailSize', value->'image'->'thumbnail'->'size', - 'extractEmbedded', value->'extractEmbedded', - 'colorspace', value->'colorspace', - 'quality', value->'image'->'preview'->'quality' - ))) - where key = 'system-config'`); - } -} diff --git a/server/src/migrations/1727781844613-IsOfflineSetDeletedAt.ts b/server/src/migrations/1727781844613-IsOfflineSetDeletedAt.ts deleted file mode 100644 index 050e9a93cf..0000000000 --- a/server/src/migrations/1727781844613-IsOfflineSetDeletedAt.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class IsOfflineSetDeletedAt1727781844613 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `UPDATE assets SET "deletedAt" = now() WHERE "isOffline" = true AND "deletedAt" IS NULL`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `UPDATE assets SET "deletedAt" = null WHERE "isOffline" = true`, - ); - } -} diff --git a/server/src/migrations/1727797340951-AddVersionHistory.ts b/server/src/migrations/1727797340951-AddVersionHistory.ts deleted file mode 100644 index 7eb731d1a3..0000000000 --- a/server/src/migrations/1727797340951-AddVersionHistory.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddVersionHistory1727797340951 implements MigrationInterface { - name = 'AddVersionHistory1727797340951' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "version_history" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "version" character varying NOT NULL, CONSTRAINT "PK_5db259cbb09ce82c0d13cfd1b23" PRIMARY KEY ("id"))`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE "version_history"`); - } - -} diff --git a/server/src/migrations/1729793521993-AddAlbumAssetCreatedAt.ts b/server/src/migrations/1729793521993-AddAlbumAssetCreatedAt.ts deleted file mode 100644 index 280b34890d..0000000000 --- a/server/src/migrations/1729793521993-AddAlbumAssetCreatedAt.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddAlbumAssetCreatedAt1729793521993 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "albums_assets_assets" ADD COLUMN "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "albums_assets_assets" DROP COLUMN "createdAt"`); - } -} diff --git a/server/src/migrations/1730227312171-RemoveNplFromSystemConfig.ts b/server/src/migrations/1730227312171-RemoveNplFromSystemConfig.ts deleted file mode 100644 index 2c929191dd..0000000000 --- a/server/src/migrations/1730227312171-RemoveNplFromSystemConfig.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveNplFromSystemConfig1730227312171 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - update system_metadata - set value = value #- '{ffmpeg,npl}' - where key = 'system-config' and value->'ffmpeg'->'npl' is not null`); - } - - public async down(): Promise {} -} diff --git a/server/src/migrations/1730989238718-DropSmartInfoTable.ts b/server/src/migrations/1730989238718-DropSmartInfoTable.ts deleted file mode 100644 index a4de2652d6..0000000000 --- a/server/src/migrations/1730989238718-DropSmartInfoTable.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class DropSmartInfoTable1730989238718 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE smart_info`); - } - - public async down(): Promise { - // not implemented - } -} diff --git a/server/src/migrations/1732072134943-NaturalEarthCountriesIdentityColumn.ts b/server/src/migrations/1732072134943-NaturalEarthCountriesIdentityColumn.ts deleted file mode 100644 index 3ebe8108cb..0000000000 --- a/server/src/migrations/1732072134943-NaturalEarthCountriesIdentityColumn.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class NaturalEarthCountriesIdentityColumn1732072134943 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE naturalearth_countries ALTER id DROP DEFAULT`); - await queryRunner.query(`DROP SEQUENCE naturalearth_countries_id_seq`); - await queryRunner.query(`ALTER TABLE naturalearth_countries ALTER id ADD GENERATED ALWAYS AS IDENTITY`); - - // same as ll_to_earth, but with explicit schema to avoid weirdness and allow it to work in expression indices - await queryRunner.query(` - CREATE FUNCTION ll_to_earth_public(latitude double precision, longitude double precision) RETURNS public.earth PARALLEL SAFE IMMUTABLE STRICT LANGUAGE SQL AS $$ - SELECT public.cube(public.cube(public.cube(public.earth()*cos(radians(latitude))*cos(radians(longitude))),public.earth()*cos(radians(latitude))*sin(radians(longitude))),public.earth()*sin(radians(latitude)))::public.earth - $$`); - - await queryRunner.query(`ALTER TABLE geodata_places DROP COLUMN "earthCoord"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE naturalearth_countries ALTER id DROP GENERATED`); - await queryRunner.query(`CREATE SEQUENCE naturalearth_countries_id_seq`); - await queryRunner.query( - `ALTER TABLE naturalearth_countries ALTER id SET DEFAULT nextval('naturalearth_countries_id_seq'::regclass)`, - ); - await queryRunner.query(`DROP FUNCTION ll_to_earth_public`); - await queryRunner.query( - `ALTER TABLE "geodata_places" ADD "earthCoord" earth GENERATED ALWAYS AS (ll_to_earth(latitude, longitude)) STORED`, - ); - } -} diff --git a/server/src/migrations/1733339482860-RenameMachineLearningUrlToUrls.ts b/server/src/migrations/1733339482860-RenameMachineLearningUrlToUrls.ts deleted file mode 100644 index 65bb02c8e2..0000000000 --- a/server/src/migrations/1733339482860-RenameMachineLearningUrlToUrls.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RenameMachineLearningUrlToUrls1733339482860 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - UPDATE system_metadata - SET value = jsonb_insert(value #- '{machineLearning,url}', '{machineLearning,urls}'::text[], jsonb_build_array(value->'machineLearning'->'url')) - WHERE key = 'system-config' AND value->'machineLearning'->'url' IS NOT NULL - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - UPDATE system_metadata - SET value = jsonb_insert(value #- '{machineLearning,urls}', '{machineLearning,url}'::text[], to_jsonb(value->'machineLearning'->'urls'->>0)) - WHERE key = 'system-config' AND value->'machineLearning'->'urls' IS NOT NULL AND jsonb_array_length(value->'machineLearning'->'urls') >= 1 - `); - } -} diff --git a/server/src/migrations/1734574016301-AddTimeBucketIndices.ts b/server/src/migrations/1734574016301-AddTimeBucketIndices.ts deleted file mode 100644 index 2162a713fc..0000000000 --- a/server/src/migrations/1734574016301-AddTimeBucketIndices.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddTimeBucketIndices1734574016301 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE INDEX idx_local_date_time_month ON assets ((date_trunc('MONTH', "localDateTime" at time zone 'UTC') at time zone 'UTC'))`, - ); - await queryRunner.query( - `CREATE INDEX idx_local_date_time ON assets ((("localDateTime" at time zone 'UTC')::date))`, - ); - await queryRunner.query(`DROP INDEX "IDX_day_of_month"`); - await queryRunner.query(`DROP INDEX "IDX_month"`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX idx_local_date_time_month`); - await queryRunner.query(`DROP INDEX idx_local_date_time`); - await queryRunner.query( - `CREATE INDEX "IDX_day_of_month" ON assets (EXTRACT(DAY FROM "localDateTime" AT TIME ZONE 'UTC'))`, - ); - await queryRunner.query( - `CREATE INDEX "IDX_month" ON assets (EXTRACT(MONTH FROM "localDateTime" AT TIME ZONE 'UTC'))`, - ); - } -} diff --git a/server/src/migrations/1734879118272-AddIsFavoritePerson.ts b/server/src/migrations/1734879118272-AddIsFavoritePerson.ts deleted file mode 100644 index 6f7640f96f..0000000000 --- a/server/src/migrations/1734879118272-AddIsFavoritePerson.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddIsFavoritePerson1734879118272 implements MigrationInterface { - name = 'AddIsFavoritePerson1734879118272' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" ADD "isFavorite" boolean NOT NULL DEFAULT false`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" DROP COLUMN "isFavorite"`); - } - -} diff --git a/server/src/migrations/1737672307560-AddUpdatedAtTriggers.ts b/server/src/migrations/1737672307560-AddUpdatedAtTriggers.ts deleted file mode 100644 index 74dde826fb..0000000000 --- a/server/src/migrations/1737672307560-AddUpdatedAtTriggers.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddUpdatedAtTriggers1737672307560 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - create function updated_at() - returns trigger as $$ - begin - new."updatedAt" = now(); - return new; - end; - $$ language 'plpgsql'`); - - await queryRunner.query(` - create trigger activity_updated_at - before update on activity - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger albums_updated_at - before update on albums - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger api_keys_updated_at - before update on api_keys - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger asset_files_updated_at - before update on asset_files - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger assets_updated_at - before update on assets - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger libraries_updated_at - before update on libraries - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger memories_updated_at - before update on memories - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger partners_updated_at - before update on partners - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger person_updated_at - before update on person - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger sessions_updated_at - before update on sessions - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger tags_updated_at - before update on tags - for each row execute procedure updated_at() - `); - - await queryRunner.query(` - create trigger users_updated_at - before update on users - for each row execute procedure updated_at() - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`drop trigger activity_updated_at on activity`); - await queryRunner.query(`drop trigger albums_updated_at on albums`); - await queryRunner.query(`drop trigger api_keys_updated_at on api_keys`); - await queryRunner.query(`drop trigger asset_files_updated_at on asset_files`); - await queryRunner.query(`drop trigger assets_updated_at on assets`); - await queryRunner.query(`drop trigger libraries_updated_at on libraries`); - await queryRunner.query(`drop trigger memories_updated_at on memories`); - await queryRunner.query(`drop trigger partners_updated_at on partners`); - await queryRunner.query(`drop trigger person_updated_at on person`); - await queryRunner.query(`drop trigger sessions_updated_at on sessions`); - await queryRunner.query(`drop trigger tags_updated_at on tags`); - await queryRunner.query(`drop trigger users_updated_at on users`); - await queryRunner.query(`drop function updated_at_trigger`); - } -} diff --git a/server/src/migrations/1737845696644-NullableDates.ts b/server/src/migrations/1737845696644-NullableDates.ts deleted file mode 100644 index 8a08b985c5..0000000000 --- a/server/src/migrations/1737845696644-NullableDates.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class NullableDates1737845696644 implements MigrationInterface { - name = 'NullableDates1737845696644' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "fileCreatedAt" DROP NOT NULL`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "localDateTime" DROP NOT NULL`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "fileModifiedAt" DROP NOT NULL`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "fileModifiedAt" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "localDateTime" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "fileCreatedAt" SET NOT NULL`); - } - -} diff --git a/server/src/migrations/1738889177573-AddPersonColor.ts b/server/src/migrations/1738889177573-AddPersonColor.ts deleted file mode 100644 index ebdc86f52d..0000000000 --- a/server/src/migrations/1738889177573-AddPersonColor.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddPersonColor1738889177573 implements MigrationInterface { - name = 'AddPersonColor1738889177573' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" ADD "color" character varying`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "person" DROP COLUMN "color"`); - } - -} diff --git a/server/src/migrations/1739466714036-AddDeletedAtColumnToAssetFacesTable.ts b/server/src/migrations/1739466714036-AddDeletedAtColumnToAssetFacesTable.ts deleted file mode 100644 index e6f18e2618..0000000000 --- a/server/src/migrations/1739466714036-AddDeletedAtColumnToAssetFacesTable.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddDeletedAtColumnToAssetFacesTable1739466714036 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE asset_faces - ADD COLUMN "deletedAt" TIMESTAMP WITH TIME ZONE DEFAULT NULL - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE asset_faces - DROP COLUMN "deletedAt" - `); - } -} diff --git a/server/src/migrations/1739824470990-AddMemoryShowHideDates.ts b/server/src/migrations/1739824470990-AddMemoryShowHideDates.ts deleted file mode 100644 index d53c7c17f6..0000000000 --- a/server/src/migrations/1739824470990-AddMemoryShowHideDates.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddMemoryShowHideDates1739824470990 implements MigrationInterface { - name = 'AddMemoryShowHideDates1739824470990' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "memories" ADD "showAt" TIMESTAMP WITH TIME ZONE`); - await queryRunner.query(`ALTER TABLE "memories" ADD "hideAt" TIMESTAMP WITH TIME ZONE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "memories" DROP COLUMN "hideAt"`); - await queryRunner.query(`ALTER TABLE "memories" DROP COLUMN "showAt"`); - } - -} diff --git a/server/src/migrations/1740001232576-AddSessionSyncCheckpointTable.ts b/server/src/migrations/1740001232576-AddSessionSyncCheckpointTable.ts deleted file mode 100644 index ef75dd7c0d..0000000000 --- a/server/src/migrations/1740001232576-AddSessionSyncCheckpointTable.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddSessionSyncCheckpointTable1740001232576 implements MigrationInterface { - name = 'AddSessionSyncCheckpointTable1740001232576' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "session_sync_checkpoints" ("sessionId" uuid NOT NULL, "type" character varying NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "ack" character varying NOT NULL, CONSTRAINT "PK_b846ab547a702863ef7cd9412fb" PRIMARY KEY ("sessionId", "type"))`); - await queryRunner.query(`ALTER TABLE "session_sync_checkpoints" ADD CONSTRAINT "FK_d8ddd9d687816cc490432b3d4bc" FOREIGN KEY ("sessionId") REFERENCES "sessions"("id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(` - create trigger session_sync_checkpoints_updated_at - before update on session_sync_checkpoints - for each row execute procedure updated_at() - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`drop trigger session_sync_checkpoints_updated_at on session_sync_checkpoints`); - await queryRunner.query(`ALTER TABLE "session_sync_checkpoints" DROP CONSTRAINT "FK_d8ddd9d687816cc490432b3d4bc"`); - await queryRunner.query(`DROP TABLE "session_sync_checkpoints"`); - } - -} diff --git a/server/src/migrations/1740064899123-AddUsersAuditTable.ts b/server/src/migrations/1740064899123-AddUsersAuditTable.ts deleted file mode 100644 index b8f2ce5e3a..0000000000 --- a/server/src/migrations/1740064899123-AddUsersAuditTable.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddUsersAuditTable1740064899123 implements MigrationInterface { - name = 'AddUsersAuditTable1740064899123' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX IF NOT EXISTS "IDX_users_updated_at_asc_id_asc" ON "users" ("updatedAt" ASC, "id" ASC);`) - await queryRunner.query(`CREATE TABLE "users_audit" ("id" SERIAL NOT NULL, "userId" uuid NOT NULL, "deletedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "PK_e9b2bdfd90e7eb5961091175180" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX IF NOT EXISTS "IDX_users_audit_deleted_at_asc_user_id_asc" ON "users_audit" ("deletedAt" ASC, "userId" ASC);`) - await queryRunner.query(`CREATE OR REPLACE FUNCTION users_delete_audit() RETURNS TRIGGER AS - $$ - BEGIN - INSERT INTO users_audit ("userId") - SELECT "id" - FROM OLD; - RETURN NULL; - END; - $$ LANGUAGE plpgsql` - ); - await queryRunner.query(`CREATE OR REPLACE TRIGGER users_delete_audit - AFTER DELETE ON users - REFERENCING OLD TABLE AS OLD - FOR EACH STATEMENT - EXECUTE FUNCTION users_delete_audit(); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TRIGGER users_delete_audit`); - await queryRunner.query(`DROP FUNCTION users_delete_audit`); - await queryRunner.query(`DROP TABLE "users_audit"`); - } - -} diff --git a/server/src/migrations/1740586617223-AddUpdateIdColumns.ts b/server/src/migrations/1740586617223-AddUpdateIdColumns.ts deleted file mode 100644 index 02d680ddf6..0000000000 --- a/server/src/migrations/1740586617223-AddUpdateIdColumns.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddUpdateIdColumns1740586617223 implements MigrationInterface { - name = 'AddUpdateIdColumns1740586617223' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - create or replace function immich_uuid_v7(p_timestamp timestamp with time zone default clock_timestamp()) - returns uuid - as $$ - select encode( - set_bit( - set_bit( - overlay(uuid_send(gen_random_uuid()) - placing substring(int8send(floor(extract(epoch from p_timestamp) * 1000)::bigint) from 3) - from 1 for 6 - ), - 52, 1 - ), - 53, 1 - ), - 'hex')::uuid; - $$ - language SQL - volatile; - `) - await queryRunner.query(` - CREATE OR REPLACE FUNCTION updated_at() RETURNS TRIGGER - LANGUAGE plpgsql - as $$ - BEGIN - return new; - END; - $$; - `) - await queryRunner.query(`ALTER TABLE "person" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "asset_files" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "libraries" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "tags" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "assets" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "users" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "albums" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "sessions" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "session_sync_checkpoints" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "partners" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "memories" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "api_keys" ADD "updateId" uuid`); - await queryRunner.query(`ALTER TABLE "activity" ADD "updateId" uuid`); - - await queryRunner.query(`UPDATE "person" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "asset_files" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "libraries" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "tags" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "assets" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "users" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "albums" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "sessions" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "session_sync_checkpoints" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "partners" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "memories" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "api_keys" SET "updateId" = immich_uuid_v7("updatedAt")`); - await queryRunner.query(`UPDATE "activity" SET "updateId" = immich_uuid_v7("updatedAt")`); - - await queryRunner.query(`ALTER TABLE "person" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "asset_files" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "libraries" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "tags" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "albums" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "sessions" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "session_sync_checkpoints" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "partners" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "memories" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "api_keys" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "activity" ALTER COLUMN "updateId" SET NOT NULL, ALTER COLUMN "updateId" SET DEFAULT immich_uuid_v7()`); - - await queryRunner.query(`CREATE INDEX "IDX_person_update_id" ON "person" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_asset_files_update_id" ON "asset_files" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_libraries_update_id" ON "libraries" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_tags_update_id" ON "tags" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_assets_update_id" ON "assets" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_users_update_id" ON "users" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_albums_update_id" ON "albums" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_sessions_update_id" ON "sessions" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_session_sync_checkpoints_update_id" ON "session_sync_checkpoints" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_partners_update_id" ON "partners" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_memories_update_id" ON "memories" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_api_keys_update_id" ON "api_keys" ("updateId")`); - await queryRunner.query(`CREATE INDEX "IDX_activity_update_id" ON "activity" ("updateId")`); - - await queryRunner.query(` - CREATE OR REPLACE FUNCTION updated_at() RETURNS TRIGGER - LANGUAGE plpgsql - as $$ - DECLARE - clock_timestamp TIMESTAMP := clock_timestamp(); - BEGIN - new."updatedAt" = clock_timestamp; - new."updateId" = immich_uuid_v7(clock_timestamp); - return new; - END; - $$; - `) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "activity" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "api_keys" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "memories" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "partners" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "session_sync_checkpoints" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "sessions" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "albums" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "tags" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "libraries" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "asset_files" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "person" DROP COLUMN "updateId"`); - await queryRunner.query(`DROP FUNCTION immich_uuid_v7`); - await queryRunner.query(` - CREATE OR REPLACE FUNCTION updated_at() RETURNS TRIGGER - LANGUAGE plpgsql - as $$ - BEGIN - new."updatedAt" = now(); - return new; - END; - $$; - `) - } - -} diff --git a/server/src/migrations/1740595460866-UsersAuditUuidv7PrimaryKey.ts b/server/src/migrations/1740595460866-UsersAuditUuidv7PrimaryKey.ts deleted file mode 100644 index 59fc4dbd5b..0000000000 --- a/server/src/migrations/1740595460866-UsersAuditUuidv7PrimaryKey.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class UsersAuditUuidv7PrimaryKey1740595460866 implements MigrationInterface { - name = 'UsersAuditUuidv7PrimaryKey1740595460866' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_users_audit_deleted_at_asc_user_id_asc"`); - await queryRunner.query(`ALTER TABLE "users_audit" DROP CONSTRAINT "PK_e9b2bdfd90e7eb5961091175180"`); - await queryRunner.query(`ALTER TABLE "users_audit" DROP COLUMN "id"`); - await queryRunner.query(`ALTER TABLE "users_audit" ADD "id" uuid NOT NULL DEFAULT immich_uuid_v7()`); - await queryRunner.query(`ALTER TABLE "users_audit" ADD CONSTRAINT "PK_e9b2bdfd90e7eb5961091175180" PRIMARY KEY ("id")`); - await queryRunner.query(`ALTER TABLE "users_audit" ALTER COLUMN "deletedAt" SET DEFAULT clock_timestamp()`) - await queryRunner.query(`CREATE INDEX "IDX_users_audit_deleted_at" ON "users_audit" ("deletedAt")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_users_audit_deleted_at"`); - await queryRunner.query(`ALTER TABLE "users_audit" DROP CONSTRAINT "PK_e9b2bdfd90e7eb5961091175180"`); - await queryRunner.query(`ALTER TABLE "users_audit" DROP COLUMN "id"`); - await queryRunner.query(`ALTER TABLE "users_audit" ADD "id" SERIAL NOT NULL`); - await queryRunner.query(`ALTER TABLE "users_audit" ADD CONSTRAINT "PK_e9b2bdfd90e7eb5961091175180" PRIMARY KEY ("id")`); - await queryRunner.query(`ALTER TABLE "users_audit" ALTER COLUMN "deletedAt" SET DEFAULT now()`); - await queryRunner.query(`CREATE INDEX "IDX_users_audit_deleted_at_asc_user_id_asc" ON "users_audit" ("userId", "deletedAt") `); - } - -} diff --git a/server/src/migrations/1740619600996-AddManualSourceType.ts b/server/src/migrations/1740619600996-AddManualSourceType.ts deleted file mode 100644 index dd53312ad7..0000000000 --- a/server/src/migrations/1740619600996-AddManualSourceType.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddManualSourceType1740619600996 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TYPE sourceType ADD VALUE 'manual'`); - } - - public async down(queryRunner: QueryRunner): Promise { - // Prior to this migration, manually tagged pictures had the 'machine-learning' type - await queryRunner.query( - `UPDATE "asset_faces" SET "sourceType" = 'machine-learning' WHERE "sourceType" = 'manual';`, - ); - - // Postgres doesn't allow removing values from enums, we have to recreate the type - await queryRunner.query(`ALTER TYPE sourceType RENAME TO oldSourceType`); - await queryRunner.query(`CREATE TYPE sourceType AS ENUM ('machine-learning', 'exif');`); - - await queryRunner.query(`ALTER TABLE "asset_faces" ALTER COLUMN "sourceType" DROP DEFAULT;`); - await queryRunner.query( - `ALTER TABLE "asset_faces" ALTER COLUMN "sourceType" TYPE sourceType USING "sourceType"::text::sourceType;`, - ); - await queryRunner.query( - `ALTER TABLE "asset_faces" ALTER COLUMN "sourceType" SET DEFAULT 'machine-learning'::sourceType;`, - ); - await queryRunner.query(`DROP TYPE oldSourceType;`); - } -} diff --git a/server/src/migrations/1740654480319-UnsetStackedAssetsFromDuplicateStatus.ts b/server/src/migrations/1740654480319-UnsetStackedAssetsFromDuplicateStatus.ts deleted file mode 100644 index 5c735a60bb..0000000000 --- a/server/src/migrations/1740654480319-UnsetStackedAssetsFromDuplicateStatus.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UnsetStackedAssetsFromDuplicateStatus1740654480319 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - update assets - set "duplicateId" = null - where "stackId" is not null`); - } - - public async down(): Promise { - // No need to revert this migration - } -} diff --git a/server/src/migrations/1740739778549-CreatePartnersAuditTable.ts b/server/src/migrations/1740739778549-CreatePartnersAuditTable.ts deleted file mode 100644 index d9c9dc1949..0000000000 --- a/server/src/migrations/1740739778549-CreatePartnersAuditTable.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class CreatePartnersAuditTable1740739778549 implements MigrationInterface { - name = 'CreatePartnersAuditTable1740739778549' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "partners_audit" ("id" uuid NOT NULL DEFAULT immich_uuid_v7(), "sharedById" uuid NOT NULL, "sharedWithId" uuid NOT NULL, "deletedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT clock_timestamp(), CONSTRAINT "PK_952b50217ff78198a7e380f0359" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_partners_audit_shared_by_id" ON "partners_audit" ("sharedById") `); - await queryRunner.query(`CREATE INDEX "IDX_partners_audit_shared_with_id" ON "partners_audit" ("sharedWithId") `); - await queryRunner.query(`CREATE INDEX "IDX_partners_audit_deleted_at" ON "partners_audit" ("deletedAt") `); - await queryRunner.query(`CREATE OR REPLACE FUNCTION partners_delete_audit() RETURNS TRIGGER AS - $$ - BEGIN - INSERT INTO partners_audit ("sharedById", "sharedWithId") - SELECT "sharedById", "sharedWithId" - FROM OLD; - RETURN NULL; - END; - $$ LANGUAGE plpgsql` - ); - await queryRunner.query(`CREATE OR REPLACE TRIGGER partners_delete_audit - AFTER DELETE ON partners - REFERENCING OLD TABLE AS OLD - FOR EACH STATEMENT - EXECUTE FUNCTION partners_delete_audit(); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "public"."IDX_partners_audit_deleted_at"`); - await queryRunner.query(`DROP INDEX "public"."IDX_partners_audit_shared_with_id"`); - await queryRunner.query(`DROP INDEX "public"."IDX_partners_audit_shared_by_id"`); - await queryRunner.query(`DROP TRIGGER partners_delete_audit`); - await queryRunner.query(`DROP FUNCTION partners_delete_audit`); - await queryRunner.query(`DROP TABLE "partners_audit"`); - } - -} diff --git a/server/src/migrations/1741027685381-ResetMemories.ts b/server/src/migrations/1741027685381-ResetMemories.ts deleted file mode 100644 index 6a80372219..0000000000 --- a/server/src/migrations/1741027685381-ResetMemories.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class ResetMemories1741027685381 implements MigrationInterface { - name = 'ResetMemories1741027685381'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DELETE FROM "memories"`); - await queryRunner.query(`DELETE FROM "system_metadata" WHERE "key" = 'memories-state'`); - } - - public async down(): Promise { - // nothing to do - } -} diff --git a/server/src/migrations/1741179334403-MoveHistoryUuidEntityId.ts b/server/src/migrations/1741179334403-MoveHistoryUuidEntityId.ts deleted file mode 100644 index 449272341c..0000000000 --- a/server/src/migrations/1741179334403-MoveHistoryUuidEntityId.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class MoveHistoryUuidEntityId1741179334403 implements MigrationInterface { - name = 'MoveHistoryUuidEntityId1741179334403'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "move_history" ALTER COLUMN "entityId" TYPE uuid USING "entityId"::uuid;`); - await queryRunner.query(`delete from "move_history" - where - "move_history"."entityId" not in ( - select - "id" - from - "assets" - where - "assets"."id" = "move_history"."entityId" - ) - and "move_history"."pathType" = 'original' - `) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "move_history" ALTER COLUMN "entityId" TYPE character varying`); - } -} - diff --git a/server/src/migrations/1741191762113-AssetAuditTable.ts b/server/src/migrations/1741191762113-AssetAuditTable.ts deleted file mode 100644 index c02408c384..0000000000 --- a/server/src/migrations/1741191762113-AssetAuditTable.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AssetAuditTable1741191762113 implements MigrationInterface { - name = 'AssetAuditTable1741191762113' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE "assets_audit" ("id" uuid NOT NULL DEFAULT immich_uuid_v7(), "assetId" uuid NOT NULL, "ownerId" uuid NOT NULL, "deletedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT clock_timestamp(), CONSTRAINT "PK_99bd5c015f81a641927a32b4212" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_assets_audit_asset_id" ON "assets_audit" ("assetId") `); - await queryRunner.query(`CREATE INDEX "IDX_assets_audit_owner_id" ON "assets_audit" ("ownerId") `); - await queryRunner.query(`CREATE INDEX "IDX_assets_audit_deleted_at" ON "assets_audit" ("deletedAt") `); - await queryRunner.query(`CREATE OR REPLACE FUNCTION assets_delete_audit() RETURNS TRIGGER AS - $$ - BEGIN - INSERT INTO assets_audit ("assetId", "ownerId") - SELECT "id", "ownerId" - FROM OLD; - RETURN NULL; - END; - $$ LANGUAGE plpgsql` - ); - await queryRunner.query(`CREATE OR REPLACE TRIGGER assets_delete_audit - AFTER DELETE ON assets - REFERENCING OLD TABLE AS OLD - FOR EACH STATEMENT - EXECUTE FUNCTION assets_delete_audit(); - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TRIGGER assets_delete_audit`); - await queryRunner.query(`DROP FUNCTION assets_delete_audit`); - await queryRunner.query(`DROP INDEX "IDX_assets_audit_deleted_at"`); - await queryRunner.query(`DROP INDEX "IDX_assets_audit_owner_id"`); - await queryRunner.query(`DROP INDEX "IDX_assets_audit_asset_id"`); - await queryRunner.query(`DROP TABLE "assets_audit"`); - } -} diff --git a/server/src/migrations/1741280328985-FixAssetAndUserCascadeConditions.ts b/server/src/migrations/1741280328985-FixAssetAndUserCascadeConditions.ts deleted file mode 100644 index 20215c1b59..0000000000 --- a/server/src/migrations/1741280328985-FixAssetAndUserCascadeConditions.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class FixAssetAndUserCascadeConditions1741280328985 implements MigrationInterface { - name = 'FixAssetAndUserCascadeConditions1741280328985'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - CREATE OR REPLACE TRIGGER assets_delete_audit - AFTER DELETE ON assets - REFERENCING OLD TABLE AS OLD - FOR EACH STATEMENT - WHEN (pg_trigger_depth() = 0) - EXECUTE FUNCTION assets_delete_audit();`); - await queryRunner.query(` - CREATE OR REPLACE TRIGGER users_delete_audit - AFTER DELETE ON users - REFERENCING OLD TABLE AS OLD - FOR EACH STATEMENT - WHEN (pg_trigger_depth() = 0) - EXECUTE FUNCTION users_delete_audit();`); - await queryRunner.query(` - CREATE OR REPLACE TRIGGER partners_delete_audit - AFTER DELETE ON partners - REFERENCING OLD TABLE AS OLD - FOR EACH STATEMENT - WHEN (pg_trigger_depth() = 0) - EXECUTE FUNCTION partners_delete_audit();`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - CREATE OR REPLACE TRIGGER assets_delete_audit - AFTER DELETE ON assets - REFERENCING OLD TABLE AS OLD - FOR EACH STATEMENT - EXECUTE FUNCTION assets_delete_audit();`); - await queryRunner.query(` - CREATE OR REPLACE TRIGGER users_delete_audit - AFTER DELETE ON users - REFERENCING OLD TABLE AS OLD - FOR EACH STATEMENT - EXECUTE FUNCTION users_delete_audit();`); - await queryRunner.query(` - CREATE OR REPLACE TRIGGER partners_delete_audit - AFTER DELETE ON partners - REFERENCING OLD TABLE AS OLD - FOR EACH STATEMENT - EXECUTE FUNCTION partners_delete_audit();`); - } -} diff --git a/server/src/migrations/1741281344519-AddExifUpdateId.ts b/server/src/migrations/1741281344519-AddExifUpdateId.ts deleted file mode 100644 index eb32836a1d..0000000000 --- a/server/src/migrations/1741281344519-AddExifUpdateId.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddExifUpdateId1741281344519 implements MigrationInterface { - name = 'AddExifUpdateId1741281344519'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "exif" ADD "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT clock_timestamp()`, - ); - await queryRunner.query(`ALTER TABLE "exif" ADD "updateId" uuid NOT NULL DEFAULT immich_uuid_v7()`); - await queryRunner.query(`CREATE INDEX "IDX_asset_exif_update_id" ON "exif" ("updateId") `); - await queryRunner.query(` - create trigger asset_exif_updated_at - before update on exif - for each row execute procedure updated_at() - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "public"."IDX_asset_exif_update_id"`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "updateId"`); - await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "updatedAt"`); - await queryRunner.query(`DROP TRIGGER asset_exif_updated_at on exif`); - } -} diff --git a/server/src/migrations/1743595393000-TableCleanup.ts b/server/src/migrations/1743595393000-TableCleanup.ts deleted file mode 100644 index adf9c65afa..0000000000 --- a/server/src/migrations/1743595393000-TableCleanup.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class TableCleanup1743595393000 implements MigrationInterface { - name = 'TableCleanup1743595393000'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE IF EXISTS "system_config"`); - await queryRunner.query(`DROP TABLE IF EXISTS "socket_io_attachments"`); - } - - public async down(): Promise {} -} diff --git a/server/src/migrations/1743611339000-GeodataCleanup.ts b/server/src/migrations/1743611339000-GeodataCleanup.ts deleted file mode 100644 index 0e25a1268e..0000000000 --- a/server/src/migrations/1743611339000-GeodataCleanup.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class GeodataCleanup1743611339000 implements MigrationInterface { - name = 'GeodataCleanup1743611339000'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER INDEX IF EXISTS "idx_geodata_places_admin2_alternate_names" RENAME TO "idx_geodata_places_alternate_names"`, - ); - await queryRunner.query(`DROP TABLE IF EXISTS "geodata_places_tmp"`); - await queryRunner.query(`DROP TABLE IF EXISTS "naturalearth_countries_tmp"`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER INDEX IF EXISTS "idx_geodata_places_alternate_names" RENAME TO "idx_geodata_places_admin2_alternate_names"`, - ); - } -} diff --git a/server/src/migrations/1744662638410-MakeFileMetadataNonNullable.ts b/server/src/migrations/1744662638410-MakeFileMetadataNonNullable.ts deleted file mode 100644 index 1ba4df01cd..0000000000 --- a/server/src/migrations/1744662638410-MakeFileMetadataNonNullable.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class MakeFileMetadataNonNullable1744662638410 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `DELETE FROM assets WHERE "fileCreatedAt" IS NULL OR "fileModifiedAt" IS NULL OR "localDateTime" IS NULL`, - ); - await queryRunner.query(` - ALTER TABLE assets - ALTER COLUMN "fileCreatedAt" SET NOT NULL, - ALTER COLUMN "fileModifiedAt" SET NOT NULL, - ALTER COLUMN "localDateTime" SET NOT NULL`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE assets - ALTER COLUMN "fileCreatedAt" DROP NOT NULL, - ALTER COLUMN "fileModifiedAt" DROP NOT NULL, - ALTER COLUMN "localDateTime" DROP NOT NULL`); - } -} diff --git a/server/src/migrations/1744900200559-AddForeignKeyIndexes.ts b/server/src/migrations/1744900200559-AddForeignKeyIndexes.ts deleted file mode 100644 index db351d5bab..0000000000 --- a/server/src/migrations/1744900200559-AddForeignKeyIndexes.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddForeignKeyIndexes1744900200559 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE INDEX "IDX_0f6fc2fb195f24d19b0fb0d57c" ON "libraries" ("ownerId")`); - await queryRunner.query(`CREATE INDEX "IDX_91704e101438fd0653f582426d" ON "asset_stack" ("primaryAssetId")`); - await queryRunner.query(`CREATE INDEX "IDX_c05079e542fd74de3b5ecb5c1c" ON "asset_stack" ("ownerId")`); - await queryRunner.query(`CREATE INDEX "IDX_2c5ac0d6fb58b238fd2068de67" ON "assets" ("ownerId")`); - await queryRunner.query(`CREATE INDEX "IDX_16294b83fa8c0149719a1f631e" ON "assets" ("livePhotoVideoId")`); - await queryRunner.query(`CREATE INDEX "IDX_9977c3c1de01c3d848039a6b90" ON "assets" ("libraryId")`); - await queryRunner.query(`CREATE INDEX "IDX_f15d48fa3ea5e4bda05ca8ab20" ON "assets" ("stackId")`); - await queryRunner.query(`CREATE INDEX "IDX_b22c53f35ef20c28c21637c85f" ON "albums" ("ownerId")`); - await queryRunner.query(`CREATE INDEX "IDX_05895aa505a670300d4816debc" ON "albums" ("albumThumbnailAssetId")`); - await queryRunner.query(`CREATE INDEX "IDX_1af8519996fbfb3684b58df280" ON "activity" ("albumId")`); - await queryRunner.query(`CREATE INDEX "IDX_3571467bcbe021f66e2bdce96e" ON "activity" ("userId")`); - await queryRunner.query(`CREATE INDEX "IDX_8091ea76b12338cb4428d33d78" ON "activity" ("assetId")`); - await queryRunner.query(`CREATE INDEX "IDX_6c2e267ae764a9413b863a2934" ON "api_keys" ("userId")`); - await queryRunner.query(`CREATE INDEX "IDX_5527cc99f530a547093f9e577b" ON "person" ("ownerId")`); - await queryRunner.query(`CREATE INDEX "IDX_2bbabe31656b6778c6b87b6102" ON "person" ("faceAssetId")`); - await queryRunner.query(`CREATE INDEX "IDX_575842846f0c28fa5da46c99b1" ON "memories" ("ownerId")`); - await queryRunner.query(`CREATE INDEX "IDX_d7e875c6c60e661723dbf372fd" ON "partners" ("sharedWithId")`); - await queryRunner.query(`CREATE INDEX "IDX_57de40bc620f456c7311aa3a1e" ON "sessions" ("userId")`); - await queryRunner.query(`CREATE INDEX "IDX_66fe3837414c5a9f1c33ca4934" ON "shared_links" ("userId")`); - await queryRunner.query(`CREATE INDEX "IDX_d8ddd9d687816cc490432b3d4b" ON "session_sync_checkpoints" ("sessionId")`); - await queryRunner.query(`CREATE INDEX "IDX_9f9590cc11561f1f48ff034ef9" ON "tags" ("parentId")`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_66fe3837414c5a9f1c33ca4934";`); - await queryRunner.query(`DROP INDEX "IDX_91704e101438fd0653f582426d";`); - await queryRunner.query(`DROP INDEX "IDX_c05079e542fd74de3b5ecb5c1c";`); - await queryRunner.query(`DROP INDEX "IDX_5527cc99f530a547093f9e577b";`); - await queryRunner.query(`DROP INDEX "IDX_2bbabe31656b6778c6b87b6102";`); - await queryRunner.query(`DROP INDEX "IDX_0f6fc2fb195f24d19b0fb0d57c";`); - await queryRunner.query(`DROP INDEX "IDX_9f9590cc11561f1f48ff034ef9";`); - await queryRunner.query(`DROP INDEX "IDX_2c5ac0d6fb58b238fd2068de67";`); - await queryRunner.query(`DROP INDEX "IDX_16294b83fa8c0149719a1f631e";`); - await queryRunner.query(`DROP INDEX "IDX_9977c3c1de01c3d848039a6b90";`); - await queryRunner.query(`DROP INDEX "IDX_f15d48fa3ea5e4bda05ca8ab20";`); - await queryRunner.query(`DROP INDEX "IDX_b22c53f35ef20c28c21637c85f";`); - await queryRunner.query(`DROP INDEX "IDX_05895aa505a670300d4816debc";`); - await queryRunner.query(`DROP INDEX "IDX_57de40bc620f456c7311aa3a1e";`); - await queryRunner.query(`DROP INDEX "IDX_d8ddd9d687816cc490432b3d4b";`); - await queryRunner.query(`DROP INDEX "IDX_d7e875c6c60e661723dbf372fd";`); - await queryRunner.query(`DROP INDEX "IDX_575842846f0c28fa5da46c99b1";`); - await queryRunner.query(`DROP INDEX "IDX_6c2e267ae764a9413b863a2934";`); - await queryRunner.query(`DROP INDEX "IDX_1af8519996fbfb3684b58df280";`); - await queryRunner.query(`DROP INDEX "IDX_3571467bcbe021f66e2bdce96e";`); - await queryRunner.query(`DROP INDEX "IDX_8091ea76b12338cb4428d33d78";`); - } -} diff --git a/server/src/migrations/1744910873956-AddMissingIndex.ts b/server/src/migrations/1744910873956-AddMissingIndex.ts deleted file mode 100644 index 38dd6f4958..0000000000 --- a/server/src/migrations/1744910873956-AddMissingIndex.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddMissingIndex1744910873956 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE INDEX IF NOT EXISTS "IDX_geodata_gist_earthcoord" ON "geodata_places" (ll_to_earth_public(latitude, longitude))`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "IDX_geodata_gist_earthcoord";`); - } -} diff --git a/server/src/queries/asset.repository.sql b/server/src/queries/asset.repository.sql index e482a38a9a..f7a4d1402d 100644 --- a/server/src/queries/asset.repository.sql +++ b/server/src/queries/asset.repository.sql @@ -168,6 +168,15 @@ from where "livePhotoVideoId" = $1::uuid +-- AssetRepository.getFileSamples +select + "assetId", + "path" +from + "asset_file" +limit + 3 + -- AssetRepository.getById select "asset".* diff --git a/server/src/queries/person.repository.sql b/server/src/queries/person.repository.sql index 3e41edde9c..8ad5b96bbc 100644 --- a/server/src/queries/person.repository.sql +++ b/server/src/queries/person.repository.sql @@ -12,6 +12,17 @@ delete from "person" where "person"."id" in ($1) +-- PersonRepository.getFileSamples +select + "id", + "thumbnailPath" +from + "person" +where + "thumbnailPath" != '' +limit + 3 + -- PersonRepository.getAllForUser select "person".* diff --git a/server/src/queries/search.repository.sql b/server/src/queries/search.repository.sql index ef5363126f..be2245a74e 100644 --- a/server/src/queries/search.repository.sql +++ b/server/src/queries/search.repository.sql @@ -77,6 +77,27 @@ union all limit $15 +-- SearchRepository.searchLargeAssets +select + "asset".*, + to_json("asset_exif") as "exifInfo" +from + "asset" + inner join "asset_exif" on "asset"."id" = "asset_exif"."assetId" + left join "asset_exif" on "asset"."id" = "asset_exif"."assetId" +where + "asset"."visibility" = $1 + and "asset"."fileCreatedAt" >= $2 + and "asset_exif"."lensModel" = $3 + and "asset"."ownerId" = any ($4::uuid[]) + and "asset"."isFavorite" = $5 + and "asset"."deletedAt" is null + and "asset_exif"."fileSizeInByte" > $6 +order by + "asset_exif"."fileSizeInByte" desc +limit + $7 + -- SearchRepository.searchSmart begin set diff --git a/server/src/queries/shared.link.repository.sql b/server/src/queries/shared.link.repository.sql index ed3507fa2f..0e13b98b5d 100644 --- a/server/src/queries/shared.link.repository.sql +++ b/server/src/queries/shared.link.repository.sql @@ -188,9 +188,47 @@ from "shared_link" left join "album" on "album"."id" = "shared_link"."albumId" where - "shared_link"."key" = $1 - and "album"."deletedAt" is null + "album"."deletedAt" is null and ( - "shared_link"."type" = $2 + "shared_link"."type" = $1 or "album"."id" is not null ) + and "shared_link"."key" = $2 + +-- SharedLinkRepository.getBySlug +select + "shared_link"."id", + "shared_link"."userId", + "shared_link"."expiresAt", + "shared_link"."showExif", + "shared_link"."allowUpload", + "shared_link"."allowDownload", + "shared_link"."password", + ( + select + to_json(obj) + from + ( + select + "user"."id", + "user"."name", + "user"."email", + "user"."isAdmin", + "user"."quotaUsageInBytes", + "user"."quotaSizeInBytes" + from + "user" + where + "user"."id" = "shared_link"."userId" + ) as obj + ) as "user" +from + "shared_link" + left join "album" on "album"."id" = "shared_link"."albumId" +where + "album"."deletedAt" is null + and ( + "shared_link"."type" = $1 + or "album"."id" is not null + ) + and "shared_link"."slug" = $2 diff --git a/server/src/queries/stack.repository.sql b/server/src/queries/stack.repository.sql index a256cdfc76..94a24f69e4 100644 --- a/server/src/queries/stack.repository.sql +++ b/server/src/queries/stack.repository.sql @@ -143,3 +143,13 @@ from "stack" where "id" = $1::uuid + +-- StackRepository.getForAssetRemoval +select + "stackId" as "id", + "stack"."primaryAssetId" +from + "asset" + left join "stack" on "stack"."id" = "asset"."stackId" +where + "asset"."id" = $1 diff --git a/server/src/queries/sync.repository.sql b/server/src/queries/sync.repository.sql index 4782eedf1d..28c6f32acc 100644 --- a/server/src/queries/sync.repository.sql +++ b/server/src/queries/sync.repository.sql @@ -66,6 +66,7 @@ select "asset"."duration", "asset"."livePhotoVideoId", "asset"."stackId", + "asset"."libraryId", "asset"."updateId" from "asset" @@ -95,6 +96,7 @@ select "asset"."duration", "asset"."livePhotoVideoId", "asset"."stackId", + "asset"."libraryId", "asset"."updateId" from "asset" @@ -357,6 +359,7 @@ select "asset"."duration", "asset"."livePhotoVideoId", "asset"."stackId", + "asset"."libraryId", "asset"."updateId" from "asset" @@ -409,6 +412,64 @@ where order by "updateId" asc +-- SyncRepository.assetFace.getDeletes +select + "asset_face_audit"."id", + "assetFaceId" +from + "asset_face_audit" + left join "asset" on "asset"."id" = "asset_face_audit"."assetId" +where + "asset"."ownerId" = $1 + and "asset_face_audit"."deletedAt" < now() - interval '1 millisecond' +order by + "asset_face_audit"."id" asc + +-- SyncRepository.assetFace.getUpserts +select + "asset_face"."id", + "assetId", + "personId", + "imageWidth", + "imageHeight", + "boundingBoxX1", + "boundingBoxY1", + "boundingBoxX2", + "boundingBoxY2", + "sourceType", + "asset_face"."updateId" +from + "asset_face" + left join "asset" on "asset"."id" = "asset_face"."assetId" +where + "asset_face"."updatedAt" < now() - interval '1 millisecond' + and "asset"."ownerId" = $1 +order by + "asset_face"."updateId" asc + +-- SyncRepository.authUser.getUpserts +select + "id", + "name", + "email", + "avatarColor", + "deletedAt", + "updateId", + "profileImagePath", + "profileChangedAt", + "isAdmin", + "pinCode", + "oauthId", + "storageLabel", + "quotaSizeInBytes", + "quotaUsageInBytes" +from + "user" +where + "updatedAt" < now() - interval '1 millisecond' +order by + "updateId" asc + -- SyncRepository.memory.getDeletes select "id", @@ -547,6 +608,7 @@ select "asset"."duration", "asset"."livePhotoVideoId", "asset"."stackId", + "asset"."libraryId", "asset"."updateId" from "asset" @@ -594,6 +656,7 @@ select "asset"."duration", "asset"."livePhotoVideoId", "asset"."stackId", + "asset"."libraryId", "asset"."updateId" from "asset" @@ -779,7 +842,6 @@ select "ownerId", "name", "birthDate", - "thumbnailPath", "isHidden", "isFavorite", "color", @@ -837,8 +899,11 @@ select "id", "name", "email", + "avatarColor", "deletedAt", - "updateId" + "updateId", + "profileImagePath", + "profileChangedAt" from "user" where diff --git a/server/src/queries/user.repository.sql b/server/src/queries/user.repository.sql index f1809464bf..6a02654781 100644 --- a/server/src/queries/user.repository.sql +++ b/server/src/queries/user.repository.sql @@ -78,6 +78,17 @@ where "user"."isAdmin" = $1 and "user"."deletedAt" is null +-- UserRepository.getFileSamples +select + "id", + "profileImagePath" +from + "user" +where + "profileImagePath" != '' +limit + 3 + -- UserRepository.hasAdmin select "user"."id" diff --git a/server/src/repositories/asset.repository.ts b/server/src/repositories/asset.repository.ts index cb7e804f6f..8aa25c4a6a 100644 --- a/server/src/repositories/asset.repository.ts +++ b/server/src/repositories/asset.repository.ts @@ -335,6 +335,11 @@ export class AssetRepository { return count; } + @GenerateSql() + getFileSamples() { + return this.db.selectFrom('asset_file').select(['assetId', 'path']).limit(sql.lit(3)).execute(); + } + @GenerateSql({ params: [DummyValue.UUID] }) getById(id: string, { exifInfo, faces, files, library, owner, smartSearch, stack, tags }: GetByIdsRelations = {}) { return this.db diff --git a/server/src/repositories/config.repository.spec.ts b/server/src/repositories/config.repository.spec.ts index a096c6a4bc..99cba43b99 100644 --- a/server/src/repositories/config.repository.spec.ts +++ b/server/src/repositories/config.repository.spec.ts @@ -13,6 +13,7 @@ const resetEnv = () => { 'IMMICH_WORKERS_EXCLUDE', 'IMMICH_TRUSTED_PROXIES', 'IMMICH_API_METRICS_PORT', + 'IMMICH_MEDIA_LOCATION', 'IMMICH_MICROSERVICES_METRICS_PORT', 'IMMICH_TELEMETRY_INCLUDE', 'IMMICH_TELEMETRY_EXCLUDE', @@ -76,6 +77,13 @@ describe('getEnv', () => { }); }); + describe('IMMICH_MEDIA_LOCATION', () => { + it('should throw an error for relative paths', () => { + process.env.IMMICH_MEDIA_LOCATION = './relative/path'; + expect(() => getEnv()).toThrowError('IMMICH_MEDIA_LOCATION must be an absolute path'); + }); + }); + describe('database', () => { it('should use defaults', () => { const { database } = getEnv(); @@ -95,7 +103,7 @@ describe('getEnv', () => { it('should validate DB_SSL_MODE', () => { process.env.DB_SSL_MODE = 'invalid'; - expect(() => getEnv()).toThrowError('Invalid environment variables: DB_SSL_MODE'); + expect(() => getEnv()).toThrowError('DB_SSL_MODE must be one of the following values:'); }); it('should accept a valid DB_SSL_MODE', () => { @@ -239,7 +247,7 @@ describe('getEnv', () => { it('should reject invalid trusted proxies', () => { process.env.IMMICH_TRUSTED_PROXIES = '10.1'; - expect(() => getEnv()).toThrowError('Invalid environment variables: IMMICH_TRUSTED_PROXIES'); + expect(() => getEnv()).toThrow('IMMICH_TRUSTED_PROXIES must be an ip address, or ip address range'); }); }); diff --git a/server/src/repositories/config.repository.ts b/server/src/repositories/config.repository.ts index 7038338927..d5c279099c 100644 --- a/server/src/repositories/config.repository.ts +++ b/server/src/repositories/config.repository.ts @@ -97,6 +97,7 @@ export interface EnvData { storage: { ignoreMountCheckErrors: boolean; + mediaLocation?: string; }; workers: ImmichWorker[]; @@ -131,9 +132,11 @@ const getEnv = (): EnvData => { const dto = plainToInstance(EnvDto, process.env); const errors = validateSync(dto); if (errors.length > 0) { - throw new Error( - `Invalid environment variables: ${errors.map((error) => `${error.property}=${error.value}`).join(', ')}`, - ); + const messages = [`Invalid environment variables: `]; + for (const error of errors) { + messages.push(` - ${error.property}=${error.value} (${Object.values(error.constraints || {}).join(', ')})`); + } + throw new Error(messages.join('\n')); } const includedWorkers = asSet(dto.IMMICH_WORKERS_INCLUDE, [ImmichWorker.Api, ImmichWorker.Microservices]); @@ -305,6 +308,7 @@ const getEnv = (): EnvData => { storage: { ignoreMountCheckErrors: !!dto.IMMICH_IGNORE_MOUNT_CHECK_ERRORS, + mediaLocation: dto.IMMICH_MEDIA_LOCATION, }, telemetry: { diff --git a/server/src/repositories/database.repository.ts b/server/src/repositories/database.repository.ts index 8b5c728ce4..e5d88339c8 100644 --- a/server/src/repositories/database.repository.ts +++ b/server/src/repositories/database.repository.ts @@ -3,7 +3,7 @@ import AsyncLock from 'async-lock'; import { FileMigrationProvider, Kysely, Migrator, sql, Transaction } from 'kysely'; import { InjectKysely } from 'nestjs-kysely'; import { readdir } from 'node:fs/promises'; -import { join, resolve } from 'node:path'; +import { join } from 'node:path'; import semver from 'semver'; import { EXTENSION_NAMES, @@ -23,10 +23,9 @@ import { DB } from 'src/schema'; import { ExtensionVersion, VectorExtension, VectorUpdateResult } from 'src/types'; import { vectorIndexQuery } from 'src/utils/database'; import { isValidInteger } from 'src/validation'; -import { DataSource, QueryRunner } from 'typeorm'; export let cachedVectorExtension: VectorExtension | undefined; -export async function getVectorExtension(runner: Kysely | QueryRunner): Promise { +export async function getVectorExtension(runner: Kysely): Promise { if (cachedVectorExtension) { return cachedVectorExtension; } @@ -36,14 +35,8 @@ export async function getVectorExtension(runner: Kysely | QueryRunner): Prom return cachedVectorExtension; } - let availableExtensions: { name: VectorExtension }[]; const query = `SELECT name FROM pg_available_extensions WHERE name IN (${VECTOR_EXTENSIONS.map((ext) => `'${ext}'`).join(', ')})`; - if (runner instanceof Kysely) { - const { rows } = await sql.raw<{ name: VectorExtension }>(query).execute(runner); - availableExtensions = rows; - } else { - availableExtensions = (await runner.query(query)) as { name: VectorExtension }[]; - } + const { rows: availableExtensions } = await sql.raw<{ name: VectorExtension }>(query).execute(runner); const extensionNames = new Set(availableExtensions.map((row) => row.name)); cachedVectorExtension = VECTOR_EXTENSIONS.find((ext) => extensionNames.has(ext)); if (!cachedVectorExtension) { @@ -364,45 +357,9 @@ export class DatabaseRepository { return count; } - async runMigrations(options?: { transaction?: 'all' | 'none' | 'each' }): Promise { - const { database } = this.configRepository.getEnv(); + async runMigrations(): Promise { + this.logger.debug('Running migrations'); - this.logger.log('Running migrations, this may take a while'); - - const tableExists = sql<{ result: string | null }>`select to_regclass('migrations') as "result"`; - const { rows } = await tableExists.execute(this.db); - const hasTypeOrmMigrations = !!rows[0]?.result; - if (hasTypeOrmMigrations) { - // eslint-disable-next-line unicorn/prefer-module - const dist = resolve(`${__dirname}/..`); - - this.logger.debug('Running typeorm migrations'); - const dataSource = new DataSource({ - type: 'postgres', - entities: [], - subscribers: [], - migrations: [`${dist}/migrations` + '/*.{js,ts}'], - migrationsRun: false, - synchronize: false, - connectTimeoutMS: 10_000, // 10 seconds - parseInt8: true, - ...(database.config.connectionType === 'url' - ? { url: database.config.url } - : { - host: database.config.host, - port: database.config.port, - username: database.config.username, - password: database.config.password, - database: database.config.database, - }), - }); - await dataSource.initialize(); - await dataSource.runMigrations(options); - await dataSource.destroy(); - this.logger.debug('Finished running typeorm migrations'); - } - - this.logger.debug('Running kysely migrations'); const migrator = new Migrator({ db: this.db, migrationLockTableName: 'kysely_migrations_lock', @@ -429,11 +386,53 @@ export class DatabaseRepository { } if (error) { - this.logger.error(`Kysely migrations failed: ${error}`); + this.logger.error(`Migrations failed: ${error}`); throw error; } - this.logger.debug('Finished running kysely migrations'); + this.logger.debug('Finished running migrations'); + } + + async migrateFilePaths(sourceFolder: string, targetFolder: string): Promise { + // remove trailing slashes + if (sourceFolder.endsWith('/')) { + sourceFolder = sourceFolder.slice(0, -1); + } + + if (targetFolder.endsWith('/')) { + targetFolder = targetFolder.slice(0, -1); + } + + // escaping regex special characters with a backslash + const sourceRegex = '^' + sourceFolder.replaceAll(/[-[\]{}()*+?.,\\^$|#\s]/g, String.raw`\$&`); + const source = sql.raw(`'${sourceRegex}'`); + const target = sql.lit(targetFolder); + + await this.db.transaction().execute(async (tx) => { + await tx + .updateTable('asset') + .set((eb) => ({ + originalPath: eb.fn('REGEXP_REPLACE', ['originalPath', source, target]), + encodedVideoPath: eb.fn('REGEXP_REPLACE', ['encodedVideoPath', source, target]), + sidecarPath: eb.fn('REGEXP_REPLACE', ['sidecarPath', source, target]), + })) + .execute(); + + await tx + .updateTable('asset_file') + .set((eb) => ({ path: eb.fn('REGEXP_REPLACE', ['path', source, target]) })) + .execute(); + + await tx + .updateTable('person') + .set((eb) => ({ thumbnailPath: eb.fn('REGEXP_REPLACE', ['thumbnailPath', source, target]) })) + .execute(); + + await tx + .updateTable('user') + .set((eb) => ({ profileImagePath: eb.fn('REGEXP_REPLACE', ['profileImagePath', source, target]) })) + .execute(); + }); } async withLock(lock: DatabaseLock, callback: () => Promise): Promise { diff --git a/server/src/repositories/logging.repository.ts b/server/src/repositories/logging.repository.ts index 1833168f3e..939ecb718f 100644 --- a/server/src/repositories/logging.repository.ts +++ b/server/src/repositories/logging.repository.ts @@ -85,8 +85,13 @@ export class LoggingRepository { this.logger = new MyConsoleLogger(cls, { context: LoggingRepository.name, color: !noColor }); } - static create() { - return new LoggingRepository(undefined, undefined); + static create(context?: string) { + const logger = new LoggingRepository(undefined, undefined); + if (context) { + logger.setContext(context); + } + + return logger; } setAppName(name: string): void { diff --git a/server/src/repositories/map.repository.ts b/server/src/repositories/map.repository.ts index d1f60791c3..7f6e2a967a 100644 --- a/server/src/repositories/map.repository.ts +++ b/server/src/repositories/map.repository.ts @@ -5,7 +5,7 @@ import { InjectKysely } from 'nestjs-kysely'; import { createReadStream, existsSync } from 'node:fs'; import { readFile } from 'node:fs/promises'; import readLine from 'node:readline'; -import { citiesFile } from 'src/constants'; +import { citiesFile, reverseGeocodeMaxDistance } from 'src/constants'; import { DummyValue, GenerateSql } from 'src/decorators'; import { AssetVisibility, SystemMetadataKey } from 'src/enum'; import { ConfigRepository } from 'src/repositories/config.repository'; @@ -145,7 +145,7 @@ export class MapRepository { .selectFrom('geodata_places') .selectAll() .where( - sql`earth_box(ll_to_earth_public(${point.latitude}, ${point.longitude}), 25000)`, + sql`earth_box(ll_to_earth_public(${point.latitude}, ${point.longitude}), ${reverseGeocodeMaxDistance})`, '@>', sql`ll_to_earth_public(latitude, longitude)`, ) @@ -165,8 +165,8 @@ export class MapRepository { return { country, state, city }; } - this.logger.warn( - `Response from database for reverse geocoding latitude: ${point.latitude}, longitude: ${point.longitude} was null`, + this.logger.log( + `Empty response from database for city reverse geocoding lat: ${point.latitude}, lon: ${point.longitude}. Likely cause: no nearby large populated place (500+ within ${reverseGeocodeMaxDistance / 1000}km). Falling back to country boundaries.`, ); const ne_response = await this.db @@ -177,8 +177,8 @@ export class MapRepository { .executeTakeFirst(); if (!ne_response) { - this.logger.warn( - `Response from database for natural earth reverse geocoding latitude: ${point.latitude}, longitude: ${point.longitude} was null`, + this.logger.log( + `Empty response from database for natural earth country reverse geocoding lat: ${point.latitude}, lon: ${point.longitude}`, ); return { country: null, state: null, city: null }; diff --git a/server/src/repositories/person.repository.ts b/server/src/repositories/person.repository.ts index 5b7d1d3700..f653bb8179 100644 --- a/server/src/repositories/person.repository.ts +++ b/server/src/repositories/person.repository.ts @@ -142,6 +142,16 @@ export class PersonRepository { .stream(); } + @GenerateSql() + getFileSamples() { + return this.db + .selectFrom('person') + .select(['id', 'thumbnailPath']) + .where('thumbnailPath', '!=', sql.lit('')) + .limit(sql.lit(3)) + .execute(); + } + @GenerateSql({ params: [{ take: 1, skip: 0 }, DummyValue.UUID] }) async getAllForUser(pagination: PaginationOptions, userId: string, options?: PersonSearchOptions) { const items = await this.db diff --git a/server/src/repositories/search.repository.ts b/server/src/repositories/search.repository.ts index 61e0cc1e29..36ef7a27f1 100644 --- a/server/src/repositories/search.repository.ts +++ b/server/src/repositories/search.repository.ts @@ -8,7 +8,7 @@ import { AssetStatus, AssetType, AssetVisibility, VectorIndex } from 'src/enum'; import { probes } from 'src/repositories/database.repository'; import { DB } from 'src/schema'; import { AssetExifTable } from 'src/schema/tables/asset-exif.table'; -import { anyUuid, searchAssetBuilder } from 'src/utils/database'; +import { anyUuid, searchAssetBuilder, withExif } from 'src/utils/database'; import { paginationHelper } from 'src/utils/pagination'; import { isValidInteger } from 'src/validation'; @@ -129,6 +129,8 @@ export type SmartSearchOptions = SearchDateOptions & SearchPeopleOptions & SearchTagOptions; +export type LargeAssetSearchOptions = AssetSearchOptions & { minFileSize?: number }; + export interface FaceEmbeddingSearch extends SearchEmbeddingOptions { hasPerson?: boolean; numResults: number; @@ -237,6 +239,29 @@ export class SearchRepository { return rows; } + @GenerateSql({ + params: [ + 100, + { + takenAfter: DummyValue.DATE, + lensModel: DummyValue.STRING, + withStacked: true, + isFavorite: true, + userIds: [DummyValue.UUID], + }, + ], + }) + searchLargeAssets(size: number, options: LargeAssetSearchOptions) { + const orderDirection = (options.orderDirection?.toLowerCase() || 'desc') as OrderByDirection; + return searchAssetBuilder(this.db, options) + .selectAll('asset') + .$call(withExif) + .where('asset_exif.fileSizeInByte', '>', options.minFileSize || 0) + .orderBy('asset_exif.fileSizeInByte', orderDirection) + .limit(size) + .execute(); + } + @GenerateSql({ params: [ { page: 1, size: 200 }, diff --git a/server/src/repositories/shared-link.repository.ts b/server/src/repositories/shared-link.repository.ts index d5fb3be47d..54eab7c86f 100644 --- a/server/src/repositories/shared-link.repository.ts +++ b/server/src/repositories/shared-link.repository.ts @@ -173,10 +173,18 @@ export class SharedLinkRepository { } @GenerateSql({ params: [DummyValue.BUFFER] }) - async getByKey(key: Buffer) { + getByKey(key: Buffer) { + return this.authBuilder().where('shared_link.key', '=', key).executeTakeFirst(); + } + + @GenerateSql({ params: [DummyValue.BUFFER] }) + getBySlug(slug: string) { + return this.authBuilder().where('shared_link.slug', '=', slug).executeTakeFirst(); + } + + private authBuilder() { return this.db .selectFrom('shared_link') - .where('shared_link.key', '=', key) .leftJoin('album', 'album.id', 'shared_link.albumId') .where('album.deletedAt', 'is', null) .select((eb) => [ @@ -185,8 +193,7 @@ export class SharedLinkRepository { eb.selectFrom('user').select(columns.authUser).whereRef('user.id', '=', 'shared_link.userId'), ).as('user'), ]) - .where((eb) => eb.or([eb('shared_link.type', '=', SharedLinkType.Individual), eb('album.id', 'is not', null)])) - .executeTakeFirst(); + .where((eb) => eb.or([eb('shared_link.type', '=', SharedLinkType.Individual), eb('album.id', 'is not', null)])); } async create(entity: Insertable & { assetIds?: string[] }) { diff --git a/server/src/repositories/stack.repository.ts b/server/src/repositories/stack.repository.ts index fe16c8b5eb..ace9468177 100644 --- a/server/src/repositories/stack.repository.ts +++ b/server/src/repositories/stack.repository.ts @@ -152,4 +152,14 @@ export class StackRepository { .where('id', '=', asUuid(id)) .executeTakeFirst(); } + + @GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID] }) + getForAssetRemoval(assetId: string) { + return this.db + .selectFrom('asset') + .leftJoin('stack', 'stack.id', 'asset.stackId') + .select(['stackId as id', 'stack.primaryAssetId']) + .where('asset.id', '=', assetId) + .executeTakeFirst(); + } } diff --git a/server/src/repositories/storage.repository.ts b/server/src/repositories/storage.repository.ts index 4d89b02a50..7d6b634845 100644 --- a/server/src/repositories/storage.repository.ts +++ b/server/src/repositories/storage.repository.ts @@ -162,6 +162,10 @@ export class StorageRepository { } } + existsSync(filepath: string) { + return existsSync(filepath); + } + async checkDiskUsage(folder: string): Promise { const stats = await fs.statfs(folder); return { diff --git a/server/src/repositories/sync.repository.ts b/server/src/repositories/sync.repository.ts index 34c450d52d..d72ddcfc4d 100644 --- a/server/src/repositories/sync.repository.ts +++ b/server/src/repositories/sync.repository.ts @@ -17,7 +17,8 @@ type AuditTables = | 'memory_asset_audit' | 'stack_audit' | 'person_audit' - | 'user_metadata_audit'; + | 'user_metadata_audit' + | 'asset_face_audit'; type UpsertTables = | 'user' | 'partner' @@ -29,7 +30,8 @@ type UpsertTables = | 'memory_asset' | 'stack' | 'person' - | 'user_metadata'; + | 'user_metadata' + | 'asset_face'; @Injectable() export class SyncRepository { @@ -40,6 +42,8 @@ export class SyncRepository { albumUser: AlbumUserSync; asset: AssetSync; assetExif: AssetExifSync; + assetFace: AssetFaceSync; + authUser: AuthUserSync; memory: MemorySync; memoryToAsset: MemoryToAssetSync; partner: PartnerSync; @@ -59,6 +63,8 @@ export class SyncRepository { this.albumUser = new AlbumUserSync(this.db); this.asset = new AssetSync(this.db); this.assetExif = new AssetExifSync(this.db); + this.assetFace = new AssetFaceSync(this.db); + this.authUser = new AuthUserSync(this.db); this.memory = new MemorySync(this.db); this.memoryToAsset = new MemoryToAssetSync(this.db); this.partner = new PartnerSync(this.db); @@ -363,6 +369,18 @@ class AssetSync extends BaseSync { } } +class AuthUserSync extends BaseSync { + @GenerateSql({ params: [], stream: true }) + getUpserts(ack?: SyncAck) { + return this.db + .selectFrom('user') + .select(columns.syncUser) + .select(['isAdmin', 'pinCode', 'oauthId', 'storageLabel', 'quotaSizeInBytes', 'quotaUsageInBytes']) + .$call(this.upsertTableFilters(ack)) + .stream(); + } +} + class PersonSync extends BaseSync { @GenerateSql({ params: [DummyValue.UUID], stream: true }) getDeletes(userId: string, ack?: SyncAck) { @@ -385,7 +403,6 @@ class PersonSync extends BaseSync { 'ownerId', 'name', 'birthDate', - 'thumbnailPath', 'isHidden', 'isFavorite', 'color', @@ -398,6 +415,46 @@ class PersonSync extends BaseSync { } } +class AssetFaceSync extends BaseSync { + @GenerateSql({ params: [DummyValue.UUID], stream: true }) + getDeletes(userId: string, ack?: SyncAck) { + return this.db + .selectFrom('asset_face_audit') + .select(['asset_face_audit.id', 'assetFaceId']) + .orderBy('asset_face_audit.id', 'asc') + .leftJoin('asset', 'asset.id', 'asset_face_audit.assetId') + .where('asset.ownerId', '=', userId) + .where('asset_face_audit.deletedAt', '<', sql.raw("now() - interval '1 millisecond'")) + .$if(!!ack, (qb) => qb.where('asset_face_audit.id', '>', ack!.updateId)) + .stream(); + } + + @GenerateSql({ params: [DummyValue.UUID], stream: true }) + getUpserts(userId: string, ack?: SyncAck) { + return this.db + .selectFrom('asset_face') + .select([ + 'asset_face.id', + 'assetId', + 'personId', + 'imageWidth', + 'imageHeight', + 'boundingBoxX1', + 'boundingBoxY1', + 'boundingBoxX2', + 'boundingBoxY2', + 'sourceType', + 'asset_face.updateId', + ]) + .where('asset_face.updatedAt', '<', sql.raw("now() - interval '1 millisecond'")) + .$if(!!ack, (qb) => qb.where('asset_face.updateId', '>', ack!.updateId)) + .orderBy('asset_face.updateId', 'asc') + .leftJoin('asset', 'asset.id', 'asset_face.assetId') + .where('asset.ownerId', '=', userId) + .stream(); + } +} + class AssetExifSync extends BaseSync { @GenerateSql({ params: [DummyValue.UUID], stream: true }) getUpserts(userId: string, ack?: SyncAck) { @@ -650,11 +707,7 @@ class UserSync extends BaseSync { @GenerateSql({ params: [], stream: true }) getUpserts(ack?: SyncAck) { - return this.db - .selectFrom('user') - .select(['id', 'name', 'email', 'deletedAt', 'updateId']) - .$call(this.upsertTableFilters(ack)) - .stream(); + return this.db.selectFrom('user').select(columns.syncUser).$call(this.upsertTableFilters(ack)).stream(); } } diff --git a/server/src/repositories/user.repository.ts b/server/src/repositories/user.repository.ts index 715aa2dc32..9d5f19b26a 100644 --- a/server/src/repositories/user.repository.ts +++ b/server/src/repositories/user.repository.ts @@ -79,6 +79,16 @@ export class UserRepository { .executeTakeFirst(); } + @GenerateSql() + getFileSamples() { + return this.db + .selectFrom('user') + .select(['id', 'profileImagePath']) + .where('profileImagePath', '!=', sql.lit('')) + .limit(sql.lit(3)) + .execute(); + } + @GenerateSql() async hasAdmin(): Promise { const admin = await this.db diff --git a/server/src/schema/functions.ts b/server/src/schema/functions.ts index 5577169227..786e7a1ffa 100644 --- a/server/src/schema/functions.ts +++ b/server/src/schema/functions.ts @@ -229,3 +229,16 @@ export const user_metadata_audit = registerFunction({ RETURN NULL; END`, }); + +export const asset_face_audit = registerFunction({ + name: 'asset_face_audit', + returnType: 'TRIGGER', + language: 'PLPGSQL', + body: ` + BEGIN + INSERT INTO asset_face_audit ("assetFaceId", "assetId") + SELECT "id", "assetId" + FROM OLD; + RETURN NULL; + END`, +}); diff --git a/server/src/schema/index.ts b/server/src/schema/index.ts index ba25a65d4d..8982437b34 100644 --- a/server/src/schema/index.ts +++ b/server/src/schema/index.ts @@ -4,6 +4,7 @@ import { album_user_after_insert, album_user_delete_audit, asset_delete_audit, + asset_face_audit, f_concat_ws, f_unaccent, immich_uuid_v7, @@ -27,6 +28,7 @@ import { AlbumTable } from 'src/schema/tables/album.table'; import { ApiKeyTable } from 'src/schema/tables/api-key.table'; import { AssetAuditTable } from 'src/schema/tables/asset-audit.table'; import { AssetExifTable } from 'src/schema/tables/asset-exif.table'; +import { AssetFaceAuditTable } from 'src/schema/tables/asset-face-audit.table'; import { AssetFaceTable } from 'src/schema/tables/asset-face.table'; import { AssetFileTable } from 'src/schema/tables/asset-file.table'; import { AssetJobStatusTable } from 'src/schema/tables/asset-job-status.table'; @@ -78,6 +80,7 @@ export class ImmichDatabase { ApiKeyTable, AssetAuditTable, AssetFaceTable, + AssetFaceAuditTable, AssetJobStatusTable, AssetTable, AssetFileTable, @@ -132,6 +135,7 @@ export class ImmichDatabase { stack_delete_audit, person_delete_audit, user_metadata_audit, + asset_face_audit, ]; enum = [assets_status_enum, asset_face_source_type, asset_visibility_enum]; @@ -158,6 +162,7 @@ export interface DB { asset: AssetTable; asset_exif: AssetExifTable; asset_face: AssetFaceTable; + asset_face_audit: AssetFaceAuditTable; asset_file: AssetFileTable; asset_job_status: AssetJobStatusTable; asset_audit: AssetAuditTable; diff --git a/server/src/schema/migrations/1750323941566-UnsetPrewarmDimParameter.ts b/server/src/schema/migrations/1750323941566-UnsetPrewarmDimParameter.ts index 776b51d1db..e7a1a06ec9 100644 --- a/server/src/schema/migrations/1750323941566-UnsetPrewarmDimParameter.ts +++ b/server/src/schema/migrations/1750323941566-UnsetPrewarmDimParameter.ts @@ -1,25 +1,10 @@ -import { Kysely, sql } from 'kysely'; +// this file used to try to reset the `vchordrq.prewarm_dim;` parameter +// that ends up being a problem on pg 15 + since the extension is not installed. -export async function up(qb: Kysely): Promise { - type Conf = { db: string; guc: string[] }; - const res = await sql` - select current_database() db, to_json(setconfig) guc - from pg_db_role_setting - where setdatabase = (select oid from pg_database where datname = current_database()) - and setrole = 0;`.execute(qb); - if (res.rows.length === 0) { - return; - } - - const { db, guc } = res.rows[0]; - await sql.raw(`alter database "${db}" reset all;`).execute(qb); - for (const parameter of guc) { - const [key, value] = parameter.split('='); - if (key === 'vchordrq.prewarm_dim') { - continue; - } - await sql.raw(`alter database "${db}" set ${key} to ${value};`).execute(qb); - } +export async function up(): Promise { + // noop } -export async function down(): Promise {} +export async function down(): Promise { + // noop +} diff --git a/server/src/schema/migrations/1752161055253-RenameGeodataPKConstraint.ts b/server/src/schema/migrations/1752161055253-RenameGeodataPKConstraint.ts index 686791e201..086b7c1273 100644 --- a/server/src/schema/migrations/1752161055253-RenameGeodataPKConstraint.ts +++ b/server/src/schema/migrations/1752161055253-RenameGeodataPKConstraint.ts @@ -1,8 +1,23 @@ import { Kysely, sql } from 'kysely'; export async function up(db: Kysely): Promise { - await sql`ALTER TABLE "geodata_places" DROP CONSTRAINT IF EXISTS "PK_c29918988912ef4036f3d7fbff4";`.execute(db); - await sql`ALTER TABLE "geodata_places" DROP CONSTRAINT IF EXISTS "geodata_places_pkey"`.execute(db); + await sql` + DO $$ + DECLARE + constraint_name text; + BEGIN + SELECT con.conname + INTO constraint_name + FROM pg_catalog.pg_constraint con + JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid + WHERE rel.relname = 'geodata_places' AND con.contype = 'p'; + + IF constraint_name IS NOT NULL THEN + EXECUTE 'ALTER TABLE "geodata_places" DROP CONSTRAINT "' || constraint_name || '"'; + END IF; + END; + $$; + `.execute(db); await sql`ALTER TABLE "geodata_places" ADD CONSTRAINT "geodata_places_pkey" PRIMARY KEY ("id");`.execute(db); } diff --git a/server/src/schema/migrations/1752759108283-ConvertToAbsolutePaths.ts b/server/src/schema/migrations/1752759108283-ConvertToAbsolutePaths.ts new file mode 100644 index 0000000000..839af40cf5 --- /dev/null +++ b/server/src/schema/migrations/1752759108283-ConvertToAbsolutePaths.ts @@ -0,0 +1,38 @@ +import { Kysely, sql } from 'kysely'; +import { LoggingRepository } from 'src/repositories/logging.repository'; + +const logger = LoggingRepository.create('Migrations'); + +export async function up(db: Kysely): Promise { + if (process.env.IMMICH_MEDIA_LOCATION) { + // do not automatically convert paths for a custom location/setting + return; + } + + // we construct paths using `path.join(mediaLocation, ...)`, which strips the leading './' + const source = 'upload'; + const target = '/usr/src/app/upload'; + + logger.log(`Converting database file paths from relative to absolute (source=${source}/*, target=${target}/*)`); + + // escaping regex special characters with a backslash + const sourceRegex = '^' + source.replaceAll(/[-[\]{}()*+?.,\\^$|#\s]/g, String.raw`\$&`); + + const items: Array<{ table: string; column: string }> = [ + { table: 'asset', column: 'originalPath' }, + { table: 'asset', column: 'encodedVideoPath' }, + { table: 'asset', column: 'sidecarPath' }, + { table: 'asset_file', column: 'path' }, + { table: 'person', column: 'thumbnailPath' }, + { table: 'user', column: 'profileImagePath' }, + ]; + + for (const { table, column } of items) { + const query = `UPDATE "${table}" SET "${column}" = REGEXP_REPLACE("${column}", '${sourceRegex}', '${target}') WHERE "${column}" IS NOT NULL`; + await sql.raw(query).execute(db); + } +} + +export async function down(): Promise { + // not supported +} diff --git a/server/src/schema/migrations/1753104909784-AssetFaceUpdateIdAndAuditTable.ts b/server/src/schema/migrations/1753104909784-AssetFaceUpdateIdAndAuditTable.ts new file mode 100644 index 0000000000..1f4072e34e --- /dev/null +++ b/server/src/schema/migrations/1753104909784-AssetFaceUpdateIdAndAuditTable.ts @@ -0,0 +1,52 @@ +import { Kysely, sql } from 'kysely'; + +export async function up(db: Kysely): Promise { + await sql`CREATE OR REPLACE FUNCTION asset_face_audit() + RETURNS TRIGGER + LANGUAGE PLPGSQL + AS $$ + BEGIN + INSERT INTO asset_face_audit ("assetFaceId", "assetId") + SELECT "id", "assetId" + FROM OLD; + RETURN NULL; + END + $$;`.execute(db); + await sql`CREATE TABLE "asset_face_audit" ( + "id" uuid NOT NULL DEFAULT immich_uuid_v7(), + "assetFaceId" uuid NOT NULL, + "assetId" uuid NOT NULL, + "deletedAt" timestamp with time zone NOT NULL DEFAULT clock_timestamp(), + CONSTRAINT "asset_face_audit_pkey" PRIMARY KEY ("id") +);`.execute(db); + await sql`CREATE INDEX "asset_face_audit_assetFaceId_idx" ON "asset_face_audit" ("assetFaceId");`.execute(db); + await sql`CREATE INDEX "asset_face_audit_assetId_idx" ON "asset_face_audit" ("assetId");`.execute(db); + await sql`CREATE INDEX "asset_face_audit_deletedAt_idx" ON "asset_face_audit" ("deletedAt");`.execute(db); + await sql`ALTER TABLE "asset_face" ADD "updatedAt" timestamp with time zone NOT NULL DEFAULT now();`.execute(db); + await sql`ALTER TABLE "asset_face" ADD "updateId" uuid NOT NULL DEFAULT immich_uuid_v7();`.execute(db); + await sql`CREATE OR REPLACE TRIGGER "asset_face_audit" + AFTER DELETE ON "asset_face" + REFERENCING OLD TABLE AS "old" + FOR EACH STATEMENT + WHEN (pg_trigger_depth() = 0) + EXECUTE FUNCTION asset_face_audit();`.execute(db); + await sql`CREATE OR REPLACE TRIGGER "asset_face_updatedAt" + BEFORE UPDATE ON "asset_face" + FOR EACH ROW + EXECUTE FUNCTION updated_at();`.execute(db); + await sql`INSERT INTO "migration_overrides" ("name", "value") VALUES ('function_asset_face_audit', '{"type":"function","name":"asset_face_audit","sql":"CREATE OR REPLACE FUNCTION asset_face_audit()\\n RETURNS TRIGGER\\n LANGUAGE PLPGSQL\\n AS $$\\n BEGIN\\n INSERT INTO asset_face_audit (\\"assetFaceId\\", \\"assetId\\")\\n SELECT \\"id\\", \\"assetId\\"\\n FROM OLD;\\n RETURN NULL;\\n END\\n $$;"}'::jsonb);`.execute(db); + await sql`INSERT INTO "migration_overrides" ("name", "value") VALUES ('trigger_asset_face_audit', '{"type":"trigger","name":"asset_face_audit","sql":"CREATE OR REPLACE TRIGGER \\"asset_face_audit\\"\\n AFTER DELETE ON \\"asset_face\\"\\n REFERENCING OLD TABLE AS \\"old\\"\\n FOR EACH STATEMENT\\n WHEN (pg_trigger_depth() = 0)\\n EXECUTE FUNCTION asset_face_audit();"}'::jsonb);`.execute(db); + await sql`INSERT INTO "migration_overrides" ("name", "value") VALUES ('trigger_asset_face_updatedAt', '{"type":"trigger","name":"asset_face_updatedAt","sql":"CREATE OR REPLACE TRIGGER \\"asset_face_updatedAt\\"\\n BEFORE UPDATE ON \\"asset_face\\"\\n FOR EACH ROW\\n EXECUTE FUNCTION updated_at();"}'::jsonb);`.execute(db); +} + +export async function down(db: Kysely): Promise { + await sql`DROP TRIGGER "asset_face_audit" ON "asset_face";`.execute(db); + await sql`DROP TRIGGER "asset_face_updatedAt" ON "asset_face";`.execute(db); + await sql`ALTER TABLE "asset_face" DROP COLUMN "updatedAt";`.execute(db); + await sql`ALTER TABLE "asset_face" DROP COLUMN "updateId";`.execute(db); + await sql`DROP TABLE "asset_face_audit";`.execute(db); + await sql`DROP FUNCTION asset_face_audit;`.execute(db); + await sql`DELETE FROM "migration_overrides" WHERE "name" = 'function_asset_face_audit';`.execute(db); + await sql`DELETE FROM "migration_overrides" WHERE "name" = 'trigger_asset_face_audit';`.execute(db); + await sql`DELETE FROM "migration_overrides" WHERE "name" = 'trigger_asset_face_updatedAt';`.execute(db); +} diff --git a/server/src/schema/migrations/1753464178233-RenameApiKeyPermissions.ts b/server/src/schema/migrations/1753464178233-RenameApiKeyPermissions.ts new file mode 100644 index 0000000000..0293a96607 --- /dev/null +++ b/server/src/schema/migrations/1753464178233-RenameApiKeyPermissions.ts @@ -0,0 +1,22 @@ +import { Kysely, sql } from 'kysely'; + +const items = [ + { oldName: 'album.addAsset', newName: 'albumAsset.create' }, + { oldName: 'album.removeAsset', newName: 'albumAsset.delete' }, + { oldName: 'admin.user.create', newName: 'adminUser.create' }, + { oldName: 'admin.user.read', newName: 'adminUser.read' }, + { oldName: 'admin.user.update', newName: 'adminUser.update' }, + { oldName: 'admin.user.delete', newName: 'adminUser.delete' }, +]; + +export async function up(db: Kysely): Promise { + for (const { oldName, newName } of items) { + await sql`UPDATE "api_key" SET "permissions" = array_replace("permissions", ${oldName}, ${newName})`.execute(db); + } +} + +export async function down(db: Kysely): Promise { + for (const { oldName, newName } of items) { + await sql`UPDATE "api_key" SET "permissions" = array_replace("permissions", ${newName}, ${oldName})`.execute(db); + } +} diff --git a/server/src/schema/migrations/1753471866748-AddSharedLinkSlug.ts b/server/src/schema/migrations/1753471866748-AddSharedLinkSlug.ts new file mode 100644 index 0000000000..1d77eddd07 --- /dev/null +++ b/server/src/schema/migrations/1753471866748-AddSharedLinkSlug.ts @@ -0,0 +1,11 @@ +import { Kysely, sql } from 'kysely'; + +export async function up(db: Kysely): Promise { + await sql`ALTER TABLE "shared_link" ADD "slug" character varying;`.execute(db); + await sql`ALTER TABLE "shared_link" ADD CONSTRAINT "shared_link_slug_uq" UNIQUE ("slug");`.execute(db); +} + +export async function down(db: Kysely): Promise { + await sql`ALTER TABLE "shared_link" DROP CONSTRAINT "shared_link_slug_uq";`.execute(db); + await sql`ALTER TABLE "shared_link" DROP COLUMN "slug";`.execute(db); +} diff --git a/server/src/schema/migrations/1753800911775-ProfileImageCheckpointRemoval.ts b/server/src/schema/migrations/1753800911775-ProfileImageCheckpointRemoval.ts new file mode 100644 index 0000000000..4f741f2113 --- /dev/null +++ b/server/src/schema/migrations/1753800911775-ProfileImageCheckpointRemoval.ts @@ -0,0 +1,25 @@ +import { Kysely, sql } from 'kysely'; + +export async function up(db: Kysely): Promise { + await sql`DELETE FROM session_sync_checkpoint + WHERE type IN ( + 'UserV1', + 'AssetV1', + 'PartnerAssetV1', + 'PartnerAssetBackfillV1', + 'AlbumAssetV1', + 'AlbumAssetBackfillV1' + )`.execute(db); +} + +export async function down(db: Kysely): Promise { + await sql`DELETE FROM session_sync_checkpoint + WHERE type IN ( + 'UserV1', + 'AssetV1', + 'PartnerAssetV1', + 'PartnerAssetBackfillV1', + 'AlbumAssetV1', + 'AlbumAssetBackfillV1' + )`.execute(db); +} diff --git a/server/src/schema/tables/asset-face-audit.table.ts b/server/src/schema/tables/asset-face-audit.table.ts new file mode 100644 index 0000000000..4f03c22aa0 --- /dev/null +++ b/server/src/schema/tables/asset-face-audit.table.ts @@ -0,0 +1,17 @@ +import { PrimaryGeneratedUuidV7Column } from 'src/decorators'; +import { Column, CreateDateColumn, Generated, Table, Timestamp } from 'src/sql-tools'; + +@Table('asset_face_audit') +export class AssetFaceAuditTable { + @PrimaryGeneratedUuidV7Column() + id!: Generated; + + @Column({ type: 'uuid', index: true }) + assetFaceId!: string; + + @Column({ type: 'uuid', index: true }) + assetId!: string; + + @CreateDateColumn({ default: () => 'clock_timestamp()', index: true }) + deletedAt!: Generated; +} diff --git a/server/src/schema/tables/asset-face.table.ts b/server/src/schema/tables/asset-face.table.ts index 6e45a3a64d..5041d945e2 100644 --- a/server/src/schema/tables/asset-face.table.ts +++ b/server/src/schema/tables/asset-face.table.ts @@ -1,8 +1,11 @@ +import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators'; import { SourceType } from 'src/enum'; import { asset_face_source_type } from 'src/schema/enums'; +import { asset_face_audit } from 'src/schema/functions'; import { AssetTable } from 'src/schema/tables/asset.table'; import { PersonTable } from 'src/schema/tables/person.table'; import { + AfterDeleteTrigger, Column, DeleteDateColumn, ForeignKeyColumn, @@ -11,9 +14,17 @@ import { PrimaryGeneratedColumn, Table, Timestamp, + UpdateDateColumn, } from 'src/sql-tools'; @Table({ name: 'asset_face' }) +@UpdatedAtTrigger('asset_face_updatedAt') +@AfterDeleteTrigger({ + scope: 'statement', + function: asset_face_audit, + referencingOldTableAs: 'old', + when: 'pg_trigger_depth() = 0', +}) // schemaFromDatabase does not preserve column order @Index({ name: 'asset_face_assetId_personId_idx', columns: ['assetId', 'personId'] }) @Index({ columns: ['personId', 'assetId'] }) @@ -61,4 +72,10 @@ export class AssetFaceTable { @DeleteDateColumn() deletedAt!: Timestamp | null; + + @UpdateDateColumn() + updatedAt!: Generated; + + @UpdateIdColumn() + updateId!: Generated; } diff --git a/server/src/schema/tables/shared-link.table.ts b/server/src/schema/tables/shared-link.table.ts index 40fd2bf6a9..80e2d7cdf4 100644 --- a/server/src/schema/tables/shared-link.table.ts +++ b/server/src/schema/tables/shared-link.table.ts @@ -48,4 +48,7 @@ export class SharedLinkTable { @Column({ type: 'character varying', nullable: true }) password!: string | null; + + @Column({ type: 'character varying', nullable: true, unique: true }) + slug!: string | null; } diff --git a/server/src/schema/tables/user-metadata-audit.table.ts b/server/src/schema/tables/user-metadata-audit.table.ts index de7d21c874..63f503ab85 100644 --- a/server/src/schema/tables/user-metadata-audit.table.ts +++ b/server/src/schema/tables/user-metadata-audit.table.ts @@ -1,4 +1,5 @@ import { PrimaryGeneratedUuidV7Column } from 'src/decorators'; +import { UserMetadataKey } from 'src/enum'; import { Column, CreateDateColumn, Generated, Table, Timestamp } from 'src/sql-tools'; @Table('user_metadata_audit') @@ -10,7 +11,7 @@ export class UserMetadataAuditTable { userId!: string; @Column({ indexName: 'IDX_user_metadata_audit_key' }) - key!: string; + key!: UserMetadataKey; @CreateDateColumn({ default: () => 'clock_timestamp()', indexName: 'IDX_user_metadata_audit_deleted_at' }) deletedAt!: Generated; diff --git a/server/src/services/album.service.ts b/server/src/services/album.service.ts index e4f2a44bfd..90aefa6d72 100644 --- a/server/src/services/album.service.ts +++ b/server/src/services/album.service.ts @@ -158,7 +158,7 @@ export class AlbumService extends BaseService { async addAssets(auth: AuthDto, id: string, dto: BulkIdsDto): Promise { const album = await this.findOrFail(id, { withAssets: false }); - await this.requireAccess({ auth, permission: Permission.AlbumAddAsset, ids: [id] }); + await this.requireAccess({ auth, permission: Permission.AlbumAssetCreate, ids: [id] }); const results = await addAssets( auth, @@ -187,7 +187,7 @@ export class AlbumService extends BaseService { } async removeAssets(auth: AuthDto, id: string, dto: BulkIdsDto): Promise { - await this.requireAccess({ auth, permission: Permission.AlbumRemoveAsset, ids: [id] }); + await this.requireAccess({ auth, permission: Permission.AlbumAssetDelete, ids: [id] }); const album = await this.findOrFail(id, { withAssets: false }); const results = await removeAssets( diff --git a/server/src/services/api.service.ts b/server/src/services/api.service.ts index 27a776e867..ced74482c2 100644 --- a/server/src/services/api.service.ts +++ b/server/src/services/api.service.ts @@ -80,7 +80,7 @@ export class ApiService { if (shareMatches) { try { const key = shareMatches[1]; - const auth = await this.authService.validateSharedLink(key); + const auth = await this.authService.validateSharedLinkKey(key); const meta = await this.sharedLinkService.getMetadataTags( auth, request.host ? `${request.protocol}://${request.host}` : undefined, diff --git a/server/src/services/asset-media.service.spec.ts b/server/src/services/asset-media.service.spec.ts index dccb79c585..91bddeb6e9 100644 --- a/server/src/services/asset-media.service.spec.ts +++ b/server/src/services/asset-media.service.spec.ts @@ -29,7 +29,7 @@ const uploadFile = { file: { uuid: 'random-uuid', checksum: Buffer.from('checksum', 'utf8'), - originalPath: 'upload/admin/image.jpeg', + originalPath: '/data/library/admin/image.jpeg', originalName: 'image.jpeg', size: 1000, }, @@ -42,7 +42,7 @@ const uploadFile = { uuid: 'random-uuid', mimeType: 'image/jpeg', checksum: Buffer.from('checksum', 'utf8'), - originalPath: `upload/admin/${filename}`, + originalPath: `/data/admin/${filename}`, originalName: filename, size: 1000, }, @@ -294,16 +294,16 @@ describe(AssetMediaService.name, () => { it('should return profile for profile uploads', () => { expect(sut.getUploadFolder(uploadFile.filename(UploadFieldName.PROFILE_DATA, 'image.jpg'))).toEqual( - 'upload/profile/admin_id', + expect.stringContaining('/data/profile/admin_id'), ); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith('upload/profile/admin_id'); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/profile/admin_id')); }); it('should return upload for everything else', () => { expect(sut.getUploadFolder(uploadFile.filename(UploadFieldName.ASSET_DATA, 'image.jpg'))).toEqual( - 'upload/upload/admin_id/ra/nd', + expect.stringContaining('/data/upload/admin_id/ra/nd'), ); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith('upload/upload/admin_id/ra/nd'); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/upload/admin_id/ra/nd')); }); }); @@ -505,6 +505,7 @@ describe(AssetMediaService.name, () => { await expect(sut.downloadOriginal(authStub.admin, 'asset-1')).resolves.toEqual( new ImmichFileResponse({ path: '/original/path.jpg', + fileName: 'asset-id.jpg', contentType: 'image/jpeg', cacheControl: CacheControl.PrivateWithCache, }), @@ -906,14 +907,14 @@ describe(AssetMediaService.name, () => { size: 1000, uuid: 'random-uuid', checksum: Buffer.from('checksum', 'utf8'), - originalPath: 'upload/upload/user-id/ra/nd/random-uuid.jpg', + originalPath: '/data/upload/user-id/ra/nd/random-uuid.jpg', } as unknown as Express.Multer.File; await sut.onUploadError(request, file); expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.FileDelete, - data: { files: ['upload/upload/user-id/ra/nd/random-uuid.jpg'] }, + data: { files: [expect.stringContaining('/data/upload/user-id/ra/nd/random-uuid.jpg')] }, }); }); }); diff --git a/server/src/services/asset-media.service.ts b/server/src/services/asset-media.service.ts index 080774d038..517a1f665f 100644 --- a/server/src/services/asset-media.service.ts +++ b/server/src/services/asset-media.service.ts @@ -197,6 +197,7 @@ export class AssetMediaService extends BaseService { return new ImmichFileResponse({ path: asset.originalPath, + fileName: asset.originalFileName, contentType: mimeTypes.lookup(asset.originalPath), cacheControl: CacheControl.PrivateWithCache, }); diff --git a/server/src/services/auth.service.spec.ts b/server/src/services/auth.service.spec.ts index f52fd9dd81..6e19292f71 100644 --- a/server/src/services/auth.service.spec.ts +++ b/server/src/services/auth.service.spec.ts @@ -322,15 +322,18 @@ describe(AuthService.name, () => { mocks.sharedLink.getByKey.mockResolvedValue(sharedLink); mocks.user.get.mockResolvedValue(user); + const buffer = sharedLink.key; + const key = buffer.toString('base64url'); + await expect( sut.authenticate({ - headers: { 'x-immich-share-key': sharedLink.key.toString('base64url') }, + headers: { 'x-immich-share-key': key }, queryParams: {}, metadata: { adminRoute: false, sharedLinkRoute: true, uri: 'test' }, }), ).resolves.toEqual({ user, sharedLink }); - expect(mocks.sharedLink.getByKey).toHaveBeenCalledWith(sharedLink.key); + expect(mocks.sharedLink.getByKey).toHaveBeenCalledWith(buffer); }); it('should accept a hex key', async () => { @@ -340,15 +343,50 @@ describe(AuthService.name, () => { mocks.sharedLink.getByKey.mockResolvedValue(sharedLink); mocks.user.get.mockResolvedValue(user); + const buffer = sharedLink.key; + const key = buffer.toString('hex'); + await expect( sut.authenticate({ - headers: { 'x-immich-share-key': sharedLink.key.toString('hex') }, + headers: { 'x-immich-share-key': key }, queryParams: {}, metadata: { adminRoute: false, sharedLinkRoute: true, uri: 'test' }, }), ).resolves.toEqual({ user, sharedLink }); - expect(mocks.sharedLink.getByKey).toHaveBeenCalledWith(sharedLink.key); + expect(mocks.sharedLink.getByKey).toHaveBeenCalledWith(buffer); + }); + }); + + describe('validate - shared link slug', () => { + it('should not accept a non-existent slug', async () => { + mocks.sharedLink.getBySlug.mockResolvedValue(void 0); + + await expect( + sut.authenticate({ + headers: { 'x-immich-share-slug': 'slug' }, + queryParams: {}, + metadata: { adminRoute: false, sharedLinkRoute: true, uri: 'test' }, + }), + ).rejects.toBeInstanceOf(UnauthorizedException); + }); + + it('should accept a valid slug', async () => { + const user = factory.userAdmin(); + const sharedLink = { ...sharedLinkStub.valid, slug: 'slug-123', user } as any; + + mocks.sharedLink.getBySlug.mockResolvedValue(sharedLink); + mocks.user.get.mockResolvedValue(user); + + await expect( + sut.authenticate({ + headers: { 'x-immich-share-slug': 'slug-123' }, + queryParams: {}, + metadata: { adminRoute: false, sharedLinkRoute: true, uri: 'test' }, + }), + ).resolves.toEqual({ user, sharedLink }); + + expect(mocks.sharedLink.getBySlug).toHaveBeenCalledWith('slug-123'); }); }); @@ -459,18 +497,34 @@ describe(AuthService.name, () => { mocks.apiKey.getKey.mockResolvedValue({ ...authApiKey, user: authUser }); - await expect( - sut.authenticate({ - headers: { 'x-api-key': 'auth_token' }, - queryParams: {}, - metadata: { adminRoute: false, sharedLinkRoute: false, uri: 'test', permission: Permission.AssetRead }, - }), - ).rejects.toBeInstanceOf(ForbiddenException); + const result = sut.authenticate({ + headers: { 'x-api-key': 'auth_token' }, + queryParams: {}, + metadata: { adminRoute: false, sharedLinkRoute: false, uri: 'test', permission: Permission.AssetRead }, + }); + + await expect(result).rejects.toBeInstanceOf(ForbiddenException); + await expect(result).rejects.toThrow('Missing required permission: asset.read'); + }); + + it('should default to requiring the all permission when omitted', async () => { + const authUser = factory.authUser(); + const authApiKey = factory.authApiKey({ permissions: [Permission.AssetRead] }); + + mocks.apiKey.getKey.mockResolvedValue({ ...authApiKey, user: authUser }); + + const result = sut.authenticate({ + headers: { 'x-api-key': 'auth_token' }, + queryParams: {}, + metadata: { adminRoute: false, sharedLinkRoute: false, uri: 'test' }, + }); + await expect(result).rejects.toBeInstanceOf(ForbiddenException); + await expect(result).rejects.toThrow('Missing required permission: all'); }); it('should return an auth dto', async () => { const authUser = factory.authUser(); - const authApiKey = factory.authApiKey({ permissions: [] }); + const authApiKey = factory.authApiKey({ permissions: [Permission.All] }); mocks.apiKey.getKey.mockResolvedValue({ ...authApiKey, user: authUser }); @@ -789,7 +843,7 @@ describe(AuthService.name, () => { ).resolves.toEqual(oauthResponse(user)); expect(mocks.user.update).toHaveBeenCalledWith(user.id, { - profileImagePath: `upload/profile/${user.id}/${fileId}.jpg`, + profileImagePath: expect.stringContaining(`/data/profile/${user.id}/${fileId}.jpg`), profileChangedAt: expect.any(Date), }); expect(mocks.oauth.getProfilePicture).toHaveBeenCalledWith(pictureUrl); diff --git a/server/src/services/auth.service.ts b/server/src/services/auth.service.ts index a5b0de25cd..fcaeb06af0 100644 --- a/server/src/services/auth.service.ts +++ b/server/src/services/auth.service.ts @@ -6,7 +6,7 @@ import { IncomingHttpHeaders } from 'node:http'; import { join } from 'node:path'; import { LOGIN_URL, MOBILE_REDIRECT, SALT_ROUNDS } from 'src/constants'; import { StorageCore } from 'src/cores/storage.core'; -import { UserAdmin } from 'src/database'; +import { AuthSharedLink, AuthUser, UserAdmin } from 'src/database'; import { AuthDto, AuthStatusResponseDto, @@ -174,7 +174,8 @@ export class AuthService extends BaseService { async authenticate({ headers, queryParams, metadata }: ValidateRequest): Promise { const authDto = await this.validate({ headers, queryParams }); - const { adminRoute, sharedLinkRoute, permission, uri } = metadata; + const { adminRoute, sharedLinkRoute, uri } = metadata; + const requestedPermission = metadata.permission ?? Permission.All; if (!authDto.user.isAdmin && adminRoute) { this.logger.warn(`Denied access to admin only route: ${uri}`); @@ -186,8 +187,8 @@ export class AuthService extends BaseService { throw new ForbiddenException('Forbidden'); } - if (authDto.apiKey && permission && !isGranted({ requested: [permission], current: authDto.apiKey.permissions })) { - throw new ForbiddenException(`Missing required permission: ${permission}`); + if (authDto.apiKey && !isGranted({ requested: [requestedPermission], current: authDto.apiKey.permissions })) { + throw new ForbiddenException(`Missing required permission: ${requestedPermission}`); } return authDto; @@ -195,6 +196,7 @@ export class AuthService extends BaseService { private async validate({ headers, queryParams }: Omit): Promise { const shareKey = (headers[ImmichHeader.SharedLinkKey] || queryParams[ImmichQuery.SharedLinkKey]) as string; + const shareSlug = (headers[ImmichHeader.SharedLinkSlug] || queryParams[ImmichQuery.SharedLinkSlug]) as string; const session = (headers[ImmichHeader.UserToken] || headers[ImmichHeader.SessionToken] || queryParams[ImmichQuery.SessionKey] || @@ -203,7 +205,11 @@ export class AuthService extends BaseService { const apiKey = (headers[ImmichHeader.ApiKey] || queryParams[ImmichQuery.ApiKey]) as string; if (shareKey) { - return this.validateSharedLink(shareKey); + return this.validateSharedLinkKey(shareKey); + } + + if (shareSlug) { + return this.validateSharedLinkSlug(shareSlug); } if (session) { @@ -402,18 +408,33 @@ export class AuthService extends BaseService { return cookies[ImmichCookie.OAuthCodeVerifier] || null; } - async validateSharedLink(key: string | string[]): Promise { + async validateSharedLinkKey(key: string | string[]): Promise { key = Array.isArray(key) ? key[0] : key; const bytes = Buffer.from(key, key.length === 100 ? 'hex' : 'base64url'); const sharedLink = await this.sharedLinkRepository.getByKey(bytes); - if (sharedLink?.user && (!sharedLink.expiresAt || new Date(sharedLink.expiresAt) > new Date())) { - return { - user: sharedLink.user, - sharedLink, - }; + if (!this.isValidSharedLink(sharedLink)) { + throw new UnauthorizedException('Invalid share key'); } - throw new UnauthorizedException('Invalid share key'); + + return { user: sharedLink.user, sharedLink }; + } + + async validateSharedLinkSlug(slug: string | string[]): Promise { + slug = Array.isArray(slug) ? slug[0] : slug; + + const sharedLink = await this.sharedLinkRepository.getBySlug(slug); + if (!this.isValidSharedLink(sharedLink)) { + throw new UnauthorizedException('Invalid share slug'); + } + + return { user: sharedLink.user, sharedLink }; + } + + private isValidSharedLink( + sharedLink?: AuthSharedLink & { user: AuthUser | null }, + ): sharedLink is AuthSharedLink & { user: AuthUser } { + return !!sharedLink?.user && (!sharedLink.expiresAt || new Date(sharedLink.expiresAt) > new Date()); } private async validateApiKey(key: string): Promise { diff --git a/server/src/services/backup.service.spec.ts b/server/src/services/backup.service.spec.ts index e36f699f53..ad60e30425 100644 --- a/server/src/services/backup.service.spec.ts +++ b/server/src/services/backup.service.spec.ts @@ -1,3 +1,4 @@ +import { DateTime } from 'luxon'; import { PassThrough } from 'node:stream'; import { defaults, SystemConfig } from 'src/config'; import { StorageCore } from 'src/cores/storage.core'; @@ -90,18 +91,23 @@ describe(BackupService.name, () => { it('should remove failed backup files', async () => { mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.backupEnabled); + //`immich-db-backup-${DateTime.now().toFormat("yyyyLLdd'T'HHmmss")}-v${serverVersion.toString()}-pg${databaseVersion.split(' ')[0]}.sql.gz.tmp`, mocks.storage.readdir.mockResolvedValue([ 'immich-db-backup-123.sql.gz.tmp', - 'immich-db-backup-234.sql.gz', - 'immich-db-backup-345.sql.gz.tmp', + `immich-db-backup-${DateTime.fromISO('2025-07-25T11:02:16Z').toFormat("yyyyLLdd'T'HHmmss")}-v1.234.5-pg14.5.sql.gz.tmp`, + `immich-db-backup-${DateTime.fromISO('2025-07-27T11:01:16Z').toFormat("yyyyLLdd'T'HHmmss")}-v1.234.5-pg14.5.sql.gz`, + `immich-db-backup-${DateTime.fromISO('2025-07-29T11:01:16Z').toFormat("yyyyLLdd'T'HHmmss")}-v1.234.5-pg14.5.sql.gz.tmp`, ]); await sut.cleanupDatabaseBackups(); - expect(mocks.storage.unlink).toHaveBeenCalledTimes(2); + expect(mocks.storage.unlink).toHaveBeenCalledTimes(3); expect(mocks.storage.unlink).toHaveBeenCalledWith( `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-123.sql.gz.tmp`, ); expect(mocks.storage.unlink).toHaveBeenCalledWith( - `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-345.sql.gz.tmp`, + `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-20250725T110216-v1.234.5-pg14.5.sql.gz.tmp`, + ); + expect(mocks.storage.unlink).toHaveBeenCalledWith( + `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-20250729T110116-v1.234.5-pg14.5.sql.gz.tmp`, ); }); @@ -118,17 +124,21 @@ describe(BackupService.name, () => { it('should remove old backup files over keepLastAmount and failed backups', async () => { mocks.systemMetadata.get.mockResolvedValue(systemConfigStub.backupEnabled); mocks.storage.readdir.mockResolvedValue([ - 'immich-db-backup-1.sql.gz.tmp', - 'immich-db-backup-2.sql.gz', - 'immich-db-backup-3.sql.gz', + `immich-db-backup-${DateTime.fromISO('2025-07-25T11:02:16Z').toFormat("yyyyLLdd'T'HHmmss")}-v1.234.5-pg14.5.sql.gz.tmp`, + `immich-db-backup-${DateTime.fromISO('2025-07-27T11:01:16Z').toFormat("yyyyLLdd'T'HHmmss")}-v1.234.5-pg14.5.sql.gz`, + 'immich-db-backup-1753789649000.sql.gz', + `immich-db-backup-${DateTime.fromISO('2025-07-29T11:01:16Z').toFormat("yyyyLLdd'T'HHmmss")}-v1.234.5-pg14.5.sql.gz`, ]); await sut.cleanupDatabaseBackups(); - expect(mocks.storage.unlink).toHaveBeenCalledTimes(2); + expect(mocks.storage.unlink).toHaveBeenCalledTimes(3); expect(mocks.storage.unlink).toHaveBeenCalledWith( - `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-1.sql.gz.tmp`, + `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-1753789649000.sql.gz`, ); expect(mocks.storage.unlink).toHaveBeenCalledWith( - `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-2.sql.gz`, + `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-20250725T110216-v1.234.5-pg14.5.sql.gz.tmp`, + ); + expect(mocks.storage.unlink).toHaveBeenCalledWith( + `${StorageCore.getBaseFolder(StorageFolder.Backups)}/immich-db-backup-20250727T110116-v1.234.5-pg14.5.sql.gz`, ); }); }); diff --git a/server/src/services/backup.service.ts b/server/src/services/backup.service.ts index 9daa2c3aea..0948965f1c 100644 --- a/server/src/services/backup.service.ts +++ b/server/src/services/backup.service.ts @@ -53,9 +53,14 @@ export class BackupService extends BaseService { const backupsFolder = StorageCore.getBaseFolder(StorageFolder.Backups); const files = await this.storageRepository.readdir(backupsFolder); - const failedBackups = files.filter((file) => file.match(/immich-db-backup-\d+\.sql\.gz\.tmp$/)); + const failedBackups = files.filter((file) => file.match(/immich-db-backup-.*\.sql\.gz\.tmp$/)); const backups = files - .filter((file) => file.match(/immich-db-backup-\d+\.sql\.gz$/)) + .filter((file) => { + const oldBackupStyle = file.match(/immich-db-backup-\d+\.sql\.gz$/); + //immich-db-backup-20250729T114018-v1.136.0-pg14.17.sql.gz + const newBackupStyle = file.match(/immich-db-backup-\d{8}T\d{6}-v.*-pg.*\.sql\.gz$/); + return oldBackupStyle || newBackupStyle; + }) .sort() .reverse(); diff --git a/server/src/services/cli.service.ts b/server/src/services/cli.service.ts index 021a5240f6..38144e95b4 100644 --- a/server/src/services/cli.service.ts +++ b/server/src/services/cli.service.ts @@ -1,4 +1,5 @@ import { Injectable } from '@nestjs/common'; +import { isAbsolute } from 'node:path'; import { SALT_ROUNDS } from 'src/constants'; import { UserAdminResponseDto, mapUserAdmin } from 'src/dtos/user.dto'; import { BaseService } from 'src/services/base.service'; @@ -67,6 +68,58 @@ export class CliService extends BaseService { await this.updateConfig(config); } + async getSampleFilePaths(): Promise { + const [assets, people, users] = await Promise.all([ + this.assetRepository.getFileSamples(), + this.personRepository.getFileSamples(), + this.userRepository.getFileSamples(), + ]); + + const paths = []; + + for (const person of people) { + paths.push(person.thumbnailPath); + } + + for (const user of users) { + paths.push(user.profileImagePath); + } + + for (const asset of assets) { + paths.push(asset.path); + } + + return paths.filter(Boolean) as string[]; + } + + async migrateFilePaths({ + oldValue, + newValue, + confirm, + }: { + oldValue: string; + newValue: string; + confirm: (data: { sourceFolder: string; targetFolder: string }) => Promise; + }): Promise { + let sourceFolder = oldValue; + if (sourceFolder.startsWith('./')) { + sourceFolder = sourceFolder.slice(2); + } + + const targetFolder = newValue; + if (!isAbsolute(targetFolder)) { + throw new Error('Target media location must be an absolute path'); + } + + if (!(await confirm({ sourceFolder, targetFolder }))) { + return false; + } + + await this.databaseRepository.migrateFilePaths(sourceFolder, targetFolder); + + return true; + } + cleanup() { return this.databaseRepository.shutdown(); } diff --git a/server/src/services/download.service.spec.ts b/server/src/services/download.service.spec.ts index 7646637093..86d0bda7f8 100644 --- a/server/src/services/download.service.spec.ts +++ b/server/src/services/download.service.spec.ts @@ -1,10 +1,10 @@ import { BadRequestException } from '@nestjs/common'; +import { Readable } from 'node:stream'; import { DownloadResponseDto } from 'src/dtos/download.dto'; import { DownloadService } from 'src/services/download.service'; import { assetStub } from 'test/fixtures/asset.stub'; import { authStub } from 'test/fixtures/auth.stub'; import { makeStream, newTestService, ServiceMocks } from 'test/utils'; -import { Readable } from 'typeorm/platform/PlatformTools.js'; import { vitest } from 'vitest'; const downloadResponse: DownloadResponseDto = { @@ -46,7 +46,11 @@ describe(DownloadService.name, () => { }); expect(archiveMock.addFile).toHaveBeenCalledTimes(1); - expect(archiveMock.addFile).toHaveBeenNthCalledWith(1, 'upload/library/IMG_123.jpg', 'IMG_123.jpg'); + expect(archiveMock.addFile).toHaveBeenNthCalledWith( + 1, + expect.stringContaining('/data/library/IMG_123.jpg'), + 'IMG_123.jpg', + ); }); it('should log a warning if the original path could not be resolved', async () => { @@ -70,8 +74,8 @@ describe(DownloadService.name, () => { expect(mocks.logger.warn).toHaveBeenCalledTimes(2); expect(archiveMock.addFile).toHaveBeenCalledTimes(2); - expect(archiveMock.addFile).toHaveBeenNthCalledWith(1, 'upload/library/IMG_123.jpg', 'IMG_123.jpg'); - expect(archiveMock.addFile).toHaveBeenNthCalledWith(2, 'upload/library/IMG_456.jpg', 'IMG_456.jpg'); + expect(archiveMock.addFile).toHaveBeenNthCalledWith(1, '/data/library/IMG_123.jpg', 'IMG_123.jpg'); + expect(archiveMock.addFile).toHaveBeenNthCalledWith(2, '/data/library/IMG_456.jpg', 'IMG_456.jpg'); }); it('should download an archive', async () => { @@ -93,8 +97,8 @@ describe(DownloadService.name, () => { }); expect(archiveMock.addFile).toHaveBeenCalledTimes(2); - expect(archiveMock.addFile).toHaveBeenNthCalledWith(1, 'upload/library/IMG_123.jpg', 'IMG_123.jpg'); - expect(archiveMock.addFile).toHaveBeenNthCalledWith(2, 'upload/library/IMG_456.jpg', 'IMG_456.jpg'); + expect(archiveMock.addFile).toHaveBeenNthCalledWith(1, '/data/library/IMG_123.jpg', 'IMG_123.jpg'); + expect(archiveMock.addFile).toHaveBeenNthCalledWith(2, '/data/library/IMG_456.jpg', 'IMG_456.jpg'); }); it('should handle duplicate file names', async () => { @@ -116,8 +120,8 @@ describe(DownloadService.name, () => { }); expect(archiveMock.addFile).toHaveBeenCalledTimes(2); - expect(archiveMock.addFile).toHaveBeenNthCalledWith(1, 'upload/library/IMG_123.jpg', 'IMG_123.jpg'); - expect(archiveMock.addFile).toHaveBeenNthCalledWith(2, 'upload/library/IMG_123.jpg', 'IMG_123+1.jpg'); + expect(archiveMock.addFile).toHaveBeenNthCalledWith(1, '/data/library/IMG_123.jpg', 'IMG_123.jpg'); + expect(archiveMock.addFile).toHaveBeenNthCalledWith(2, '/data/library/IMG_123.jpg', 'IMG_123+1.jpg'); }); it('should be deterministic', async () => { @@ -139,8 +143,8 @@ describe(DownloadService.name, () => { }); expect(archiveMock.addFile).toHaveBeenCalledTimes(2); - expect(archiveMock.addFile).toHaveBeenNthCalledWith(1, 'upload/library/IMG_123.jpg', 'IMG_123.jpg'); - expect(archiveMock.addFile).toHaveBeenNthCalledWith(2, 'upload/library/IMG_123.jpg', 'IMG_123+1.jpg'); + expect(archiveMock.addFile).toHaveBeenNthCalledWith(1, '/data/library/IMG_123.jpg', 'IMG_123.jpg'); + expect(archiveMock.addFile).toHaveBeenNthCalledWith(2, '/data/library/IMG_123.jpg', 'IMG_123+1.jpg'); }); it('should resolve symlinks', async () => { @@ -279,9 +283,15 @@ describe(DownloadService.name, () => { mocks.downloadRepository.downloadAssetIds.mockReturnValue( makeStream([{ id: 'asset-1', livePhotoVideoId: 'asset-3', size: 5000 }]), ); + mocks.downloadRepository.downloadMotionAssetIds.mockReturnValue( makeStream([ - { id: 'asset-2', livePhotoVideoId: null, size: 23_456, originalPath: 'upload/encoded-video/uuid-MP.mp4' }, + { + id: 'asset-2', + livePhotoVideoId: null, + size: 23_456, + originalPath: '/data/encoded-video/uuid-MP.mp4', + }, ]), ); diff --git a/server/src/services/job.service.spec.ts b/server/src/services/job.service.spec.ts index a57db736af..63d5fb2d06 100644 --- a/server/src/services/job.service.spec.ts +++ b/server/src/services/job.service.spec.ts @@ -11,7 +11,7 @@ describe(JobService.name, () => { let mocks: ServiceMocks; beforeEach(() => { - ({ sut, mocks } = newTestService(JobService, {})); + ({ sut, mocks } = newTestService(JobService)); mocks.config.getWorker.mockReturnValue(ImmichWorker.Microservices); }); diff --git a/server/src/services/job.service.ts b/server/src/services/job.service.ts index c67f3af39f..0116c869c6 100644 --- a/server/src/services/job.service.ts +++ b/server/src/services/job.service.ts @@ -382,6 +382,7 @@ export class JobService extends BaseService { visibility: asset.visibility, livePhotoVideoId: asset.livePhotoVideoId, stackId: asset.stackId, + libraryId: asset.libraryId, }, exif: { assetId: exif.assetId, diff --git a/server/src/services/library.service.spec.ts b/server/src/services/library.service.spec.ts index dac2b64ebc..edd0d46bce 100644 --- a/server/src/services/library.service.spec.ts +++ b/server/src/services/library.service.spec.ts @@ -24,7 +24,7 @@ describe(LibraryService.name, () => { let mocks: ServiceMocks; beforeEach(() => { - ({ sut, mocks } = newTestService(LibraryService, {})); + ({ sut, mocks } = newTestService(LibraryService)); mocks.database.tryLock.mockResolvedValue(true); mocks.config.getWorker.mockReturnValue(ImmichWorker.Microservices); @@ -1171,10 +1171,10 @@ describe(LibraryService.name, () => { mocks.storage.checkFileExists.mockResolvedValue(true); - await expect(sut.validate('library-id', { importPaths: ['/data/user1/'] })).resolves.toEqual({ + await expect(sut.validate('library-id', { importPaths: ['/external/user1/'] })).resolves.toEqual({ importPaths: [ { - importPath: '/data/user1/', + importPath: '/external/user1/', isValid: true, message: undefined, }, @@ -1188,10 +1188,10 @@ describe(LibraryService.name, () => { throw error; }); - await expect(sut.validate('library-id', { importPaths: ['/data/user1/'] })).resolves.toEqual({ + await expect(sut.validate('library-id', { importPaths: ['/external/user1/'] })).resolves.toEqual({ importPaths: [ { - importPath: '/data/user1/', + importPath: '/external/user1/', isValid: false, message: 'Path does not exist (ENOENT)', }, @@ -1204,10 +1204,10 @@ describe(LibraryService.name, () => { isDirectory: () => false, } as Stats); - await expect(sut.validate('library-id', { importPaths: ['/data/user1/file'] })).resolves.toEqual({ + await expect(sut.validate('library-id', { importPaths: ['/external/user1/file'] })).resolves.toEqual({ importPaths: [ { - importPath: '/data/user1/file', + importPath: '/external/user1/file', isValid: false, message: 'Not a directory', }, @@ -1220,10 +1220,10 @@ describe(LibraryService.name, () => { throw new Error('Unknown error'); }); - await expect(sut.validate('library-id', { importPaths: ['/data/user1/'] })).resolves.toEqual({ + await expect(sut.validate('library-id', { importPaths: ['/external/user1/'] })).resolves.toEqual({ importPaths: [ { - importPath: '/data/user1/', + importPath: '/external/user1/', isValid: false, message: 'Error: Unknown error', }, @@ -1238,10 +1238,10 @@ describe(LibraryService.name, () => { mocks.storage.checkFileExists.mockResolvedValue(false); - await expect(sut.validate('library-id', { importPaths: ['/data/user1/'] })).resolves.toEqual({ + await expect(sut.validate('library-id', { importPaths: ['/external/user1/'] })).resolves.toEqual({ importPaths: [ { - importPath: '/data/user1/', + importPath: '/external/user1/', isValid: false, message: 'Lacking read permission for folder', }, @@ -1264,7 +1264,7 @@ describe(LibraryService.name, () => { }); it('should detect when import path is in immich media folder', async () => { - const importPaths = ['upload/thumbs', `${process.cwd()}/xyz`, 'upload/library']; + const importPaths = ['/data/thumbs', `${process.cwd()}/xyz`, '/data/library']; const library = factory.library({ importPaths }); mocks.storage.stat.mockResolvedValue({ isDirectory: () => true } as Stats); diff --git a/server/src/services/media.service.spec.ts b/server/src/services/media.service.spec.ts index d3c361bb8a..97351af37b 100644 --- a/server/src/services/media.service.spec.ts +++ b/server/src/services/media.service.spec.ts @@ -204,19 +204,19 @@ describe(MediaService.name, () => { entityId: assetStub.image.id, pathType: AssetPathType.FullSize, oldPath: '/uploads/user-id/fullsize/path.webp', - newPath: 'upload/thumbs/user-id/as/se/asset-id-fullsize.jpeg', + newPath: expect.stringContaining('/data/thumbs/user-id/as/se/asset-id-fullsize.jpeg'), }); expect(mocks.move.create).toHaveBeenCalledWith({ entityId: assetStub.image.id, pathType: AssetPathType.Preview, oldPath: '/uploads/user-id/thumbs/path.jpg', - newPath: 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg', + newPath: expect.stringContaining('/data/thumbs/user-id/as/se/asset-id-preview.jpeg'), }); expect(mocks.move.create).toHaveBeenCalledWith({ entityId: assetStub.image.id, pathType: AssetPathType.Thumbnail, oldPath: '/uploads/user-id/webp/path.ext', - newPath: 'upload/thumbs/user-id/as/se/asset-id-thumbnail.webp', + newPath: expect.stringContaining('/data/thumbs/user-id/as/se/asset-id-thumbnail.webp'), }); expect(mocks.move.create).toHaveBeenCalledTimes(3); }); @@ -295,7 +295,7 @@ describe(MediaService.name, () => { await sut.handleGenerateThumbnails({ id: assetStub.image.id }); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id/as/se'); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.any(String)); expect(mocks.media.decodeImage).toHaveBeenCalledOnce(); expect(mocks.media.decodeImage).toHaveBeenCalledWith(assetStub.image.originalPath, { @@ -315,7 +315,7 @@ describe(MediaService.name, () => { processInvalidImages: false, raw: rawInfo, }, - 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg', + expect.any(String), ); expect(mocks.media.generateThumbnail).toHaveBeenCalledWith( rawBuffer, @@ -327,7 +327,7 @@ describe(MediaService.name, () => { processInvalidImages: false, raw: rawInfo, }, - 'upload/thumbs/user-id/as/se/asset-id-thumbnail.webp', + expect.any(String), ); expect(mocks.media.generateThumbhash).toHaveBeenCalledOnce(); @@ -341,12 +341,12 @@ describe(MediaService.name, () => { { assetId: 'asset-id', type: AssetFileType.Preview, - path: 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg', + path: expect.any(String), }, { assetId: 'asset-id', type: AssetFileType.Thumbnail, - path: 'upload/thumbs/user-id/as/se/asset-id-thumbnail.webp', + path: expect.any(String), }, ]); expect(mocks.asset.update).toHaveBeenCalledWith({ id: 'asset-id', thumbhash: thumbhashBuffer }); @@ -357,10 +357,10 @@ describe(MediaService.name, () => { mocks.assetJob.getForGenerateThumbnailJob.mockResolvedValue(assetStub.video); await sut.handleGenerateThumbnails({ id: assetStub.video.id }); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id/as/se'); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.any(String)); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg', + expect.any(String), expect.objectContaining({ inputOptions: ['-skip_frame nointra', '-sws_flags accurate_rnd+full_chroma_int'], outputOptions: [ @@ -377,12 +377,12 @@ describe(MediaService.name, () => { { assetId: 'asset-id', type: AssetFileType.Preview, - path: 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg', + path: expect.any(String), }, { assetId: 'asset-id', type: AssetFileType.Thumbnail, - path: 'upload/thumbs/user-id/as/se/asset-id-thumbnail.webp', + path: expect.any(String), }, ]); }); @@ -392,10 +392,10 @@ describe(MediaService.name, () => { mocks.assetJob.getForGenerateThumbnailJob.mockResolvedValue(assetStub.video); await sut.handleGenerateThumbnails({ id: assetStub.video.id }); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id/as/se'); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.any(String)); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg', + expect.any(String), expect.objectContaining({ inputOptions: ['-skip_frame nointra', '-sws_flags accurate_rnd+full_chroma_int'], outputOptions: [ @@ -412,12 +412,12 @@ describe(MediaService.name, () => { { assetId: 'asset-id', type: AssetFileType.Preview, - path: 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg', + path: expect.any(String), }, { assetId: 'asset-id', type: AssetFileType.Thumbnail, - path: 'upload/thumbs/user-id/as/se/asset-id-thumbnail.webp', + path: expect.any(String), }, ]); }); @@ -432,7 +432,7 @@ describe(MediaService.name, () => { expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg', + expect.any(String), expect.objectContaining({ inputOptions: ['-skip_frame nointra', '-sws_flags accurate_rnd+full_chroma_int'], outputOptions: [ @@ -453,7 +453,7 @@ describe(MediaService.name, () => { expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg', + expect.any(String), expect.objectContaining({ inputOptions: ['-sws_flags accurate_rnd+full_chroma_int'], outputOptions: expect.any(Array), @@ -471,7 +471,7 @@ describe(MediaService.name, () => { expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining([expect.stringContaining('scale=-2:1440')]), @@ -485,12 +485,12 @@ describe(MediaService.name, () => { mocks.assetJob.getForGenerateThumbnailJob.mockResolvedValue(assetStub.image); const thumbhashBuffer = Buffer.from('a thumbhash', 'utf8'); mocks.media.generateThumbhash.mockResolvedValue(thumbhashBuffer); - const previewPath = `upload/thumbs/user-id/as/se/asset-id-preview.${format}`; - const thumbnailPath = `upload/thumbs/user-id/as/se/asset-id-thumbnail.webp`; + const previewPath = `/data/thumbs/user-id/as/se/asset-id-preview.${format}`; + const thumbnailPath = `/data/thumbs/user-id/as/se/asset-id-thumbnail.webp`; await sut.handleGenerateThumbnails({ id: assetStub.image.id }); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id/as/se'); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.any(String)); expect(mocks.media.decodeImage).toHaveBeenCalledOnce(); expect(mocks.media.decodeImage).toHaveBeenCalledWith(assetStub.image.originalPath, { colorspace: Colorspace.Srgb, @@ -530,12 +530,12 @@ describe(MediaService.name, () => { mocks.assetJob.getForGenerateThumbnailJob.mockResolvedValue(assetStub.image); const thumbhashBuffer = Buffer.from('a thumbhash', 'utf8'); mocks.media.generateThumbhash.mockResolvedValue(thumbhashBuffer); - const previewPath = `upload/thumbs/user-id/as/se/asset-id-preview.jpeg`; - const thumbnailPath = `upload/thumbs/user-id/as/se/asset-id-thumbnail.${format}`; + const previewPath = expect.stringContaining(`/data/thumbs/user-id/as/se/asset-id-preview.jpeg`); + const thumbnailPath = expect.stringContaining(`/data/thumbs/user-id/as/se/asset-id-thumbnail.${format}`); await sut.handleGenerateThumbnails({ id: assetStub.image.id }); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id/as/se'); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.any(String)); expect(mocks.media.decodeImage).toHaveBeenCalledOnce(); expect(mocks.media.decodeImage).toHaveBeenCalledWith(assetStub.image.originalPath, { colorspace: Colorspace.Srgb, @@ -658,12 +658,12 @@ describe(MediaService.name, () => { expect(mocks.media.generateThumbnail).toHaveBeenCalledWith( rawBuffer, expect.objectContaining({ processInvalidImages: false }), - 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg', + expect.any(String), ); expect(mocks.media.generateThumbnail).toHaveBeenCalledWith( rawBuffer, expect.objectContaining({ processInvalidImages: false }), - 'upload/thumbs/user-id/as/se/asset-id-thumbnail.webp', + expect.any(String), ); expect(mocks.media.generateThumbhash).toHaveBeenCalledOnce(); @@ -704,7 +704,7 @@ describe(MediaService.name, () => { processInvalidImages: false, raw: rawInfo, }, - 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg', + expect.any(String), ); }); @@ -734,7 +734,7 @@ describe(MediaService.name, () => { processInvalidImages: false, raw: rawInfo, }, - 'upload/thumbs/user-id/as/se/asset-id-fullsize.webp', + expect.any(String), ); expect(mocks.media.generateThumbnail).toHaveBeenCalledWith( fullsizeBuffer, @@ -746,7 +746,7 @@ describe(MediaService.name, () => { processInvalidImages: false, raw: rawInfo, }, - 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg', + expect.any(String), ); }); @@ -774,7 +774,7 @@ describe(MediaService.name, () => { processInvalidImages: false, raw: rawInfo, }, - 'upload/thumbs/user-id/as/se/asset-id-fullsize.jpeg', + expect.any(String), ); expect(mocks.media.generateThumbnail).toHaveBeenCalledWith( rawBuffer, @@ -786,7 +786,7 @@ describe(MediaService.name, () => { processInvalidImages: false, raw: rawInfo, }, - 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg', + expect.any(String), ); }); @@ -815,7 +815,7 @@ describe(MediaService.name, () => { processInvalidImages: false, raw: rawInfo, }, - 'upload/thumbs/user-id/as/se/asset-id-fullsize.jpeg', + expect.any(String), ); }); @@ -838,7 +838,7 @@ describe(MediaService.name, () => { expect(mocks.media.generateThumbnail).not.toHaveBeenCalledWith( expect.anything(), expect.anything(), - 'upload/thumbs/user-id/as/se/asset-id-fullsize.jpeg', + expect.stringContaining('fullsize.jpeg'), ); }); @@ -869,7 +869,7 @@ describe(MediaService.name, () => { processInvalidImages: false, raw: rawInfo, }, - 'upload/thumbs/user-id/as/se/asset-id-fullsize.webp', + expect.any(String), ); }); }); @@ -911,7 +911,7 @@ describe(MediaService.name, () => { ); expect(mocks.person.getDataForThumbnailGenerationJob).toHaveBeenCalledWith(personStub.primaryPerson.id); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith('upload/thumbs/admin_id/pe/rs'); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.any(String)); expect(mocks.media.decodeImage).toHaveBeenCalledWith(personThumbnailStub.newThumbnailMiddle.originalPath, { colorspace: Colorspace.P3, orientation: undefined, @@ -933,12 +933,9 @@ describe(MediaService.name, () => { processInvalidImages: false, size: 250, }, - 'upload/thumbs/admin_id/pe/rs/person-1.jpeg', + expect.any(String), ); - expect(mocks.person.update).toHaveBeenCalledWith({ - id: 'person-1', - thumbnailPath: 'upload/thumbs/admin_id/pe/rs/person-1.jpeg', - }); + expect(mocks.person.update).toHaveBeenCalledWith({ id: 'person-1', thumbnailPath: expect.any(String) }); }); it('should use preview path if video', async () => { @@ -953,7 +950,7 @@ describe(MediaService.name, () => { ); expect(mocks.person.getDataForThumbnailGenerationJob).toHaveBeenCalledWith(personStub.primaryPerson.id); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith('upload/thumbs/admin_id/pe/rs'); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.any(String)); expect(mocks.media.decodeImage).toHaveBeenCalledWith(personThumbnailStub.newThumbnailMiddle.previewPath, { colorspace: Colorspace.P3, orientation: undefined, @@ -975,12 +972,9 @@ describe(MediaService.name, () => { processInvalidImages: false, size: 250, }, - 'upload/thumbs/admin_id/pe/rs/person-1.jpeg', + expect.any(String), ); - expect(mocks.person.update).toHaveBeenCalledWith({ - id: 'person-1', - thumbnailPath: 'upload/thumbs/admin_id/pe/rs/person-1.jpeg', - }); + expect(mocks.person.update).toHaveBeenCalledWith({ id: 'person-1', thumbnailPath: expect.any(String) }); }); it('should generate a thumbnail without going negative', async () => { @@ -1015,7 +1009,7 @@ describe(MediaService.name, () => { processInvalidImages: false, size: 250, }, - 'upload/thumbs/admin_id/pe/rs/person-1.jpeg', + expect.any(String), ); }); @@ -1052,7 +1046,7 @@ describe(MediaService.name, () => { processInvalidImages: false, size: 250, }, - 'upload/thumbs/admin_id/pe/rs/person-1.jpeg', + expect.any(String), ); }); @@ -1089,7 +1083,7 @@ describe(MediaService.name, () => { processInvalidImages: false, size: 250, }, - 'upload/thumbs/admin_id/pe/rs/person-1.jpeg', + expect.any(String), ); }); @@ -1126,7 +1120,7 @@ describe(MediaService.name, () => { processInvalidImages: false, size: 250, }, - 'upload/thumbs/admin_id/pe/rs/person-1.jpeg', + expect.any(String), ); }); @@ -1168,7 +1162,7 @@ describe(MediaService.name, () => { processInvalidImages: false, size: 250, }, - 'upload/thumbs/admin_id/pe/rs/person-1.jpeg', + expect.any(String), ); }); @@ -1288,7 +1282,7 @@ describe(MediaService.name, () => { expect(mocks.storage.mkdirSync).toHaveBeenCalled(); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-map 0:1', '-map 0:3']), @@ -1308,7 +1302,7 @@ describe(MediaService.name, () => { expect(mocks.storage.mkdirSync).toHaveBeenCalled(); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-map 0:0', '-map 0:2']), @@ -1354,7 +1348,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.any(Array), @@ -1369,7 +1363,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.any(Array), @@ -1384,7 +1378,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.any(Array), @@ -1399,7 +1393,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.any(Array), @@ -1416,7 +1410,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.not.arrayContaining([expect.stringContaining('scale')]), @@ -1431,7 +1425,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining([expect.stringMatching(/scale(_.+)?=-2:720/)]), @@ -1446,7 +1440,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining([expect.stringMatching(/scale(_.+)?=720:-2/)]), @@ -1463,7 +1457,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining([expect.stringMatching(/scale(_.+)?=-2:354/)]), @@ -1480,7 +1474,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining([expect.stringMatching(/scale(_.+)?=354:-2/)]), @@ -1497,7 +1491,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-c:v copy', '-c:a aac']), @@ -1518,7 +1512,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.not.arrayContaining(['-tag:v hvc1']), @@ -1539,7 +1533,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-c:v copy', '-tag:v hvc1']), @@ -1554,7 +1548,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-c:v h264', '-c:a copy']), @@ -1568,7 +1562,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-c:v copy', '-c:a copy']), @@ -1627,7 +1621,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-c:v h264', '-maxrate 4500k', '-bufsize 9000k']), @@ -1642,7 +1636,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-c:v h264', '-maxrate 4500k', '-bufsize 9000k']), @@ -1657,7 +1651,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-c:v h264', '-b:v 3104k', '-minrate 1552k', '-maxrate 4500k']), @@ -1672,7 +1666,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-c:v h264', '-c:a copy']), @@ -1693,7 +1687,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-b:v 3104k', '-minrate 1552k', '-maxrate 4500k']), @@ -1714,7 +1708,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.not.arrayContaining([expect.stringContaining('-maxrate')]), @@ -1729,7 +1723,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-cpu-used 2']), @@ -1744,7 +1738,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.not.arrayContaining([expect.stringContaining('-cpu-used')]), @@ -1759,7 +1753,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-threads 2']), @@ -1774,7 +1768,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-threads 1', '-x264-params frame-threads=1:pools=none']), @@ -1789,7 +1783,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.not.arrayContaining([expect.stringContaining('-threads')]), @@ -1804,7 +1798,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-c:v hevc', '-threads 1', '-x265-params frame-threads=1:pools=none']), @@ -1819,7 +1813,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.not.arrayContaining([expect.stringContaining('-threads')]), @@ -1834,7 +1828,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining([ @@ -1859,7 +1853,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-preset 4']), @@ -1874,7 +1868,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-svtav1-params mbr=2M']), @@ -1889,7 +1883,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-svtav1-params lp=4']), @@ -1906,7 +1900,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-svtav1-params lp=4:mbr=2M']), @@ -1950,7 +1944,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining(['-init_hw_device cuda=cuda:0', '-filter_hw_device cuda']), outputOptions: expect.arrayContaining([ @@ -1987,7 +1981,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining(['-init_hw_device cuda=cuda:0', '-filter_hw_device cuda']), outputOptions: expect.arrayContaining([expect.stringContaining('-multipass')]), @@ -2004,7 +1998,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining(['-init_hw_device cuda=cuda:0', '-filter_hw_device cuda']), outputOptions: expect.arrayContaining(['-cq:v 23', '-maxrate 10000k', '-bufsize 6897k']), @@ -2021,7 +2015,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining(['-init_hw_device cuda=cuda:0', '-filter_hw_device cuda']), outputOptions: expect.not.stringContaining('-maxrate'), @@ -2038,7 +2032,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining(['-init_hw_device cuda=cuda:0', '-filter_hw_device cuda']), outputOptions: expect.not.arrayContaining([expect.stringContaining('-preset')]), @@ -2053,7 +2047,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining(['-init_hw_device cuda=cuda:0', '-filter_hw_device cuda']), outputOptions: expect.not.arrayContaining([expect.stringContaining('-multipass')]), @@ -2070,7 +2064,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining([ '-hwaccel cuda', @@ -2092,7 +2086,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining(['-hwaccel cuda', '-hwaccel_output_format cuda']), outputOptions: expect.arrayContaining([ @@ -2113,7 +2107,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining(['-hwaccel cuda', '-hwaccel_output_format cuda']), outputOptions: expect.arrayContaining([expect.stringContaining('scale_cuda=-2:720:format=nv12')]), @@ -2130,7 +2124,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining([ '-init_hw_device qsv=hw,child_device=/dev/dri/renderD128', @@ -2170,7 +2164,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining([ '-init_hw_device qsv=hw,child_device=/dev/dri/renderD128', @@ -2190,7 +2184,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining([ '-init_hw_device qsv=hw,child_device=/dev/dri/renderD128', @@ -2210,7 +2204,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining([ '-init_hw_device qsv=hw,child_device=/dev/dri/renderD128', @@ -2239,7 +2233,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining([ '-init_hw_device qsv=hw,child_device=/dev/dri/renderD129', @@ -2261,7 +2255,7 @@ describe(MediaService.name, () => { expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining([ '-hwaccel qsv', @@ -2287,7 +2281,7 @@ describe(MediaService.name, () => { expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining([ '-hwaccel qsv', @@ -2315,7 +2309,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining(['-hwaccel qsv', '-qsv_device /dev/dri/renderD129']), outputOptions: expect.any(Array), @@ -2334,7 +2328,7 @@ describe(MediaService.name, () => { expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining([ '-hwaccel qsv', @@ -2354,7 +2348,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining([ '-init_hw_device vaapi=accel:/dev/dri/renderD128', @@ -2386,7 +2380,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining([ '-init_hw_device vaapi=accel:/dev/dri/renderD128', @@ -2410,7 +2404,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining([ '-init_hw_device vaapi=accel:/dev/dri/renderD128', @@ -2436,7 +2430,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining([ '-init_hw_device vaapi=accel:/dev/dri/renderD128', @@ -2455,7 +2449,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining([ '-init_hw_device vaapi=accel:/dev/dri/renderD129', @@ -2476,7 +2470,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining([ '-init_hw_device vaapi=accel:/dev/dri/renderD128', @@ -2498,7 +2492,7 @@ describe(MediaService.name, () => { expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining([ '-hwaccel vaapi', @@ -2523,7 +2517,7 @@ describe(MediaService.name, () => { expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining(['-hwaccel vaapi', '-hwaccel_output_format vaapi', '-threads 1']), outputOptions: expect.arrayContaining([ @@ -2546,7 +2540,7 @@ describe(MediaService.name, () => { expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining(['-hwaccel vaapi', '-hwaccel_output_format vaapi', '-threads 1']), outputOptions: expect.arrayContaining([expect.stringContaining('format=nv12')]), @@ -2565,7 +2559,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining(['-hwaccel vaapi', '-hwaccel_device /dev/dri/renderD129']), outputOptions: expect.any(Array), @@ -2584,7 +2578,7 @@ describe(MediaService.name, () => { expect(mocks.media.transcode).toHaveBeenCalledTimes(2); expect(mocks.media.transcode).toHaveBeenLastCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining([ '-init_hw_device vaapi=accel:/dev/dri/renderD128', @@ -2607,7 +2601,7 @@ describe(MediaService.name, () => { expect(mocks.media.transcode).toHaveBeenCalledTimes(3); expect(mocks.media.transcode).toHaveBeenLastCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-c:v h264']), @@ -2624,7 +2618,7 @@ describe(MediaService.name, () => { expect(mocks.media.transcode).toHaveBeenCalledTimes(2); expect(mocks.media.transcode).toHaveBeenLastCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-c:v h264']), @@ -2649,7 +2643,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining([ '-hwaccel rkmpp', @@ -2689,7 +2683,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining(['-hwaccel rkmpp', '-hwaccel_output_format drm_prime', '-afbc rga']), outputOptions: expect.arrayContaining([`-c:v hevc_rkmpp`, '-level 153', '-rc_mode AVBR', '-b:v 10000k']), @@ -2706,7 +2700,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining(['-hwaccel rkmpp', '-hwaccel_output_format drm_prime', '-afbc rga']), outputOptions: expect.arrayContaining([`-c:v h264_rkmpp`, '-level 51', '-rc_mode CQP', '-qp_init 30']), @@ -2723,7 +2717,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining(['-hwaccel rkmpp', '-hwaccel_output_format drm_prime', '-afbc rga']), outputOptions: expect.arrayContaining([ @@ -2745,7 +2739,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.arrayContaining(['-hwaccel rkmpp', '-hwaccel_output_format drm_prime', '-afbc rga']), outputOptions: expect.arrayContaining([ @@ -2764,7 +2758,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: [], outputOptions: expect.arrayContaining([ @@ -2786,7 +2780,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining([ @@ -2805,7 +2799,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining([ @@ -2824,7 +2818,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining([ @@ -2843,7 +2837,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-c:v h264', '-c:a copy', '-vf format=yuv420p']), @@ -2858,7 +2852,7 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + expect.any(String), expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-c:v h264', '-c:a copy', '-vf scale=-2:720,format=yuv420p']), @@ -2874,19 +2868,15 @@ describe(MediaService.name, () => { await sut.handleVideoConversion({ id: assetStub.video.id }); expect(mocks.media.probe).toHaveBeenCalledWith(assetStub.video.originalPath, { countFrames: true }); - expect(mocks.media.transcode).toHaveBeenCalledWith( - assetStub.video.originalPath, - 'upload/encoded-video/user-id/as/se/asset-id.mp4', - { - inputOptions: expect.any(Array), - outputOptions: expect.any(Array), - twoPass: false, - progress: { - frameCount: probeStub.videoStream2160p.videoStreams[0].frameCount, - percentInterval: expect.any(Number), - }, + expect(mocks.media.transcode).toHaveBeenCalledWith(assetStub.video.originalPath, expect.any(String), { + inputOptions: expect.any(Array), + outputOptions: expect.any(Array), + twoPass: false, + progress: { + frameCount: probeStub.videoStream2160p.videoStreams[0].frameCount, + percentInterval: expect.any(Number), }, - ); + }); }); it('should not count frames for progress when log level is not debug', async () => { @@ -2904,7 +2894,7 @@ describe(MediaService.name, () => { expect(mocks.media.transcode).toHaveBeenCalledWith( '/original/path.ext', - 'upload/encoded-video/user-id/as/se/asset-id.mp4', + '/data/encoded-video/user-id/as/se/asset-id.mp4', expect.objectContaining({ inputOptions: expect.any(Array), outputOptions: expect.arrayContaining(['-c:a copy']), diff --git a/server/src/services/metadata.service.spec.ts b/server/src/services/metadata.service.spec.ts index 77f0b50a0a..1e304137e4 100644 --- a/server/src/services/metadata.service.spec.ts +++ b/server/src/services/metadata.service.spec.ts @@ -587,7 +587,7 @@ describe(MetadataService.name, () => { libraryId: assetStub.livePhotoWithOriginalFileName.libraryId, localDateTime: assetStub.livePhotoWithOriginalFileName.fileCreatedAt, originalFileName: 'asset_1.mp4', - originalPath: 'upload/encoded-video/user-id/li/ve/live-photo-motion-asset-MP.mp4', + originalPath: expect.stringContaining('/data/encoded-video/user-id/li/ve/live-photo-motion-asset-MP.mp4'), ownerId: assetStub.livePhotoWithOriginalFileName.ownerId, type: AssetType.Video, }); @@ -645,7 +645,7 @@ describe(MetadataService.name, () => { libraryId: assetStub.livePhotoWithOriginalFileName.libraryId, localDateTime: assetStub.livePhotoWithOriginalFileName.fileCreatedAt, originalFileName: 'asset_1.mp4', - originalPath: 'upload/encoded-video/user-id/li/ve/live-photo-motion-asset-MP.mp4', + originalPath: expect.stringContaining('/data/encoded-video/user-id/li/ve/live-photo-motion-asset-MP.mp4'), ownerId: assetStub.livePhotoWithOriginalFileName.ownerId, type: AssetType.Video, }); @@ -703,7 +703,7 @@ describe(MetadataService.name, () => { libraryId: assetStub.livePhotoWithOriginalFileName.libraryId, localDateTime: assetStub.livePhotoWithOriginalFileName.fileCreatedAt, originalFileName: 'asset_1.mp4', - originalPath: 'upload/encoded-video/user-id/li/ve/live-photo-motion-asset-MP.mp4', + originalPath: expect.stringContaining('/data/encoded-video/user-id/li/ve/live-photo-motion-asset-MP.mp4'), ownerId: assetStub.livePhotoWithOriginalFileName.ownerId, type: AssetType.Video, }); diff --git a/server/src/services/search.service.ts b/server/src/services/search.service.ts index 1c75c4a434..b9391fed90 100644 --- a/server/src/services/search.service.ts +++ b/server/src/services/search.service.ts @@ -4,6 +4,7 @@ import { AssetMapOptions, AssetResponseDto, MapAsset, mapAsset } from 'src/dtos/ import { AuthDto } from 'src/dtos/auth.dto'; import { mapPerson, PersonResponseDto } from 'src/dtos/person.dto'; import { + LargeAssetSearchDto, mapPlaces, MetadataSearchDto, PlacesResponseDto, @@ -91,6 +92,16 @@ export class SearchService extends BaseService { return items.map((item) => mapAsset(item, { auth })); } + async searchLargeAssets(auth: AuthDto, dto: LargeAssetSearchDto): Promise { + if (dto.visibility === AssetVisibility.Locked) { + requireElevatedPermission(auth); + } + + const userIds = await this.getUserIdsToSearch(auth); + const items = await this.searchRepository.searchLargeAssets(dto.size || 250, { ...dto, userIds }); + return items.map((item) => mapAsset(item, { auth })); + } + async searchSmart(auth: AuthDto, dto: SmartSearchDto): Promise { if (dto.visibility === AssetVisibility.Locked) { requireElevatedPermission(auth); diff --git a/server/src/services/server.service.spec.ts b/server/src/services/server.service.spec.ts index 0ddf3d69b1..a96a9925db 100644 --- a/server/src/services/server.service.spec.ts +++ b/server/src/services/server.service.spec.ts @@ -28,7 +28,7 @@ describe(ServerService.name, () => { diskUseRaw: 300, }); - expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith('upload/library'); + expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('/data/library')); }); it('should return the disk space as KiB', async () => { @@ -44,7 +44,7 @@ describe(ServerService.name, () => { diskUseRaw: 300_000, }); - expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith('upload/library'); + expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('/data/library')); }); it('should return the disk space as MiB', async () => { @@ -60,7 +60,7 @@ describe(ServerService.name, () => { diskUseRaw: 300_000_000, }); - expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith('upload/library'); + expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('/data/library')); }); it('should return the disk space as GiB', async () => { @@ -80,7 +80,7 @@ describe(ServerService.name, () => { diskUseRaw: 300_000_000_000, }); - expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith('upload/library'); + expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('/data/library')); }); it('should return the disk space as TiB', async () => { @@ -100,7 +100,7 @@ describe(ServerService.name, () => { diskUseRaw: 300_000_000_000_000, }); - expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith('upload/library'); + expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('/data/library')); }); it('should return the disk space as PiB', async () => { @@ -120,7 +120,7 @@ describe(ServerService.name, () => { diskUseRaw: 300_000_000_000_000_000, }); - expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith('upload/library'); + expect(mocks.storage.checkDiskUsage).toHaveBeenCalledWith(expect.stringContaining('/data/library')); }); }); diff --git a/server/src/services/shared-link.service.spec.ts b/server/src/services/shared-link.service.spec.ts index 8e09580d55..9483cdddff 100644 --- a/server/src/services/shared-link.service.spec.ts +++ b/server/src/services/shared-link.service.spec.ts @@ -136,6 +136,7 @@ describe(SharedLinkService.name, () => { allowUpload: true, description: null, expiresAt: null, + slug: null, showExif: true, key: Buffer.from('random-bytes', 'utf8'), }); @@ -163,6 +164,7 @@ describe(SharedLinkService.name, () => { userId: authStub.admin.user.id, albumId: null, allowDownload: true, + slug: null, allowUpload: true, assetIds: [assetStub.image.id], description: null, @@ -199,6 +201,7 @@ describe(SharedLinkService.name, () => { description: null, expiresAt: null, showExif: false, + slug: null, key: Buffer.from('random-bytes', 'utf8'), }); }); @@ -223,6 +226,7 @@ describe(SharedLinkService.name, () => { expect(mocks.sharedLink.get).toHaveBeenCalledWith(authStub.user1.user.id, sharedLinkStub.valid.id); expect(mocks.sharedLink.update).toHaveBeenCalledWith({ id: sharedLinkStub.valid.id, + slug: null, userId: authStub.user1.user.id, allowDownload: false, }); @@ -277,6 +281,7 @@ describe(SharedLinkService.name, () => { expect(mocks.sharedLink.update).toHaveBeenCalled(); expect(mocks.sharedLink.update).toHaveBeenCalledWith({ ...sharedLinkStub.individual, + slug: null, assetIds: ['asset-3'], }); }); diff --git a/server/src/services/shared-link.service.ts b/server/src/services/shared-link.service.ts index 9f8e238c43..096739d056 100644 --- a/server/src/services/shared-link.service.ts +++ b/server/src/services/shared-link.service.ts @@ -1,4 +1,5 @@ import { BadRequestException, ForbiddenException, Injectable, UnauthorizedException } from '@nestjs/common'; +import { PostgresError } from 'postgres'; import { SharedLink } from 'src/database'; import { AssetIdErrorReason, AssetIdsResponseDto } from 'src/dtos/asset-ids.response.dto'; import { AssetIdsDto } from 'src/dtos/asset.dto'; @@ -64,36 +65,53 @@ export class SharedLinkService extends BaseService { } } - const sharedLink = await this.sharedLinkRepository.create({ - key: this.cryptoRepository.randomBytes(50), - userId: auth.user.id, - type: dto.type, - albumId: dto.albumId || null, - assetIds: dto.assetIds, - description: dto.description || null, - password: dto.password, - expiresAt: dto.expiresAt || null, - allowUpload: dto.allowUpload ?? true, - allowDownload: dto.showMetadata === false ? false : (dto.allowDownload ?? true), - showExif: dto.showMetadata ?? true, - }); + try { + const sharedLink = await this.sharedLinkRepository.create({ + key: this.cryptoRepository.randomBytes(50), + userId: auth.user.id, + type: dto.type, + albumId: dto.albumId || null, + assetIds: dto.assetIds, + description: dto.description || null, + password: dto.password, + expiresAt: dto.expiresAt || null, + allowUpload: dto.allowUpload ?? true, + allowDownload: dto.showMetadata === false ? false : (dto.allowDownload ?? true), + showExif: dto.showMetadata ?? true, + slug: dto.slug || null, + }); - return this.mapToSharedLink(sharedLink, { withExif: true }); + return this.mapToSharedLink(sharedLink, { withExif: true }); + } catch (error) { + this.handleError(error); + } + } + + private handleError(error: unknown): never { + if ((error as PostgresError).constraint_name === 'shared_link_slug_uq') { + throw new BadRequestException('Shared link with this slug already exists'); + } + throw error; } async update(auth: AuthDto, id: string, dto: SharedLinkEditDto) { await this.findOrFail(auth.user.id, id); - const sharedLink = await this.sharedLinkRepository.update({ - id, - userId: auth.user.id, - description: dto.description, - password: dto.password, - expiresAt: dto.changeExpiryTime && !dto.expiresAt ? null : dto.expiresAt, - allowUpload: dto.allowUpload, - allowDownload: dto.allowDownload, - showExif: dto.showMetadata, - }); - return this.mapToSharedLink(sharedLink, { withExif: true }); + try { + const sharedLink = await this.sharedLinkRepository.update({ + id, + userId: auth.user.id, + description: dto.description, + password: dto.password, + expiresAt: dto.changeExpiryTime && !dto.expiresAt ? null : dto.expiresAt, + allowUpload: dto.allowUpload, + allowDownload: dto.allowDownload, + showExif: dto.showMetadata, + slug: dto.slug || null, + }); + return this.mapToSharedLink(sharedLink, { withExif: true }); + } catch (error) { + this.handleError(error); + } } async remove(auth: AuthDto, id: string): Promise { diff --git a/server/src/services/stack.service.spec.ts b/server/src/services/stack.service.spec.ts index 5c7b505cd9..5517cf17f8 100644 --- a/server/src/services/stack.service.spec.ts +++ b/server/src/services/stack.service.spec.ts @@ -188,4 +188,53 @@ describe(StackService.name, () => { }); }); }); + + describe('removeAsset', () => { + it('should require stack.update permissions', async () => { + await expect(sut.removeAsset(authStub.admin, { id: 'stack-id', assetId: 'asset-id' })).rejects.toBeInstanceOf( + BadRequestException, + ); + + expect(mocks.stack.getForAssetRemoval).not.toHaveBeenCalled(); + expect(mocks.asset.update).not.toHaveBeenCalled(); + expect(mocks.event.emit).not.toHaveBeenCalled(); + }); + + it('should fail if the asset is not in the stack', async () => { + mocks.access.stack.checkOwnerAccess.mockResolvedValue(new Set(['stack-id'])); + mocks.stack.getForAssetRemoval.mockResolvedValue({ id: null, primaryAssetId: null }); + + await expect( + sut.removeAsset(authStub.admin, { id: 'stack-id', assetId: assetStub.imageFrom2015.id }), + ).rejects.toBeInstanceOf(BadRequestException); + + expect(mocks.asset.update).not.toHaveBeenCalled(); + expect(mocks.event.emit).not.toHaveBeenCalled(); + }); + + it('should fail if the assetId is the primaryAssetId', async () => { + mocks.access.stack.checkOwnerAccess.mockResolvedValue(new Set(['stack-id'])); + mocks.stack.getForAssetRemoval.mockResolvedValue({ id: 'stack-id', primaryAssetId: assetStub.image.id }); + + await expect( + sut.removeAsset(authStub.admin, { id: 'stack-id', assetId: assetStub.image.id }), + ).rejects.toBeInstanceOf(BadRequestException); + + expect(mocks.asset.update).not.toHaveBeenCalled(); + expect(mocks.event.emit).not.toHaveBeenCalled(); + }); + + it("should update the asset to nullify it's stack-id", async () => { + mocks.access.stack.checkOwnerAccess.mockResolvedValue(new Set(['stack-id'])); + mocks.stack.getForAssetRemoval.mockResolvedValue({ id: 'stack-id', primaryAssetId: assetStub.image.id }); + + await sut.removeAsset(authStub.admin, { id: 'stack-id', assetId: assetStub.image1.id }); + + expect(mocks.asset.update).toHaveBeenCalledWith({ id: assetStub.image1.id, stackId: null }); + expect(mocks.event.emit).toHaveBeenCalledWith('StackUpdate', { + stackId: 'stack-id', + userId: authStub.admin.user.id, + }); + }); + }); }); diff --git a/server/src/services/stack.service.ts b/server/src/services/stack.service.ts index 18600abd12..c84ec70fbf 100644 --- a/server/src/services/stack.service.ts +++ b/server/src/services/stack.service.ts @@ -4,6 +4,7 @@ import { AuthDto } from 'src/dtos/auth.dto'; import { StackCreateDto, StackResponseDto, StackSearchDto, StackUpdateDto, mapStack } from 'src/dtos/stack.dto'; import { Permission } from 'src/enum'; import { BaseService } from 'src/services/base.service'; +import { UUIDAssetIDParamDto } from 'src/validation'; @Injectable() export class StackService extends BaseService { @@ -58,6 +59,24 @@ export class StackService extends BaseService { await this.eventRepository.emit('StackDeleteAll', { stackIds: dto.ids, userId: auth.user.id }); } + async removeAsset(auth: AuthDto, dto: UUIDAssetIDParamDto): Promise { + const { id: stackId, assetId } = dto; + await this.requireAccess({ auth, permission: Permission.StackUpdate, ids: [stackId] }); + + const stack = await this.stackRepository.getForAssetRemoval(assetId); + + if (!stack?.id || stack.id !== stackId) { + throw new BadRequestException('Asset not in stack'); + } + + if (stack.primaryAssetId === assetId) { + throw new BadRequestException("Cannot remove stack's primary asset"); + } + + await this.assetRepository.update({ id: assetId, stackId: null }); + await this.eventRepository.emit('StackUpdate', { stackId, userId: auth.user.id }); + } + private async findOrFail(id: string) { const stack = await this.stackRepository.getById(id); if (!stack) { diff --git a/server/src/services/storage-template.service.spec.ts b/server/src/services/storage-template.service.spec.ts index 2751651dbf..d0d7ea3a3c 100644 --- a/server/src/services/storage-template.service.spec.ts +++ b/server/src/services/storage-template.service.spec.ts @@ -110,8 +110,8 @@ describe(StorageTemplateService.name, () => { it('should migrate single moving picture', async () => { mocks.user.get.mockResolvedValue(userStub.user1); - const newMotionPicturePath = `upload/library/${motionAsset.ownerId}/2022/2022-06-19/${motionAsset.originalFileName}`; - const newStillPicturePath = `upload/library/${stillAsset.ownerId}/2022/2022-06-19/${stillAsset.originalFileName}`; + const newMotionPicturePath = `/data/library/${motionAsset.ownerId}/2022/2022-06-19/${motionAsset.originalFileName}`; + const newStillPicturePath = `/data/library/${stillAsset.ownerId}/2022/2022-06-19/${stillAsset.originalFileName}`; mocks.assetJob.getForStorageTemplateJob.mockResolvedValueOnce(stillAsset); mocks.assetJob.getForStorageTemplateJob.mockResolvedValueOnce(motionAsset); @@ -156,7 +156,9 @@ describe(StorageTemplateService.name, () => { expect(mocks.move.create).toHaveBeenCalledWith({ entityId: asset.id, - newPath: `upload/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/${album.albumName}/${asset.originalFileName}`, + newPath: expect.stringContaining( + `/data/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/${album.albumName}/${asset.originalFileName}`, + ), oldPath: asset.originalPath, pathType: AssetPathType.Original, }); @@ -177,7 +179,9 @@ describe(StorageTemplateService.name, () => { const month = (asset.fileCreatedAt.getMonth() + 1).toString().padStart(2, '0'); expect(mocks.move.create).toHaveBeenCalledWith({ entityId: asset.id, - newPath: `upload/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/other/${month}/${asset.originalFileName}`, + newPath: expect.stringContaining( + `/data/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/other/${month}/${asset.originalFileName}`, + ), oldPath: asset.originalPath, pathType: AssetPathType.Original, }); @@ -211,7 +215,9 @@ describe(StorageTemplateService.name, () => { const month = (asset.fileCreatedAt.getMonth() + 1).toString().padStart(2, '0'); expect(mocks.move.create).toHaveBeenCalledWith({ entityId: asset.id, - newPath: `upload/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/${month} - ${album.albumName}/${asset.originalFileName}`, + newPath: expect.stringContaining( + `/data/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/${month} - ${album.albumName}/${asset.originalFileName}`, + ), oldPath: asset.originalPath, pathType: AssetPathType.Original, }); @@ -234,7 +240,7 @@ describe(StorageTemplateService.name, () => { const month = (asset.fileCreatedAt.getMonth() + 1).toString().padStart(2, '0'); expect(mocks.move.create).toHaveBeenCalledWith({ entityId: asset.id, - newPath: `upload/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/${month}/${asset.originalFileName}`, + newPath: `/data/library/${user.id}/${asset.fileCreatedAt.getFullYear()}/${month}/${asset.originalFileName}`, oldPath: asset.originalPath, pathType: AssetPathType.Original, }); @@ -244,8 +250,8 @@ describe(StorageTemplateService.name, () => { mocks.user.get.mockResolvedValue(userStub.user1); const asset = assetStub.storageAsset(); - const previousFailedNewPath = `upload/library/${userStub.user1.id}/2023/Feb/${asset.originalFileName}`; - const newPath = `upload/library/${userStub.user1.id}/2022/2022-06-19/${asset.originalFileName}`; + const previousFailedNewPath = `/data/library/${userStub.user1.id}/2023/Feb/${asset.originalFileName}`; + const newPath = `/data/library/${userStub.user1.id}/2022/2022-06-19/${asset.originalFileName}`; mocks.storage.checkFileExists.mockImplementation((path) => Promise.resolve(path === asset.originalPath)); mocks.move.getByEntity.mockResolvedValue({ @@ -284,8 +290,8 @@ describe(StorageTemplateService.name, () => { mocks.user.get.mockResolvedValue(userStub.user1); const asset = assetStub.storageAsset({ fileSizeInByte: 5000 }); - const previousFailedNewPath = `upload/library/${asset.ownerId}/2022/June/${asset.originalFileName}`; - const newPath = `upload/library/${asset.ownerId}/2022/2022-06-19/${asset.originalFileName}`; + const previousFailedNewPath = `/data/library/${asset.ownerId}/2022/June/${asset.originalFileName}`; + const newPath = `/data/library/${asset.ownerId}/2022/2022-06-19/${asset.originalFileName}`; mocks.storage.checkFileExists.mockImplementation((path) => Promise.resolve(path === previousFailedNewPath)); mocks.storage.stat.mockResolvedValue({ size: 5000 } as Stats); @@ -319,7 +325,7 @@ describe(StorageTemplateService.name, () => { it('should fail move if copying and hash of asset and the new file do not match', async () => { mocks.user.get.mockResolvedValue(userStub.user1); - const newPath = `upload/library/${userStub.user1.id}/2022/2022-06-19/${testAsset.originalFileName}`; + const newPath = `/data/library/${userStub.user1.id}/2022/2022-06-19/${testAsset.originalFileName}`; mocks.storage.rename.mockRejectedValue({ code: 'EXDEV' }); mocks.storage.stat.mockResolvedValue({ size: 5000 } as Stats); @@ -361,8 +367,8 @@ describe(StorageTemplateService.name, () => { 'should fail to migrate previously failed move from previous new path when old path no longer exists if $reason validation fails', async ({ failedPathChecksum, failedPathSize }) => { mocks.user.get.mockResolvedValue(userStub.user1); - const previousFailedNewPath = `upload/library/${userStub.user1.id}/2023/Feb/${testAsset.originalFileName}`; - const newPath = `upload/library/${userStub.user1.id}/2023/2023-02-23/${testAsset.originalFileName}`; + const previousFailedNewPath = `/data/library/${userStub.user1.id}/2023/Feb/${testAsset.originalFileName}`; + const newPath = `/data/library/${userStub.user1.id}/2023/2023-02-23/${testAsset.originalFileName}`; mocks.storage.checkFileExists.mockImplementation((path) => Promise.resolve(previousFailedNewPath === path)); mocks.storage.stat.mockResolvedValue({ size: failedPathSize } as Stats); @@ -409,7 +415,7 @@ describe(StorageTemplateService.name, () => { it('should handle an asset with a duplicate destination', async () => { const asset = assetStub.storageAsset(); const oldPath = asset.originalPath; - const newPath = `upload/library/user-id/2022/2022-06-19/${asset.originalFileName}`; + const newPath = `/data/library/user-id/2022/2022-06-19/${asset.originalFileName}`; const newPath2 = newPath.replace('.jpg', '+1.jpg'); mocks.assetJob.streamForStorageTemplateJob.mockReturnValue(makeStream([asset])); @@ -434,7 +440,7 @@ describe(StorageTemplateService.name, () => { }); it('should skip when an asset already matches the template', async () => { - const asset = assetStub.storageAsset({ originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id.jpg' }); + const asset = assetStub.storageAsset({ originalPath: '/data/library/user-id/2023/2023-02-23/asset-id.jpg' }); mocks.assetJob.streamForStorageTemplateJob.mockReturnValue(makeStream([asset])); mocks.user.getList.mockResolvedValue([userStub.user1]); @@ -449,7 +455,7 @@ describe(StorageTemplateService.name, () => { }); it('should skip when an asset is probably a duplicate', async () => { - const asset = assetStub.storageAsset({ originalPath: 'upload/library/user-id/2023/2023-02-23/asset-id+1.jpg' }); + const asset = assetStub.storageAsset({ originalPath: '/data/library/user-id/2023/2023-02-23/asset-id+1.jpg' }); mocks.assetJob.streamForStorageTemplateJob.mockReturnValue(makeStream([asset])); mocks.user.getList.mockResolvedValue([userStub.user1]); @@ -466,7 +472,7 @@ describe(StorageTemplateService.name, () => { it('should move an asset', async () => { const asset = assetStub.storageAsset(); const oldPath = asset.originalPath; - const newPath = `upload/library/user-id/2022/2022-06-19/${asset.originalFileName}`; + const newPath = `/data/library/user-id/2022/2022-06-19/${asset.originalFileName}`; mocks.assetJob.streamForStorageTemplateJob.mockReturnValue(makeStream([asset])); mocks.user.getList.mockResolvedValue([userStub.user1]); mocks.move.create.mockResolvedValue({ @@ -494,7 +500,7 @@ describe(StorageTemplateService.name, () => { entityId: asset.id, pathType: AssetPathType.Original, oldPath: asset.originalPath, - newPath: `upload/library/${user.storageLabel}/2023/2023-02-23/${asset.originalFileName}`, + newPath: `/data/library/${user.storageLabel}/2023/2023-02-23/${asset.originalFileName}`, }); await sut.handleMigration(); @@ -502,18 +508,20 @@ describe(StorageTemplateService.name, () => { expect(mocks.assetJob.streamForStorageTemplateJob).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( '/original/path.jpg', - `upload/library/${user.storageLabel}/2022/2022-06-19/${asset.originalFileName}`, + expect.stringContaining(`/data/library/${user.storageLabel}/2022/2022-06-19/${asset.originalFileName}`), ); expect(mocks.asset.update).toHaveBeenCalledWith({ id: asset.id, - originalPath: `upload/library/${user.storageLabel}/2022/2022-06-19/${asset.originalFileName}`, + originalPath: expect.stringContaining( + `/data/library/${user.storageLabel}/2022/2022-06-19/${asset.originalFileName}`, + ), }); }); it('should copy the file if rename fails due to EXDEV (rename across filesystems)', async () => { const asset = assetStub.storageAsset({ originalPath: '/path/to/original.jpg', fileSizeInByte: 5000 }); const oldPath = asset.originalPath; - const newPath = `upload/library/user-id/2022/2022-06-19/${asset.originalFileName}`; + const newPath = `/data/library/user-id/2022/2022-06-19/${asset.originalFileName}`; mocks.assetJob.streamForStorageTemplateJob.mockReturnValue(makeStream([asset])); mocks.storage.rename.mockRejectedValue({ code: 'EXDEV' }); mocks.user.getList.mockResolvedValue([userStub.user1]); @@ -561,7 +569,7 @@ describe(StorageTemplateService.name, () => { entityId: asset.id, pathType: AssetPathType.Original, oldPath: asset.originalPath, - newPath: `upload/library/user-id/2022/2022-06-19/${asset.originalFileName}`, + newPath: `/data/library/user-id/2022/2022-06-19/${asset.originalFileName}`, }); mocks.storage.stat.mockResolvedValue({ size: 100, @@ -572,14 +580,14 @@ describe(StorageTemplateService.name, () => { expect(mocks.assetJob.streamForStorageTemplateJob).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( '/original/path.jpg', - `upload/library/user-id/2022/2022-06-19/${asset.originalFileName}`, + expect.stringContaining(`/data/library/user-id/2022/2022-06-19/${asset.originalFileName}`), ); expect(mocks.storage.copyFile).toHaveBeenCalledWith( '/original/path.jpg', - `upload/library/user-id/2022/2022-06-19/${asset.originalFileName}`, + expect.stringContaining(`/data/library/user-id/2022/2022-06-19/${asset.originalFileName}`), ); expect(mocks.storage.stat).toHaveBeenCalledWith( - `upload/library/user-id/2022/2022-06-19/${asset.originalFileName}`, + expect.stringContaining(`/data/library/user-id/2022/2022-06-19/${asset.originalFileName}`), ); expect(mocks.asset.update).not.toHaveBeenCalled(); }); @@ -603,7 +611,7 @@ describe(StorageTemplateService.name, () => { expect(mocks.assetJob.streamForStorageTemplateJob).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( '/original/path.jpg', - `upload/library/user-id/2022/2022-06-19/${asset.originalFileName}`, + expect.stringContaining(`/data/library/user-id/2022/2022-06-19/${asset.originalFileName}`), ); expect(mocks.asset.update).not.toHaveBeenCalled(); }); @@ -614,7 +622,7 @@ describe(StorageTemplateService.name, () => { const user = factory.userAdmin({ storageLabel: 'label-1' }); const asset = assetStub.storageAsset({ ownerId: user.id, - originalPath: `upload/library/${user.id}/2022/2022-06-19/IMG_7065.heic`, + originalPath: `/data/library/${user.id}/2022/2022-06-19/IMG_7065.heic`, originalFileName: 'IMG_7065.HEIC', }); mocks.assetJob.streamForStorageTemplateJob.mockReturnValue(makeStream([asset])); @@ -623,16 +631,16 @@ describe(StorageTemplateService.name, () => { id: '123', entityId: asset.id, pathType: AssetPathType.Original, - oldPath: `upload/library/${user.id}/2022/2022-06-19/IMG_7065.heic`, - newPath: `upload/library/${user.id}/2023/2023-02-23/IMG_7065.heic`, + oldPath: `/data/library/${user.id}/2022/2022-06-19/IMG_7065.heic`, + newPath: `/data/library/${user.id}/2023/2023-02-23/IMG_7065.heic`, }); await sut.handleMigration(); expect(mocks.assetJob.streamForStorageTemplateJob).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( - `upload/library/${user.id}/2022/2022-06-19/IMG_7065.heic`, - `upload/library/${user.storageLabel}/2022/2022-06-19/IMG_7065.heic`, + expect.stringContaining(`/data/library/${user.id}/2022/2022-06-19/IMG_7065.heic`), + expect.stringContaining(`/data/library/${user.storageLabel}/2022/2022-06-19/IMG_7065.heic`), ); }); @@ -640,7 +648,7 @@ describe(StorageTemplateService.name, () => { const user = factory.userAdmin(); const asset = assetStub.storageAsset({ ownerId: user.id, - originalPath: `upload/library/${user.id}/2022/2022-06-19/IMG_7065.HEIC`, + originalPath: `/data/library/${user.id}/2022/2022-06-19/IMG_7065.HEIC`, originalFileName: 'IMG_7065.HEIC', }); mocks.assetJob.streamForStorageTemplateJob.mockReturnValue(makeStream([asset])); @@ -649,16 +657,16 @@ describe(StorageTemplateService.name, () => { id: '123', entityId: asset.id, pathType: AssetPathType.Original, - oldPath: `upload/library/${user.id}/2022/2022-06-19/IMG_7065.HEIC`, - newPath: `upload/library/${user.id}/2023/2023-02-23/IMG_7065.heic`, + oldPath: `/data/library/${user.id}/2022/2022-06-19/IMG_7065.HEIC`, + newPath: `/data/library/${user.id}/2023/2023-02-23/IMG_7065.heic`, }); await sut.handleMigration(); expect(mocks.assetJob.streamForStorageTemplateJob).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( - `upload/library/${user.id}/2022/2022-06-19/IMG_7065.HEIC`, - `upload/library/${user.id}/2022/2022-06-19/IMG_7065.heic`, + expect.stringContaining(`/data/library/${user.id}/2022/2022-06-19/IMG_7065.HEIC`), + expect.stringContaining(`/data/library/${user.id}/2022/2022-06-19/IMG_7065.heic`), ); }); @@ -666,7 +674,7 @@ describe(StorageTemplateService.name, () => { const user = factory.userAdmin(); const asset = assetStub.storageAsset({ ownerId: user.id, - originalPath: `upload/library/${user.id}/2022/2022-06-19/IMG_7065.JPEG`, + originalPath: `/data/library/${user.id}/2022/2022-06-19/IMG_7065.JPEG`, originalFileName: 'IMG_7065.JPEG', }); mocks.assetJob.streamForStorageTemplateJob.mockReturnValue(makeStream([asset])); @@ -675,16 +683,16 @@ describe(StorageTemplateService.name, () => { id: '123', entityId: asset.id, pathType: AssetPathType.Original, - oldPath: `upload/library/${user.id}/2022/2022-06-19/IMG_7065.JPEG`, - newPath: `upload/library/${user.id}/2023/2023-02-23/IMG_7065.jpg`, + oldPath: `/data/library/${user.id}/2022/2022-06-19/IMG_7065.JPEG`, + newPath: `/data/library/${user.id}/2023/2023-02-23/IMG_7065.jpg`, }); await sut.handleMigration(); expect(mocks.assetJob.streamForStorageTemplateJob).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( - `upload/library/${user.id}/2022/2022-06-19/IMG_7065.JPEG`, - `upload/library/${user.id}/2022/2022-06-19/IMG_7065.jpg`, + expect.stringContaining(`/data/library/${user.id}/2022/2022-06-19/IMG_7065.JPEG`), + expect.stringContaining(`/data/library/${user.id}/2022/2022-06-19/IMG_7065.jpg`), ); }); @@ -692,7 +700,7 @@ describe(StorageTemplateService.name, () => { const user = factory.userAdmin(); const asset = assetStub.storageAsset({ ownerId: user.id, - originalPath: 'upload/library/user-id/2022/2022-06-19/IMG_7065.JPG', + originalPath: '/data/library/user-id/2022/2022-06-19/IMG_7065.JPG', originalFileName: 'IMG_7065.JPG', }); mocks.assetJob.streamForStorageTemplateJob.mockReturnValue(makeStream([asset])); @@ -701,16 +709,16 @@ describe(StorageTemplateService.name, () => { id: '123', entityId: asset.id, pathType: AssetPathType.Original, - oldPath: `upload/library/${user.id}/2022/2022-06-19/IMG_7065.JPG`, - newPath: `upload/library/${user.id}/2023/2023-02-23/IMG_7065.jpg`, + oldPath: `/data/library/${user.id}/2022/2022-06-19/IMG_7065.JPG`, + newPath: `/data/library/${user.id}/2023/2023-02-23/IMG_7065.jpg`, }); await sut.handleMigration(); expect(mocks.assetJob.streamForStorageTemplateJob).toHaveBeenCalled(); expect(mocks.storage.rename).toHaveBeenCalledWith( - `upload/library/${user.id}/2022/2022-06-19/IMG_7065.JPG`, - `upload/library/${user.id}/2022/2022-06-19/IMG_7065.jpg`, + expect.stringContaining(`/data/library/${user.id}/2022/2022-06-19/IMG_7065.JPG`), + expect.stringContaining(`/data/library/${user.id}/2022/2022-06-19/IMG_7065.jpg`), ); }); }); diff --git a/server/src/services/storage.service.spec.ts b/server/src/services/storage.service.spec.ts index 0e051f2642..3ca9cd7ce2 100644 --- a/server/src/services/storage.service.spec.ts +++ b/server/src/services/storage.service.spec.ts @@ -19,6 +19,15 @@ describe(StorageService.name, () => { describe('onBootstrap', () => { it('should enable mount folder checking', async () => { mocks.systemMetadata.get.mockResolvedValue(null); + mocks.asset.getFileSamples.mockResolvedValue([]); + mocks.config.getEnv.mockReturnValue( + mockEnvData({ + storage: { + ignoreMountCheckErrors: false, + mediaLocation: '/data', + }, + }), + ); await expect(sut.onBootstrap()).resolves.toBeUndefined(); @@ -32,18 +41,36 @@ describe(StorageService.name, () => { upload: true, }, }); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith('upload/encoded-video'); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith('upload/library'); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith('upload/profile'); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith('upload/thumbs'); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith('upload/upload'); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith('upload/backups'); - expect(mocks.storage.createFile).toHaveBeenCalledWith('upload/encoded-video/.immich', expect.any(Buffer)); - expect(mocks.storage.createFile).toHaveBeenCalledWith('upload/library/.immich', expect.any(Buffer)); - expect(mocks.storage.createFile).toHaveBeenCalledWith('upload/profile/.immich', expect.any(Buffer)); - expect(mocks.storage.createFile).toHaveBeenCalledWith('upload/thumbs/.immich', expect.any(Buffer)); - expect(mocks.storage.createFile).toHaveBeenCalledWith('upload/upload/.immich', expect.any(Buffer)); - expect(mocks.storage.createFile).toHaveBeenCalledWith('upload/backups/.immich', expect.any(Buffer)); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/encoded-video')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/library')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/profile')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/thumbs')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/upload')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/backups')); + expect(mocks.storage.createFile).toHaveBeenCalledWith( + expect.stringContaining('/data/encoded-video/.immich'), + expect.any(Buffer), + ); + expect(mocks.storage.createFile).toHaveBeenCalledWith( + expect.stringContaining('/data/library/.immich'), + expect.any(Buffer), + ); + expect(mocks.storage.createFile).toHaveBeenCalledWith( + expect.stringContaining('/data/profile/.immich'), + expect.any(Buffer), + ); + expect(mocks.storage.createFile).toHaveBeenCalledWith( + expect.stringContaining('/data/thumbs/.immich'), + expect.any(Buffer), + ); + expect(mocks.storage.createFile).toHaveBeenCalledWith( + expect.stringContaining('/data/upload/.immich'), + expect.any(Buffer), + ); + expect(mocks.storage.createFile).toHaveBeenCalledWith( + expect.stringContaining('/data/backups/.immich'), + expect.any(Buffer), + ); }); it('should enable mount folder checking for a new folder type', async () => { @@ -57,6 +84,15 @@ describe(StorageService.name, () => { upload: true, }, }); + mocks.asset.getFileSamples.mockResolvedValue([]); + mocks.config.getEnv.mockReturnValue( + mockEnvData({ + storage: { + ignoreMountCheckErrors: false, + mediaLocation: '/data', + }, + }), + ); await expect(sut.onBootstrap()).resolves.toBeUndefined(); @@ -71,11 +107,17 @@ describe(StorageService.name, () => { }, }); expect(mocks.storage.mkdirSync).toHaveBeenCalledTimes(2); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith('upload/library'); - expect(mocks.storage.mkdirSync).toHaveBeenCalledWith('upload/backups'); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/library')); + expect(mocks.storage.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/data/backups')); expect(mocks.storage.createFile).toHaveBeenCalledTimes(2); - expect(mocks.storage.createFile).toHaveBeenCalledWith('upload/library/.immich', expect.any(Buffer)); - expect(mocks.storage.createFile).toHaveBeenCalledWith('upload/backups/.immich', expect.any(Buffer)); + expect(mocks.storage.createFile).toHaveBeenCalledWith( + expect.stringContaining('/data/library/.immich'), + expect.any(Buffer), + ); + expect(mocks.storage.createFile).toHaveBeenCalledWith( + expect.stringContaining('/data/backups/.immich'), + expect.any(Buffer), + ); }); it('should throw an error if .immich is missing', async () => { @@ -104,6 +146,7 @@ describe(StorageService.name, () => { error.code = 'EEXIST'; mocks.systemMetadata.get.mockResolvedValue({ mountChecks: {} }); mocks.storage.createFile.mockRejectedValue(error); + mocks.asset.getFileSamples.mockResolvedValue([]); await expect(sut.onBootstrap()).resolves.toBeUndefined(); @@ -125,13 +168,14 @@ describe(StorageService.name, () => { storage: { ignoreMountCheckErrors: true }, }), ); + mocks.asset.getFileSamples.mockResolvedValue([]); mocks.storage.overwriteFile.mockRejectedValue( new Error("ENOENT: no such file or directory, open '/app/.immich'"), ); await expect(sut.onBootstrap()).resolves.toBeUndefined(); - expect(mocks.systemMetadata.set).not.toHaveBeenCalled(); + expect(mocks.systemMetadata.set).not.toHaveBeenCalledWith(SystemMetadataKey.SystemFlags, expect.anything()); }); }); diff --git a/server/src/services/storage.service.ts b/server/src/services/storage.service.ts index 6e3ef4820a..5b08204bf9 100644 --- a/server/src/services/storage.service.ts +++ b/server/src/services/storage.service.ts @@ -1,8 +1,16 @@ import { Injectable } from '@nestjs/common'; -import { join, resolve } from 'node:path'; +import { join } from 'node:path'; import { StorageCore } from 'src/cores/storage.core'; import { OnEvent, OnJob } from 'src/decorators'; -import { DatabaseLock, JobName, JobStatus, QueueName, StorageFolder, SystemMetadataKey } from 'src/enum'; +import { + BootstrapEventPriority, + DatabaseLock, + JobName, + JobStatus, + QueueName, + StorageFolder, + SystemMetadataKey, +} from 'src/enum'; import { BaseService } from 'src/services/base.service'; import { JobOf, SystemFlags } from 'src/types'; import { ImmichStartupError } from 'src/utils/misc'; @@ -11,9 +19,32 @@ const docsMessage = `Please see https://immich.app/docs/administration/system-in @Injectable() export class StorageService extends BaseService { - @OnEvent({ name: 'AppBootstrap' }) - async onBootstrap() { + private detectMediaLocation(): string { const envData = this.configRepository.getEnv(); + if (envData.storage.mediaLocation) { + return envData.storage.mediaLocation; + } + + const targets: string[] = []; + const candidates = ['/data', '/usr/src/app/upload']; + + for (const candidate of candidates) { + const exists = this.storageRepository.existsSync(candidate); + if (exists) { + targets.push(candidate); + } + } + + if (targets.length === 1) { + return targets[0]; + } + + return '/usr/src/app/upload'; + } + + @OnEvent({ name: 'AppBootstrap', priority: BootstrapEventPriority.StorageService }) + async onBootstrap() { + StorageCore.setMediaLocation(this.detectMediaLocation()); await this.databaseRepository.withLock(DatabaseLock.SystemFileMounts, async () => { const flags = @@ -52,6 +83,7 @@ export class StorageService extends BaseService { this.logger.log('Successfully verified system mount folder checks'); } catch (error) { + const envData = this.configRepository.getEnv(); if (envData.storage.ignoreMountCheckErrors) { this.logger.error(error as Error); this.logger.warn('Ignoring mount folder errors'); @@ -60,6 +92,41 @@ export class StorageService extends BaseService { } } }); + + await this.databaseRepository.withLock(DatabaseLock.MediaLocation, async () => { + const current = StorageCore.getMediaLocation(); + const samples = await this.assetRepository.getFileSamples(); + if (samples.length > 0) { + const path = samples[0].path; + const savedValue = await this.systemMetadataRepository.get(SystemMetadataKey.MediaLocation); + let previous = savedValue?.location || ''; + + if (!previous && this.configRepository.getEnv().storage.mediaLocation) { + previous = current; + } + + if (!previous) { + previous = path.startsWith('upload/') ? 'upload' : '/usr/src/app/upload'; + } + + if (previous !== current) { + this.logger.log(`Media location changed (from=${previous}, to=${current})`); + + if (!path.startsWith(previous)) { + throw new Error( + 'Detected an inconsistent media location. For more information, see https://immich.app/errors#inconsistent-media-location', + ); + } + + this.logger.warn( + `Detected a change to media location, performing an automatic migration of file paths from ${previous} to ${current}, this may take awhile`, + ); + await this.databaseRepository.migrateFilePaths(previous, current); + } + } + + await this.systemMetadataRepository.set(SystemMetadataKey.MediaLocation, { location: current }); + }); } @OnJob({ name: JobName.FileDelete, queue: QueueName.BackgroundTask }) @@ -87,9 +154,8 @@ export class StorageService extends BaseService { try { await this.storageRepository.readFile(internalPath); } catch (error) { - const fullyQualifiedPath = resolve(process.cwd(), internalPath); - this.logger.error(`Failed to read ${fullyQualifiedPath} (${internalPath}): ${error}`); - throw new ImmichStartupError(`Failed to read: "${externalPath} (${fullyQualifiedPath}) - ${docsMessage}"`); + this.logger.error(`Failed to read (${internalPath}): ${error}`); + throw new ImmichStartupError(`Failed to read: "${externalPath} (${internalPath}) - ${docsMessage}"`); } } diff --git a/server/src/services/sync.service.ts b/server/src/services/sync.service.ts index 4463ab0d76..fee77f35ba 100644 --- a/server/src/services/sync.service.ts +++ b/server/src/services/sync.service.ts @@ -54,6 +54,7 @@ const sendEntityBackfillCompleteAck = (response: Writable, ackType: SyncEntityTy const FULL_SYNC = { needsFullSync: true, deleted: [], upserted: [] }; export const SYNC_TYPES_ORDER = [ + SyncRequestType.AuthUsersV1, SyncRequestType.UsersV1, SyncRequestType.PartnersV1, SyncRequestType.AssetsV1, @@ -70,6 +71,7 @@ export const SYNC_TYPES_ORDER = [ SyncRequestType.MemoriesV1, SyncRequestType.MemoryToAssetsV1, SyncRequestType.PeopleV1, + SyncRequestType.AssetFacesV1, SyncRequestType.UserMetadataV1, ]; @@ -139,6 +141,7 @@ export class SyncService extends BaseService { const checkpointMap: CheckpointMap = Object.fromEntries(checkpoints.map(({ type, ack }) => [type, fromAck(ack)])); const handlers: Record Promise> = { + [SyncRequestType.AuthUsersV1]: () => this.syncAuthUsersV1(response, checkpointMap), [SyncRequestType.UsersV1]: () => this.syncUsersV1(response, checkpointMap), [SyncRequestType.PartnersV1]: () => this.syncPartnersV1(response, checkpointMap, auth), [SyncRequestType.AssetsV1]: () => this.syncAssetsV1(response, checkpointMap, auth), @@ -156,6 +159,7 @@ export class SyncService extends BaseService { [SyncRequestType.StacksV1]: () => this.syncStackV1(response, checkpointMap, auth), [SyncRequestType.PartnerStacksV1]: () => this.syncPartnerStackV1(response, checkpointMap, auth, session.id), [SyncRequestType.PeopleV1]: () => this.syncPeopleV1(response, checkpointMap, auth), + [SyncRequestType.AssetFacesV1]: async () => this.syncAssetFacesV1(response, checkpointMap, auth), [SyncRequestType.UserMetadataV1]: () => this.syncUserMetadataV1(response, checkpointMap, auth), }; @@ -167,6 +171,14 @@ export class SyncService extends BaseService { response.end(); } + private async syncAuthUsersV1(response: Writable, checkpointMap: CheckpointMap) { + const upsertType = SyncEntityType.AuthUserV1; + const upserts = this.syncRepository.authUser.getUpserts(checkpointMap[upsertType]); + for await (const { updateId, profileImagePath, ...data } of upserts) { + send(response, { type: upsertType, ids: [updateId], data: { ...data, hasProfileImage: !!profileImagePath } }); + } + } + private async syncUsersV1(response: Writable, checkpointMap: CheckpointMap) { const deleteType = SyncEntityType.UserDeleteV1; const deletes = this.syncRepository.user.getDeletes(checkpointMap[deleteType]); @@ -176,8 +188,8 @@ export class SyncService extends BaseService { const upsertType = SyncEntityType.UserV1; const upserts = this.syncRepository.user.getUpserts(checkpointMap[upsertType]); - for await (const { updateId, ...data } of upserts) { - send(response, { type: upsertType, ids: [updateId], data }); + for await (const { updateId, profileImagePath, ...data } of upserts) { + send(response, { type: upsertType, ids: [updateId], data: { ...data, hasProfileImage: !!profileImagePath } }); } } @@ -606,6 +618,20 @@ export class SyncService extends BaseService { } } + private async syncAssetFacesV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto) { + const deleteType = SyncEntityType.AssetFaceDeleteV1; + const deletes = this.syncRepository.assetFace.getDeletes(auth.user.id, checkpointMap[deleteType]); + for await (const { id, ...data } of deletes) { + send(response, { type: deleteType, ids: [id], data }); + } + + const upsertType = SyncEntityType.AssetFaceV1; + const upserts = this.syncRepository.assetFace.getUpserts(auth.user.id, checkpointMap[upsertType]); + for await (const { updateId, ...data } of upserts) { + send(response, { type: upsertType, ids: [updateId], data }); + } + } + private async syncUserMetadataV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto) { const deleteType = SyncEntityType.UserMetadataDeleteV1; const deletes = this.syncRepository.userMetadata.getDeletes(auth.user.id, checkpointMap[deleteType]); diff --git a/server/src/services/user.service.spec.ts b/server/src/services/user.service.spec.ts index 3a85389ace..bd896ffc24 100644 --- a/server/src/services/user.service.spec.ts +++ b/server/src/services/user.service.spec.ts @@ -235,11 +235,26 @@ describe(UserService.name, () => { await sut.handleUserDelete({ id: user.id }); - expect(mocks.storage.unlinkDir).toHaveBeenCalledWith('upload/library/deleted-user', options); - expect(mocks.storage.unlinkDir).toHaveBeenCalledWith('upload/upload/deleted-user', options); - expect(mocks.storage.unlinkDir).toHaveBeenCalledWith('upload/profile/deleted-user', options); - expect(mocks.storage.unlinkDir).toHaveBeenCalledWith('upload/thumbs/deleted-user', options); - expect(mocks.storage.unlinkDir).toHaveBeenCalledWith('upload/encoded-video/deleted-user', options); + expect(mocks.storage.unlinkDir).toHaveBeenCalledWith( + expect.stringContaining('/data/library/deleted-user'), + options, + ); + expect(mocks.storage.unlinkDir).toHaveBeenCalledWith( + expect.stringContaining('/data/upload/deleted-user'), + options, + ); + expect(mocks.storage.unlinkDir).toHaveBeenCalledWith( + expect.stringContaining('/data/profile/deleted-user'), + options, + ); + expect(mocks.storage.unlinkDir).toHaveBeenCalledWith( + expect.stringContaining('/data/thumbs/deleted-user'), + options, + ); + expect(mocks.storage.unlinkDir).toHaveBeenCalledWith( + expect.stringContaining('/data/encoded-video/deleted-user'), + options, + ); expect(mocks.album.deleteAll).toHaveBeenCalledWith(user.id); expect(mocks.user.delete).toHaveBeenCalledWith(user, true); }); @@ -253,7 +268,7 @@ describe(UserService.name, () => { const options = { force: true, recursive: true }; - expect(mocks.storage.unlinkDir).toHaveBeenCalledWith('upload/library/admin', options); + expect(mocks.storage.unlinkDir).toHaveBeenCalledWith(expect.stringContaining('data/library/admin'), options); }); }); diff --git a/server/src/types.ts b/server/src/types.ts index 79aeedd47d..9cd1aa996b 100644 --- a/server/src/types.ts +++ b/server/src/types.ts @@ -451,11 +451,13 @@ export type MemoriesState = { /** memories have already been created through this date */ lastOnThisDayDate: string; }; +export type MediaLocation = { location: string }; export interface SystemMetadata extends Record> { [SystemMetadataKey.AdminOnboarding]: { isOnboarded: boolean }; [SystemMetadataKey.FacialRecognitionState]: { lastRun?: string }; [SystemMetadataKey.License]: { licenseKey: string; activationKey: string; activatedAt: Date }; + [SystemMetadataKey.MediaLocation]: MediaLocation; [SystemMetadataKey.ReverseGeocodingState]: { lastUpdate?: string; lastImportFileName?: string }; [SystemMetadataKey.SystemConfig]: DeepPartial; [SystemMetadataKey.SystemFlags]: DeepPartial; diff --git a/server/src/utils/access.ts b/server/src/utils/access.ts index 08ff81e840..8427da6f1b 100644 --- a/server/src/utils/access.ts +++ b/server/src/utils/access.ts @@ -92,7 +92,7 @@ const checkSharedLinkAccess = async ( return sharedLink.allowDownload ? await access.album.checkSharedLinkAccess(sharedLinkId, ids) : new Set(); } - case Permission.AlbumAddAsset: { + case Permission.AlbumAssetCreate: { return sharedLink.allowUpload ? await access.album.checkSharedLinkAccess(sharedLinkId, ids) : new Set(); } @@ -163,7 +163,7 @@ const checkOtherAccess = async (access: AccessRepository, request: OtherAccessRe return setUnion(isOwner, isShared); } - case Permission.AlbumAddAsset: { + case Permission.AlbumAssetCreate: { const isOwner = await access.album.checkOwnerAccess(auth.user.id, ids); const isShared = await access.album.checkSharedAlbumAccess( auth.user.id, @@ -195,7 +195,7 @@ const checkOtherAccess = async (access: AccessRepository, request: OtherAccessRe return setUnion(isOwner, isShared); } - case Permission.AlbumRemoveAsset: { + case Permission.AlbumAssetDelete: { const isOwner = await access.album.checkOwnerAccess(auth.user.id, ids); const isShared = await access.album.checkSharedAlbumAccess( auth.user.id, diff --git a/server/src/utils/file.ts b/server/src/utils/file.ts index 3e1a1b7f68..2331a45a62 100644 --- a/server/src/utils/file.ts +++ b/server/src/utils/file.ts @@ -1,7 +1,7 @@ import { HttpException, StreamableFile } from '@nestjs/common'; import { NextFunction, Response } from 'express'; import { access, constants } from 'node:fs/promises'; -import { basename, extname, isAbsolute } from 'node:path'; +import { basename, extname } from 'node:path'; import { promisify } from 'node:util'; import { CacheControl } from 'src/enum'; import { LoggingRepository } from 'src/repositories/logging.repository'; @@ -62,15 +62,9 @@ export const sendFile = async ( res.header('Content-Disposition', `inline; filename*=UTF-8''${encodeURIComponent(file.fileName)}`); } - // configure options for serving - const options: SendFileOptions = { dotfiles: 'allow' }; - if (!isAbsolute(file.path)) { - options.root = process.cwd(); - } - await access(file.path, constants.R_OK); - return await _sendFile(file.path, options); + return await _sendFile(file.path, { dotfiles: 'allow' }); } catch (error: Error | any) { // ignore client-closed connection if (isConnectionAborted(error) || res.headersSent) { diff --git a/server/src/utils/misc.ts b/server/src/utils/misc.ts index 3acb72b663..a32632b52d 100644 --- a/server/src/utils/misc.ts +++ b/server/src/utils/misc.ts @@ -6,7 +6,11 @@ import { SwaggerDocumentOptions, SwaggerModule, } from '@nestjs/swagger'; -import { ReferenceObject, SchemaObject } from '@nestjs/swagger/dist/interfaces/open-api-spec.interface'; +import { + OperationObject, + ReferenceObject, + SchemaObject, +} from '@nestjs/swagger/dist/interfaces/open-api-spec.interface'; import _ from 'lodash'; import { writeFileSync } from 'node:fs'; import path from 'node:path'; @@ -15,7 +19,7 @@ import parse from 'picomatch/lib/parse'; import { SystemConfig } from 'src/config'; import { CLIP_MODEL_INFO, serverVersion } from 'src/constants'; import { extraSyncModels } from 'src/dtos/sync.dto'; -import { ImmichCookie, ImmichHeader, MetadataKey } from 'src/enum'; +import { ApiCustomExtension, ImmichCookie, ImmichHeader, MetadataKey } from 'src/enum'; import { LoggingRepository } from 'src/repositories/logging.repository'; export class ImmichStartupError extends Error {} @@ -198,7 +202,12 @@ const patchOpenAPI = (document: OpenAPIObject) => { trace: path.trace, }; - for (const operation of Object.values(operations)) { + for (const operation of Object.values(operations) as Array< + OperationObject & { + [ApiCustomExtension.AdminOnly]?: boolean; + [ApiCustomExtension.Permission]?: string; + } + >) { if (!operation) { continue; } @@ -211,12 +220,21 @@ const patchOpenAPI = (document: OpenAPIObject) => { // console.log(`${routeToErrorMessage(operation.operationId).padEnd(40)} (${operation.operationId})`); } - if (operation.description === '') { - delete operation.description; - } + const adminOnly = operation[ApiCustomExtension.AdminOnly] ?? false; + const permission = operation[ApiCustomExtension.Permission]; + if (permission) { + let description = (operation.description || '').trim(); + if (description && !description.endsWith('.')) { + description += '. '; + } - if (operation.parameters) { - operation.parameters = _.orderBy(operation.parameters, 'name'); + operation.description = + description + + `This endpoint ${adminOnly ? 'is an admin-only route, and ' : ''}requires the \`${permission}\` permission.`; + + if (operation.parameters) { + operation.parameters = _.orderBy(operation.parameters, 'name'); + } } } } diff --git a/server/src/validation.ts b/server/src/validation.ts index 049b5432d6..3f7e1c6f3b 100644 --- a/server/src/validation.ts +++ b/server/src/validation.ts @@ -63,6 +63,22 @@ export class FileNotEmptyValidator extends FileValidator { } } +type UUIDOptions = { optional?: boolean; each?: boolean; nullable?: boolean }; +export const ValidateUUID = (options?: UUIDOptions & ApiPropertyOptions) => { + const { optional, each, nullable, ...apiPropertyOptions } = { + optional: false, + each: false, + nullable: false, + ...options, + }; + return applyDecorators( + IsUUID('4', { each }), + ApiProperty({ format: 'uuid', ...apiPropertyOptions }), + optional ? Optional({ nullable }) : IsNotEmpty(), + each ? IsArray() : IsString(), + ); +}; + export class UUIDParamDto { @IsNotEmpty() @IsUUID('4') @@ -70,6 +86,14 @@ export class UUIDParamDto { id!: string; } +export class UUIDAssetIDParamDto { + @ValidateUUID() + id!: string; + + @ValidateUUID() + assetId!: string; +} + type PinCodeOptions = { optional?: boolean } & OptionalOptions; export const PinCode = (options?: PinCodeOptions & ApiPropertyOptions) => { const { optional, nullable, emptyToNull, ...apiPropertyOptions } = { @@ -131,22 +155,6 @@ export const ValidateHexColor = () => { return applyDecorators(...decorators); }; -type UUIDOptions = { optional?: boolean; each?: boolean; nullable?: boolean }; -export const ValidateUUID = (options?: UUIDOptions & ApiPropertyOptions) => { - const { optional, each, nullable, ...apiPropertyOptions } = { - optional: false, - each: false, - nullable: false, - ...options, - }; - return applyDecorators( - IsUUID('4', { each }), - ApiProperty({ format: 'uuid', ...apiPropertyOptions }), - optional ? Optional({ nullable }) : IsNotEmpty(), - each ? IsArray() : IsString(), - ); -}; - type DateOptions = { optional?: boolean; nullable?: boolean; format?: 'date' | 'date-time' }; export const ValidateDate = (options?: DateOptions & ApiPropertyOptions) => { const { optional, nullable, format, ...apiPropertyOptions } = { diff --git a/server/start.sh b/server/start.sh deleted file mode 100755 index 1a08d01a75..0000000000 --- a/server/start.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env bash - -echo "Initializing Immich $IMMICH_SOURCE_REF" - -lib_path="/usr/lib/$(arch)-linux-gnu/libmimalloc.so.2" -export LD_PRELOAD="$lib_path" -export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/lib/jellyfin-ffmpeg/lib" - -read_file_and_export() { - if [ -n "${!1}" ]; then - content="$(cat "${!1}")" - export "$2"="${content}" - unset "$1" - fi -} -read_file_and_export "DB_URL_FILE" "DB_URL" -read_file_and_export "DB_HOSTNAME_FILE" "DB_HOSTNAME" -read_file_and_export "DB_DATABASE_NAME_FILE" "DB_DATABASE_NAME" -read_file_and_export "DB_USERNAME_FILE" "DB_USERNAME" -read_file_and_export "DB_PASSWORD_FILE" "DB_PASSWORD" -read_file_and_export "REDIS_PASSWORD_FILE" "REDIS_PASSWORD" - -export CPU_CORES="${CPU_CORES:=$(./get-cpus.sh)}" -echo "Detected CPU Cores: $CPU_CORES" -if [ "$CPU_CORES" -gt 4 ]; then - export UV_THREADPOOL_SIZE=$CPU_CORES -fi - -exec node /usr/src/app/dist/main "$@" diff --git a/server/test/fixtures/asset.stub.ts b/server/test/fixtures/asset.stub.ts index 991c5d2c4f..066996ead5 100644 --- a/server/test/fixtures/asset.stub.ts +++ b/server/test/fixtures/asset.stub.ts @@ -65,7 +65,7 @@ export const assetStub = { owner: userStub.user1, ownerId: 'user-id', deviceId: 'device-id', - originalPath: 'upload/library/IMG_123.jpg', + originalPath: '/data/library/IMG_123.jpg', files: [thumbnailFile], checksum: Buffer.from('file hash', 'utf8'), type: AssetType.Image, @@ -101,7 +101,7 @@ export const assetStub = { owner: userStub.user1, ownerId: 'user-id', deviceId: 'device-id', - originalPath: 'upload/library/IMG_456.jpg', + originalPath: '/data/library/IMG_456.jpg', files: [previewFile], checksum: Buffer.from('file hash', 'utf8'), type: AssetType.Image, @@ -462,7 +462,7 @@ export const assetStub = { }), imageFrom2015: Object.freeze({ - id: 'asset-id-1', + id: 'asset-id-2015', status: AssetStatus.Active, deviceAssetId: 'device-asset-id', fileModifiedAt: new Date('2015-02-23T05:06:29.716Z'), @@ -484,6 +484,9 @@ export const assetStub = { duration: null, livePhotoVideo: null, livePhotoVideoId: null, + updateId: 'foo', + libraryId: null, + stackId: null, sharedLinks: [], originalFileName: 'asset-id.ext', faces: [], diff --git a/server/test/fixtures/face.stub.ts b/server/test/fixtures/face.stub.ts index beecf7c69e..f655a3944e 100644 --- a/server/test/fixtures/face.stub.ts +++ b/server/test/fixtures/face.stub.ts @@ -23,6 +23,8 @@ export const faceStub = { sourceType: SourceType.MachineLearning, faceSearch: { faceId: 'assetFaceId1', embedding: '[1, 2, 3, 4]' }, deletedAt: new Date(), + updatedAt: new Date('2023-01-01T00:00:00Z'), + updateId: '0d1173e3-4d80-4d76-b41e-57d56de21125', }), primaryFace1: Object.freeze({ id: 'assetFaceId2', @@ -39,6 +41,8 @@ export const faceStub = { sourceType: SourceType.MachineLearning, faceSearch: { faceId: 'assetFaceId2', embedding: '[1, 2, 3, 4]' }, deletedAt: null, + updatedAt: new Date('2023-01-01T00:00:00Z'), + updateId: '0d1173e3-4d80-4d76-b41e-57d56de21125', }), mergeFace1: Object.freeze({ id: 'assetFaceId3', @@ -55,6 +59,8 @@ export const faceStub = { sourceType: SourceType.MachineLearning, faceSearch: { faceId: 'assetFaceId3', embedding: '[1, 2, 3, 4]' }, deletedAt: null, + updatedAt: new Date('2023-01-01T00:00:00Z'), + updateId: '0d1173e3-4d80-4d76-b41e-57d56de21125', }), noPerson1: Object.freeze({ id: 'assetFaceId8', @@ -71,6 +77,8 @@ export const faceStub = { sourceType: SourceType.MachineLearning, faceSearch: { faceId: 'assetFaceId8', embedding: '[1, 2, 3, 4]' }, deletedAt: null, + updatedAt: new Date('2023-01-01T00:00:00Z'), + updateId: '0d1173e3-4d80-4d76-b41e-57d56de21125', }), noPerson2: Object.freeze({ id: 'assetFaceId9', @@ -87,6 +95,8 @@ export const faceStub = { sourceType: SourceType.MachineLearning, faceSearch: { faceId: 'assetFaceId9', embedding: '[1, 2, 3, 4]' }, deletedAt: null, + updatedAt: new Date('2023-01-01T00:00:00Z'), + updateId: '0d1173e3-4d80-4d76-b41e-57d56de21125', }), fromExif1: Object.freeze({ id: 'assetFaceId9', @@ -102,6 +112,8 @@ export const faceStub = { imageWidth: 400, sourceType: SourceType.Exif, deletedAt: null, + updatedAt: new Date('2023-01-01T00:00:00Z'), + updateId: '0d1173e3-4d80-4d76-b41e-57d56de21125', }), fromExif2: Object.freeze({ id: 'assetFaceId9', @@ -117,6 +129,8 @@ export const faceStub = { imageWidth: 1024, sourceType: SourceType.Exif, deletedAt: null, + updatedAt: new Date('2023-01-01T00:00:00Z'), + updateId: '0d1173e3-4d80-4d76-b41e-57d56de21125', }), withBirthDate: Object.freeze({ id: 'assetFaceId10', @@ -132,5 +146,7 @@ export const faceStub = { imageWidth: 1024, sourceType: SourceType.MachineLearning, deletedAt: null, + updatedAt: new Date('2023-01-01T00:00:00Z'), + updateId: '0d1173e3-4d80-4d76-b41e-57d56de21125', }), }; diff --git a/server/test/fixtures/shared-link.stub.ts b/server/test/fixtures/shared-link.stub.ts index 47201a5b3b..1cd36f1f23 100644 --- a/server/test/fixtures/shared-link.stub.ts +++ b/server/test/fixtures/shared-link.stub.ts @@ -118,6 +118,7 @@ export const sharedLinkStub = { description: null, assets: [assetStub.image], password: 'password', + slug: null, }), valid: Object.freeze({ id: '123', @@ -135,6 +136,7 @@ export const sharedLinkStub = { password: null, assets: [] as MapAsset[], album: null, + slug: null, }), expired: Object.freeze({ id: '123', @@ -152,6 +154,7 @@ export const sharedLinkStub = { albumId: null, assets: [] as MapAsset[], album: null, + slug: null, }), readonlyNoExif: Object.freeze({ id: '123', @@ -166,6 +169,7 @@ export const sharedLinkStub = { description: null, password: null, assets: [], + slug: null, albumId: 'album-123', album: { id: 'album-123', @@ -266,6 +270,7 @@ export const sharedLinkStub = { allowUpload: true, allowDownload: true, showExif: true, + slug: null, description: null, password: 'password', assets: [], @@ -288,6 +293,7 @@ export const sharedLinkResponseStub = { showMetadata: true, type: SharedLinkType.Album, userId: 'admin_id', + slug: null, }), expired: Object.freeze({ album: undefined, @@ -303,6 +309,7 @@ export const sharedLinkResponseStub = { showMetadata: true, type: SharedLinkType.Album, userId: 'admin_id', + slug: null, }), readonlyNoMetadata: Object.freeze({ id: '123', @@ -316,6 +323,7 @@ export const sharedLinkResponseStub = { allowUpload: false, allowDownload: false, showMetadata: false, + slug: null, album: { ...albumResponse, startDate: assetResponse.localDateTime, endDate: assetResponse.localDateTime }, assets: [{ ...assetResponseWithoutMetadata, exifInfo: undefined }], }), diff --git a/server/test/medium.factory.ts b/server/test/medium.factory.ts index 4d13264fa2..1b669e83e4 100644 --- a/server/test/medium.factory.ts +++ b/server/test/medium.factory.ts @@ -154,6 +154,12 @@ export class MediumTestContext { return { asset, result }; } + async newAssetFace(dto: Partial> & { assetId: string }) { + const assetFace = mediumFactory.assetFaceInsert(dto); + const result = await this.get(PersonRepository).createAssetFace(assetFace); + return { assetFace, result }; + } + async newMemory(dto: Partial> = {}) { const memory = mediumFactory.memoryInsert(dto); const result = await this.get(MemoryRepository).create(memory, new Set()); @@ -501,7 +507,14 @@ const userInsert = (user: Partial> = {}) => { deletedAt: null, isAdmin: false, profileImagePath: '', + profileChangedAt: newDate(), shouldChangePassword: true, + storageLabel: null, + pinCode: null, + oauthId: '', + avatarColor: null, + quotaSizeInBytes: null, + quotaUsageInBytes: 0, }; return { ...defaults, ...user, id }; diff --git a/server/test/medium/specs/services/search.service.spec.ts b/server/test/medium/specs/services/search.service.spec.ts new file mode 100644 index 0000000000..517e6cc277 --- /dev/null +++ b/server/test/medium/specs/services/search.service.spec.ts @@ -0,0 +1,55 @@ +import { Kysely } from 'kysely'; +import { AccessRepository } from 'src/repositories/access.repository'; +import { DatabaseRepository } from 'src/repositories/database.repository'; +import { LoggingRepository } from 'src/repositories/logging.repository'; +import { PartnerRepository } from 'src/repositories/partner.repository'; +import { PersonRepository } from 'src/repositories/person.repository'; +import { SearchRepository } from 'src/repositories/search.repository'; +import { DB } from 'src/schema'; +import { SearchService } from 'src/services/search.service'; +import { newMediumService } from 'test/medium.factory'; +import { factory } from 'test/small.factory'; +import { getKyselyDB } from 'test/utils'; + +let defaultDatabase: Kysely; + +const setup = (db?: Kysely) => { + return newMediumService(SearchService, { + database: db || defaultDatabase, + real: [AccessRepository, DatabaseRepository, SearchRepository, PartnerRepository, PersonRepository], + mock: [LoggingRepository], + }); +}; + +beforeAll(async () => { + defaultDatabase = await getKyselyDB(); +}); + +describe(SearchService.name, () => { + it('should work', () => { + const { sut } = setup(); + expect(sut).toBeDefined(); + }); + + it('should return assets', async () => { + const { sut, ctx } = setup(); + const { user } = await ctx.newUser(); + + const assets = []; + const sizes = [12_334, 599, 123_456]; + + for (let i = 0; i < sizes.length; i++) { + const { asset } = await ctx.newAsset({ ownerId: user.id }); + await ctx.newExif({ assetId: asset.id, fileSizeInByte: sizes[i] }); + assets.push(asset); + } + + const auth = factory.auth({ user: { id: user.id } }); + + await expect(sut.searchLargeAssets(auth, {})).resolves.toEqual([ + expect.objectContaining({ id: assets[2].id }), + expect.objectContaining({ id: assets[0].id }), + expect.objectContaining({ id: assets[1].id }), + ]); + }); +}); diff --git a/server/test/medium/specs/services/storage.service.spec.ts b/server/test/medium/specs/services/storage.service.spec.ts new file mode 100644 index 0000000000..6c3fc487d4 --- /dev/null +++ b/server/test/medium/specs/services/storage.service.spec.ts @@ -0,0 +1,46 @@ +import { Kysely } from 'kysely'; +import { AssetRepository } from 'src/repositories/asset.repository'; +import { ConfigRepository } from 'src/repositories/config.repository'; +import { DatabaseRepository } from 'src/repositories/database.repository'; +import { LoggingRepository } from 'src/repositories/logging.repository'; +import { StorageRepository } from 'src/repositories/storage.repository'; +import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository'; +import { DB } from 'src/schema'; +import { StorageService } from 'src/services/storage.service'; +import { newMediumService } from 'test/medium.factory'; +import { mockEnvData } from 'test/repositories/config.repository.mock'; +import { getKyselyDB } from 'test/utils'; + +let defaultDatabase: Kysely; + +const setup = (db?: Kysely) => { + return newMediumService(StorageService, { + database: db || defaultDatabase, + real: [AssetRepository, DatabaseRepository, SystemMetadataRepository], + mock: [StorageRepository, ConfigRepository, LoggingRepository], + }); +}; + +beforeAll(async () => { + defaultDatabase = await getKyselyDB(); +}); + +describe(StorageService.name, () => { + describe('onBoostrap', () => { + it('should work', async () => { + const { sut, ctx } = setup(); + + const configMock = ctx.getMock(ConfigRepository); + configMock.getEnv.mockReturnValue(mockEnvData({})); + + const storageMock = ctx.getMock(StorageRepository); + storageMock.mkdirSync.mockReturnValue(void 0); + storageMock.existsSync.mockReturnValue(true); + storageMock.createFile.mockResolvedValue(void 0); + storageMock.overwriteFile.mockResolvedValue(void 0); + storageMock.readFile.mockResolvedValue(Buffer.from('test content')); + + await expect(sut.onBootstrap()).resolves.toBeUndefined(); + }); + }); +}); diff --git a/server/test/medium/specs/sync/sync-album-asset.spec.ts b/server/test/medium/specs/sync/sync-album-asset.spec.ts index 9a42c0f027..3002b99071 100644 --- a/server/test/medium/specs/sync/sync-album-asset.spec.ts +++ b/server/test/medium/specs/sync/sync-album-asset.spec.ts @@ -38,6 +38,7 @@ describe(SyncRequestType.AlbumAssetsV1, () => { duration: '0:10:00.00000', livePhotoVideoId: null, stackId: null, + libraryId: null, }); const { album } = await ctx.newAlbum({ ownerId: user2.id }); await ctx.newAlbumAsset({ albumId: album.id, assetId: asset.id }); @@ -64,6 +65,7 @@ describe(SyncRequestType.AlbumAssetsV1, () => { duration: asset.duration, livePhotoVideoId: asset.livePhotoVideoId, stackId: asset.stackId, + libraryId: asset.libraryId, }, type: SyncEntityType.AlbumAssetV1, }, diff --git a/server/test/medium/specs/sync/sync-asset-face.spec.ts b/server/test/medium/specs/sync/sync-asset-face.spec.ts new file mode 100644 index 0000000000..68d3007c52 --- /dev/null +++ b/server/test/medium/specs/sync/sync-asset-face.spec.ts @@ -0,0 +1,92 @@ +import { Kysely } from 'kysely'; +import { SyncEntityType, SyncRequestType } from 'src/enum'; +import { PersonRepository } from 'src/repositories/person.repository'; +import { DB } from 'src/schema'; +import { SyncTestContext } from 'test/medium.factory'; +import { factory } from 'test/small.factory'; +import { getKyselyDB } from 'test/utils'; + +let defaultDatabase: Kysely; + +const setup = async (db?: Kysely) => { + const ctx = new SyncTestContext(db || defaultDatabase); + const { auth, user, session } = await ctx.newSyncAuthUser(); + return { auth, user, session, ctx }; +}; + +beforeAll(async () => { + defaultDatabase = await getKyselyDB(); +}); + +describe(SyncEntityType.AssetFaceV1, () => { + it('should detect and sync the first asset face', async () => { + const { auth, ctx } = await setup(); + const { asset } = await ctx.newAsset({ ownerId: auth.user.id }); + const { person } = await ctx.newPerson({ ownerId: auth.user.id }); + const { assetFace } = await ctx.newAssetFace({ assetId: asset.id, personId: person.id }); + + const response = await ctx.syncStream(auth, [SyncRequestType.AssetFacesV1]); + expect(response).toHaveLength(1); + expect(response).toEqual([ + { + ack: expect.any(String), + data: expect.objectContaining({ + id: assetFace.id, + assetId: asset.id, + personId: person.id, + imageWidth: assetFace.imageWidth, + imageHeight: assetFace.imageHeight, + boundingBoxX1: assetFace.boundingBoxX1, + boundingBoxY1: assetFace.boundingBoxY1, + boundingBoxX2: assetFace.boundingBoxX2, + boundingBoxY2: assetFace.boundingBoxY2, + sourceType: assetFace.sourceType, + }), + type: 'AssetFaceV1', + }, + ]); + + await ctx.syncAckAll(auth, response); + await expect(ctx.syncStream(auth, [SyncRequestType.AssetFacesV1])).resolves.toEqual([]); + }); + + it('should detect and sync a deleted asset face', async () => { + const { auth, ctx } = await setup(); + const personRepo = ctx.get(PersonRepository); + const { asset } = await ctx.newAsset({ ownerId: auth.user.id }); + const { assetFace } = await ctx.newAssetFace({ assetId: asset.id }); + await personRepo.deleteAssetFace(assetFace.id); + + const response = await ctx.syncStream(auth, [SyncRequestType.AssetFacesV1]); + expect(response).toHaveLength(1); + expect(response).toEqual([ + { + ack: expect.any(String), + data: { + assetFaceId: assetFace.id, + }, + type: 'AssetFaceDeleteV1', + }, + ]); + + await ctx.syncAckAll(auth, response); + await expect(ctx.syncStream(auth, [SyncRequestType.AssetFacesV1])).resolves.toEqual([]); + }); + + it('should not sync an asset face or asset face delete for an unrelated user', async () => { + const { auth, ctx } = await setup(); + const personRepo = ctx.get(PersonRepository); + const { user: user2 } = await ctx.newUser(); + const { session } = await ctx.newSession({ userId: user2.id }); + const { asset } = await ctx.newAsset({ ownerId: user2.id }); + const { assetFace } = await ctx.newAssetFace({ assetId: asset.id }); + const auth2 = factory.auth({ session, user: user2 }); + + expect(await ctx.syncStream(auth2, [SyncRequestType.AssetFacesV1])).toHaveLength(1); + expect(await ctx.syncStream(auth, [SyncRequestType.AssetFacesV1])).toHaveLength(0); + + await personRepo.deleteAssetFace(assetFace.id); + expect(await ctx.syncStream(auth2, [SyncRequestType.AssetFacesV1])).toHaveLength(1); + expect(await ctx.syncStream(auth, [SyncRequestType.AssetFacesV1])).toHaveLength(0); + }); +}); diff --git a/server/test/medium/specs/sync/sync-asset.spec.ts b/server/test/medium/specs/sync/sync-asset.spec.ts index 52d6bcb524..ce83eed98c 100644 --- a/server/test/medium/specs/sync/sync-asset.spec.ts +++ b/server/test/medium/specs/sync/sync-asset.spec.ts @@ -36,6 +36,7 @@ describe(SyncEntityType.AssetV1, () => { localDateTime: date, deletedAt: null, duration: '0:10:00.00000', + libraryId: null, }); const response = await ctx.syncStream(auth, [SyncRequestType.AssetsV1]); @@ -59,6 +60,7 @@ describe(SyncEntityType.AssetV1, () => { duration: asset.duration, stackId: null, livePhotoVideoId: null, + libraryId: asset.libraryId, }, type: 'AssetV1', }, diff --git a/server/test/medium/specs/sync/sync-auth-user.spec.ts b/server/test/medium/specs/sync/sync-auth-user.spec.ts new file mode 100644 index 0000000000..80ce8b37fa --- /dev/null +++ b/server/test/medium/specs/sync/sync-auth-user.spec.ts @@ -0,0 +1,87 @@ +import { Kysely } from 'kysely'; +import { SyncEntityType, SyncRequestType } from 'src/enum'; +import { UserRepository } from 'src/repositories/user.repository'; +import { DB } from 'src/schema'; +import { SyncTestContext } from 'test/medium.factory'; +import { getKyselyDB } from 'test/utils'; + +let defaultDatabase: Kysely; + +const setup = async (db?: Kysely) => { + const ctx = new SyncTestContext(db || defaultDatabase); + const { auth, user, session } = await ctx.newSyncAuthUser(); + return { auth, user, session, ctx }; +}; + +beforeAll(async () => { + defaultDatabase = await getKyselyDB(); +}); + +describe(SyncEntityType.AuthUserV1, () => { + it('should detect and sync the first user', async () => { + const { auth, user, ctx } = await setup(await getKyselyDB()); + + const response = await ctx.syncStream(auth, [SyncRequestType.AuthUsersV1]); + expect(response).toHaveLength(1); + expect(response).toEqual([ + { + ack: expect.any(String), + data: { + id: user.id, + isAdmin: user.isAdmin, + deletedAt: user.deletedAt, + name: user.name, + avatarColor: user.avatarColor, + email: user.email, + pinCode: user.pinCode, + hasProfileImage: false, + profileChangedAt: (user.profileChangedAt as Date).toISOString(), + oauthId: user.oauthId, + quotaSizeInBytes: user.quotaSizeInBytes, + quotaUsageInBytes: user.quotaUsageInBytes, + storageLabel: user.storageLabel, + }, + type: 'AuthUserV1', + }, + ]); + + await ctx.syncAckAll(auth, response); + await expect(ctx.syncStream(auth, [SyncRequestType.AuthUsersV1])).resolves.toEqual([]); + }); + + it('should sync a change and then another change to that same user', async () => { + const { auth, user, ctx } = await setup(await getKyselyDB()); + + const userRepo = ctx.get(UserRepository); + + const response = await ctx.syncStream(auth, [SyncRequestType.AuthUsersV1]); + expect(response).toHaveLength(1); + expect(response).toEqual([ + { + ack: expect.any(String), + data: expect.objectContaining({ + id: user.id, + isAdmin: false, + }), + type: 'AuthUserV1', + }, + ]); + + await ctx.syncAckAll(auth, response); + + await userRepo.update(user.id, { isAdmin: true }); + + const newResponse = await ctx.syncStream(auth, [SyncRequestType.AuthUsersV1]); + expect(newResponse).toHaveLength(1); + expect(newResponse).toEqual([ + { + ack: expect.any(String), + data: expect.objectContaining({ + id: user.id, + isAdmin: true, + }), + type: 'AuthUserV1', + }, + ]); + }); +}); diff --git a/server/test/medium/specs/sync/sync-partner-asset.spec.ts b/server/test/medium/specs/sync/sync-partner-asset.spec.ts index 2daa750bf3..e9dc7403bd 100644 --- a/server/test/medium/specs/sync/sync-partner-asset.spec.ts +++ b/server/test/medium/specs/sync/sync-partner-asset.spec.ts @@ -40,6 +40,7 @@ describe(SyncRequestType.PartnerAssetsV1, () => { localDateTime: date, deletedAt: null, duration: '0:10:00.00000', + libraryId: null, }); await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id }); @@ -65,6 +66,7 @@ describe(SyncRequestType.PartnerAssetsV1, () => { duration: asset.duration, stackId: null, livePhotoVideoId: null, + libraryId: asset.libraryId, }, type: SyncEntityType.PartnerAssetV1, }, diff --git a/server/test/medium/specs/sync/sync-person.spec.ts b/server/test/medium/specs/sync/sync-person.spec.ts index 807e41894c..fbf401e377 100644 --- a/server/test/medium/specs/sync/sync-person.spec.ts +++ b/server/test/medium/specs/sync/sync-person.spec.ts @@ -31,7 +31,6 @@ describe(SyncEntityType.PersonV1, () => { data: expect.objectContaining({ id: person.id, name: person.name, - thumbnailPath: person.thumbnailPath, isHidden: person.isHidden, birthDate: person.birthDate, faceAssetId: person.faceAssetId, diff --git a/server/test/medium/specs/sync/sync-user.spec.ts b/server/test/medium/specs/sync/sync-user.spec.ts index 24137e3aea..c5d572d7d6 100644 --- a/server/test/medium/specs/sync/sync-user.spec.ts +++ b/server/test/medium/specs/sync/sync-user.spec.ts @@ -35,8 +35,11 @@ describe(SyncEntityType.UserV1, () => { data: { deletedAt: user.deletedAt, email: user.email, + hasProfileImage: user.profileImagePath !== '', id: user.id, name: user.name, + avatarColor: user.avatarColor, + profileChangedAt: user.profileChangedAt.toISOString(), }, type: 'UserV1', }, @@ -49,8 +52,7 @@ describe(SyncEntityType.UserV1, () => { it('should detect and sync a soft deleted user', async () => { const { auth, ctx } = await setup(await getKyselyDB()); - const deletedAt = new Date().toISOString(); - const { user: deleted } = await ctx.newUser({ deletedAt }); + const { user: deleted } = await ctx.newUser({ deletedAt: new Date().toISOString() }); const response = await ctx.syncStream(auth, [SyncRequestType.UsersV1]); @@ -59,22 +61,12 @@ describe(SyncEntityType.UserV1, () => { expect.arrayContaining([ { ack: expect.any(String), - data: { - deletedAt: null, - email: auth.user.email, - id: auth.user.id, - name: auth.user.name, - }, + data: expect.objectContaining({ id: auth.user.id }), type: 'UserV1', }, { ack: expect.any(String), - data: { - deletedAt, - email: deleted.email, - id: deleted.id, - name: deleted.name, - }, + data: expect.objectContaining({ id: deleted.id }), type: 'UserV1', }, ]), @@ -85,7 +77,7 @@ describe(SyncEntityType.UserV1, () => { }); it('should detect and sync a deleted user', async () => { - const { auth, ctx } = await setup(await getKyselyDB()); + const { auth, user: authUser, ctx } = await setup(await getKyselyDB()); const userRepo = ctx.get(UserRepository); @@ -104,12 +96,7 @@ describe(SyncEntityType.UserV1, () => { }, { ack: expect.any(String), - data: { - deletedAt: null, - email: auth.user.email, - id: auth.user.id, - name: auth.user.name, - }, + data: expect.objectContaining({ id: authUser.id }), type: 'UserV1', }, ]); @@ -119,7 +106,7 @@ describe(SyncEntityType.UserV1, () => { }); it('should sync a user and then an update to that same user', async () => { - const { auth, ctx } = await setup(await getKyselyDB()); + const { auth, user, ctx } = await setup(await getKyselyDB()); const userRepo = ctx.get(UserRepository); @@ -128,12 +115,7 @@ describe(SyncEntityType.UserV1, () => { expect(response).toEqual([ { ack: expect.any(String), - data: { - deletedAt: null, - email: auth.user.email, - id: auth.user.id, - name: auth.user.name, - }, + data: expect.objectContaining({ id: user.id }), type: 'UserV1', }, ]); @@ -147,12 +129,7 @@ describe(SyncEntityType.UserV1, () => { expect(newResponse).toEqual([ { ack: expect.any(String), - data: { - deletedAt: null, - email: auth.user.email, - id: auth.user.id, - name: updated.name, - }, + data: expect.objectContaining({ id: user.id, name: updated.name }), type: 'UserV1', }, ]); diff --git a/server/test/repositories/asset.repository.mock.ts b/server/test/repositories/asset.repository.mock.ts index a29babbf54..6fca29d98e 100644 --- a/server/test/repositories/asset.repository.mock.ts +++ b/server/test/repositories/asset.repository.mock.ts @@ -39,5 +39,6 @@ export const newAssetRepositoryMock = (): Mocked Promise.resolve(); }; -export const newStorageRepositoryMock = (reset = true): Mocked> => { - if (reset) { - StorageCore.reset(); - } +export const newStorageRepositoryMock = (): Mocked> => { + StorageCore.reset(); + StorageCore.setMediaLocation('/data'); return { createZipStream: vitest.fn(), @@ -53,6 +52,7 @@ export const newStorageRepositoryMock = (reset = true): Mocked = {}) => ({ livePhotoVideoId: null, localDateTime: newDate(), originalFileName: 'IMG_123.jpg', - originalPath: `upload/12/34/IMG_123.jpg`, + originalPath: `/data/12/34/IMG_123.jpg`, ownerId: newUuid(), sidecarPath: null, stackId: null, diff --git a/server/test/utils.ts b/server/test/utils.ts index af6f2826f9..9f212578c0 100644 --- a/server/test/utils.ts +++ b/server/test/utils.ts @@ -4,7 +4,7 @@ import { Test } from '@nestjs/testing'; import { ClassConstructor } from 'class-transformer'; import { Kysely } from 'kysely'; import { ChildProcessWithoutNullStreams } from 'node:child_process'; -import { Writable } from 'node:stream'; +import { Readable, Writable } from 'node:stream'; import { PNG } from 'pngjs'; import postgres from 'postgres'; import { AssetUploadInterceptor } from 'src/middleware/asset-upload.interceptor'; @@ -71,7 +71,6 @@ import { newMetadataRepositoryMock } from 'test/repositories/metadata.repository import { newStorageRepositoryMock } from 'test/repositories/storage.repository.mock'; import { newSystemMetadataRepositoryMock } from 'test/repositories/system-metadata.repository.mock'; import { ITelemetryRepositoryMock, newTelemetryRepositoryMock } from 'test/repositories/telemetry.repository.mock'; -import { Readable } from 'typeorm/platform/PlatformTools'; import { assert, Mock, Mocked, vitest } from 'vitest'; export type ControllerContext = { diff --git a/web/.nvmrc b/web/.nvmrc index fc37597bcc..7377d130ed 100644 --- a/web/.nvmrc +++ b/web/.nvmrc @@ -1 +1 @@ -22.17.0 +22.17.1 diff --git a/web/Dockerfile b/web/Dockerfile index 1c6c4b46bf..3c119fdd4d 100644 --- a/web/Dockerfile +++ b/web/Dockerfile @@ -1,11 +1,17 @@ FROM node:22.16.0-alpine3.20@sha256:2289fb1fba0f4633b08ec47b94a89c7e20b829fc5679f9b7b298eaa2f1ed8b7e -RUN apk add --no-cache tini +RUN apk add --no-cache tini bash + USER node WORKDIR /usr/src/app -COPY --chown=node:node package*.json ./ + +COPY --chown=node:node ./web/package* ./web/ + +WORKDIR /usr/src/app/web RUN npm ci -ENV CHOKIDAR_USEPOLLING=true + +ENV CHOKIDAR_USEPOLLING=true \ + PATH="${PATH}:/usr/src/app/web/bin" EXPOSE 24678 EXPOSE 3000 -ENTRYPOINT ["/sbin/tini", "--", "/bin/sh"] +ENTRYPOINT ["tini", "--", "/bin/bash", "-c"] diff --git a/web/bin/immich-web b/web/bin/immich-web index ea748863db..d2739cf6c3 100755 --- a/web/bin/immich-web +++ b/web/bin/immich-web @@ -1,10 +1,11 @@ #!/usr/bin/env sh -TYPESCRIPT_SDK=/usr/src/open-api/typescript-sdk +TYPESCRIPT_SDK=/usr/src/app/open-api/typescript-sdk npm --prefix "$TYPESCRIPT_SDK" install npm --prefix "$TYPESCRIPT_SDK" run build +cd /usr/src/app/web || exit 1 COUNT=0 UPSTREAM="${IMMICH_SERVER_URL:-http://immich-server:2283/}" @@ -18,4 +19,4 @@ done echo "Connected to $UPSTREAM" -node ./node_modules/.bin/vite dev --host 0.0.0.0 --port 3000 +npx vite dev --host 0.0.0.0 --port 3000 diff --git a/web/package-lock.json b/web/package-lock.json index 2bd5409903..cfd65b63d8 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,17 +1,17 @@ { "name": "immich-web", - "version": "1.135.3", + "version": "1.137.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich-web", - "version": "1.135.3", + "version": "1.137.3", "license": "GNU Affero General Public License version 3", "dependencies": { "@formatjs/icu-messageformat-parser": "^2.9.8", "@immich/sdk": "file:../open-api/typescript-sdk", - "@immich/ui": "^0.23.2", + "@immich/ui": "^0.23.6", "@mapbox/mapbox-gl-rtl-text": "0.2.3", "@mdi/js": "^7.4.47", "@photo-sphere-viewer/core": "^5.11.5", @@ -51,9 +51,9 @@ "@koddsson/eslint-plugin-tscompat": "^0.2.0", "@socket.io/component-emitter": "^3.1.0", "@sveltejs/adapter-static": "^3.0.8", - "@sveltejs/enhanced-img": "^0.6.0", - "@sveltejs/kit": "^2.15.2", - "@sveltejs/vite-plugin-svelte": "^6.0.0", + "@sveltejs/enhanced-img": "^0.7.0", + "@sveltejs/kit": "^2.25.0", + "@sveltejs/vite-plugin-svelte": "6.1.0", "@tailwindcss/vite": "^4.1.7", "@testing-library/jest-dom": "^6.4.2", "@testing-library/svelte": "^5.2.8", @@ -66,9 +66,9 @@ "@types/qrcode": "^1.5.5", "@vitest/coverage-v8": "^3.0.0", "autoprefixer": "^10.4.17", - "dotenv": "^16.4.7", + "dotenv": "^17.0.0", "eslint": "^9.18.0", - "eslint-config-prettier": "^10.0.0", + "eslint-config-prettier": "^10.1.8", "eslint-p": "^0.25.0", "eslint-plugin-compat": "^6.0.2", "eslint-plugin-svelte": "^3.9.0", @@ -81,26 +81,26 @@ "prettier-plugin-sort-json": "^4.1.1", "prettier-plugin-svelte": "^3.3.3", "rollup-plugin-visualizer": "^6.0.0", - "svelte": "^5.25.3", + "svelte": "5.35.5", "svelte-check": "^4.1.5", "svelte-eslint-parser": "^1.2.0", "tailwindcss": "^4.1.7", "tslib": "^2.6.2", "typescript": "^5.7.3", "typescript-eslint": "^8.28.0", - "vite": "^7.0.0", + "vite": "^7.0.5", "vitest": "^3.0.0" } }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.135.3", + "version": "1.137.3", "license": "GNU Affero General Public License version 3", "dependencies": { "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.15.33", + "@types/node": "^22.16.5", "typescript": "^5.3.3" } }, @@ -496,9 +496,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz", - "integrity": "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz", + "integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==", "cpu": [ "arm64" ], @@ -529,9 +529,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz", - "integrity": "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz", + "integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==", "cpu": [ "arm64" ], @@ -561,6 +561,23 @@ "node": ">=12" } }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz", + "integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/sunos-x64": { "version": "0.19.12", "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", @@ -667,6 +684,31 @@ "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, + "node_modules/@eslint/config-array": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", + "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@eslint/core": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", @@ -718,9 +760,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.30.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.30.1.tgz", - "integrity": "sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg==", + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", + "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", "dev": true, "license": "MIT", "engines": { @@ -1315,9 +1357,9 @@ "link": true }, "node_modules/@immich/ui": { - "version": "0.23.3", - "resolved": "https://registry.npmjs.org/@immich/ui/-/ui-0.23.3.tgz", - "integrity": "sha512-YbYJSv3HqDu2+6MmiHhLThSessZ6HkoVOWun/ZoGb8mKj5x/ZZ4AyXGPIqbyKTamsjzbcD9FInij70G+m4egkg==", + "version": "0.23.6", + "resolved": "https://registry.npmjs.org/@immich/ui/-/ui-0.23.6.tgz", + "integrity": "sha512-HYIguDx/nCXcvqLKhY1R/+Aks6mn8B9jIiNVQH6WODDPbvGFrvQT5uINhXHrjsdyuzKBVS6dps+lx9+9Z6z4rA==", "license": "GNU Affero General Public License version 3", "dependencies": { "@mdi/js": "^7.4.47", @@ -1749,50 +1791,50 @@ } }, "node_modules/@photo-sphere-viewer/core": { - "version": "5.13.3", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/core/-/core-5.13.3.tgz", - "integrity": "sha512-6Fjkx2fTUFSmoy6jJxXn+FrwNlMtTi6M8ErEZkQgn7ULenMpIUx6tdFz3l85NM7Br/Rj4CK8IyUo8Le1eI1Fdg==", + "version": "5.13.4", + "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/core/-/core-5.13.4.tgz", + "integrity": "sha512-leVQL6gG9wTF+uvCFarHUcr8mzafCZ/GLzauksYQJfiqDVRFSAJNXnTOy7RH9otToluEdjN1hsN1f9HQy+rLYg==", "license": "MIT", "dependencies": { "three": "^0.175.0" } }, "node_modules/@photo-sphere-viewer/equirectangular-video-adapter": { - "version": "5.13.3", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/equirectangular-video-adapter/-/equirectangular-video-adapter-5.13.3.tgz", - "integrity": "sha512-Gl63vt9ztRHrw2lxDF4KTPsndDbIByg+hUsaMtEAuc304wLUKj6GIapAh71eCyDqvPBhLoTZ5+4RX/TPafnCGg==", + "version": "5.13.4", + "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/equirectangular-video-adapter/-/equirectangular-video-adapter-5.13.4.tgz", + "integrity": "sha512-OdTOKxFunP56FNoPR47mQp7V1WHvV4eiow3qtyJjAgLeU8T2q3kivLuH1kMZN2yTAJaXab+VBXzA/YChiHZ6mQ==", "license": "MIT", "peerDependencies": { - "@photo-sphere-viewer/core": "5.13.3", - "@photo-sphere-viewer/video-plugin": "5.13.3" + "@photo-sphere-viewer/core": "5.13.4", + "@photo-sphere-viewer/video-plugin": "5.13.4" } }, "node_modules/@photo-sphere-viewer/resolution-plugin": { - "version": "5.13.3", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/resolution-plugin/-/resolution-plugin-5.13.3.tgz", - "integrity": "sha512-spSBm7OXDisnw5Fx4+JPR510nb+nGFZf2aecPRhP23GZyxKPg82i1PYoDJgRCWKTlvz8Yq2eilKdAtncmHWgSA==", + "version": "5.13.4", + "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/resolution-plugin/-/resolution-plugin-5.13.4.tgz", + "integrity": "sha512-HRBC5zYmpNoo/joKZzXbxn7jwoh3tdtTJFXzHxYPV51ELDclRNmzhmqEaZeVkrFHr4bRF5ow3AOjxiMtu1xQxA==", "license": "MIT", "peerDependencies": { - "@photo-sphere-viewer/core": "5.13.3", - "@photo-sphere-viewer/settings-plugin": "5.13.3" + "@photo-sphere-viewer/core": "5.13.4", + "@photo-sphere-viewer/settings-plugin": "5.13.4" } }, "node_modules/@photo-sphere-viewer/settings-plugin": { - "version": "5.13.3", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/settings-plugin/-/settings-plugin-5.13.3.tgz", - "integrity": "sha512-x/KulP3UxoawDS8MuGrZU3DznrzpaYqQ2BQnxlaOVItewrgCdS8WRG18E7nBo+2lfZGDDwGhRsSp/IRUOwScRg==", + "version": "5.13.4", + "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/settings-plugin/-/settings-plugin-5.13.4.tgz", + "integrity": "sha512-As1nmlsfnjKBFQOWPVQLH1+dJ+s62MdEb6Jvlm16+3fUVHF4CBWRTJZyBKejLiu4xjbDxrE8v5ZHDLvG6ButiQ==", "license": "MIT", "peerDependencies": { - "@photo-sphere-viewer/core": "5.13.3" + "@photo-sphere-viewer/core": "5.13.4" } }, "node_modules/@photo-sphere-viewer/video-plugin": { - "version": "5.13.3", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/video-plugin/-/video-plugin-5.13.3.tgz", - "integrity": "sha512-npNeknhV3jLJLKbbFzaLRVRROiN2POSdHLnV0R6QNiF7rQURC0Cs7Yuztl2nFJWUBOS3MiO13t5Wyz1wwfKfSQ==", + "version": "5.13.4", + "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/video-plugin/-/video-plugin-5.13.4.tgz", + "integrity": "sha512-QWbHMVAJHukLbFNn0irND/nEPtmzjbXth1ckBkT1bg8aRilFw50+IIB0Zfdl6X919R2GfGo8P0u+I/Mwxf7yfg==", "license": "MIT", "peerDependencies": { - "@photo-sphere-viewer/core": "5.13.3" + "@photo-sphere-viewer/core": "5.13.4" } }, "node_modules/@pkgjs/parseargs": { @@ -2142,9 +2184,9 @@ } }, "node_modules/@sveltejs/enhanced-img": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@sveltejs/enhanced-img/-/enhanced-img-0.6.1.tgz", - "integrity": "sha512-8oj/cXc/M1soGQOkkkuEzeaiE/LTa3MJnoRwoRzG7GOPKHOfNRJDzsCcx3s1GqxQlcoHc4BJK3HoU1m0OV9UMA==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@sveltejs/enhanced-img/-/enhanced-img-0.7.0.tgz", + "integrity": "sha512-nvWETsp2IsUoRlt2dFg8swJ8BXmEAZCmOTpIrJ+SGGf54V+rYQ01IbIOPNkmzlejz0Tr0PmslzwMunie8eKU7A==", "dev": true, "license": "MIT", "dependencies": { @@ -2155,15 +2197,15 @@ "zimmerframe": "^1.1.2" }, "peerDependencies": { - "@sveltejs/vite-plugin-svelte": "^5.0.0 || ^6.0.0-next.0", + "@sveltejs/vite-plugin-svelte": "^6.0.0", "svelte": "^5.0.0", - "vite": ">= 5.0.0" + "vite": "^6.3.0 || >=7.0.0" } }, "node_modules/@sveltejs/kit": { - "version": "2.22.4", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.22.4.tgz", - "integrity": "sha512-BXK9hTbP8AeQIfoz6+P3uoyVYStVHc5CIKqoTSF7hXm3Q5P9BwFMdEus4jsQuhaYmXGHzukcGlxe2QrsE8BJfQ==", + "version": "2.25.2", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.25.2.tgz", + "integrity": "sha512-aKfj82vqEINedoH9Pw4Ip16jj3w8soNq9F3nJqc56kxXW74TcEu/gdTAuLUI+gsl8i+KXfetRqg1F+gG/AZRVQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2193,9 +2235,9 @@ } }, "node_modules/@sveltejs/vite-plugin-svelte": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-6.0.0.tgz", - "integrity": "sha512-mma5GJ23pYiWpTNbN//g9XI3Hfob3aAlXPP42qRtvjgTAU6pfJyLyNPTdLjFuj+jfC9JslP4J3AkeiJNhjtLLA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-6.1.0.tgz", + "integrity": "sha512-+U6lz1wvGEG/BvQyL4z/flyNdQ9xDNv5vrh+vWBWTHaebqT0c9RNggpZTo/XSPoHsSCWBlYaTlRX8pZ9GATXCw==", "dev": true, "license": "MIT", "dependencies": { @@ -2470,6 +2512,66 @@ "node": ">=14.0.0" } }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.4.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.0.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.4.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.11", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.9.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { + "version": "0.9.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { + "version": "2.8.0", + "dev": true, + "inBundle": true, + "license": "0BSD", + "optional": true + }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.11.tgz", @@ -2919,17 +3021,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.36.0.tgz", - "integrity": "sha512-lZNihHUVB6ZZiPBNgOQGSxUASI7UJWhT8nHyUGCnaQ28XFCw98IfrMCG3rUl1uwUWoAvodJQby2KTs79UTcrAg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz", + "integrity": "sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/type-utils": "8.36.0", - "@typescript-eslint/utils": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/type-utils": "8.38.0", + "@typescript-eslint/utils": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -2943,187 +3045,11 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.36.0", + "@typescript-eslint/parser": "^8.38.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/project-service": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.36.0.tgz", - "integrity": "sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.36.0", - "@typescript-eslint/types": "^8.36.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.36.0.tgz", - "integrity": "sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.36.0.tgz", - "integrity": "sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.36.0.tgz", - "integrity": "sha512-5aaGYG8cVDd6cxfk/ynpYzxBRZJk7w/ymto6uiyUFtdCozQIsQWh7M28/6r57Fwkbweng8qAzoMCPwSJfWlmsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/utils": "8.36.0", - "debug": "^4.3.4", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.36.0.tgz", - "integrity": "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.36.0.tgz", - "integrity": "sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.36.0", - "@typescript-eslint/tsconfig-utils": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.36.0.tgz", - "integrity": "sha512-VOqmHu42aEMT+P2qYjylw6zP/3E/HvptRwdn/PZxyV27KhZg2IOszXod4NcXisWzPAGSS4trE/g4moNj6XmH2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.36.0.tgz", - "integrity": "sha512-vZrhV2lRPWDuGoxcmrzRZyxAggPL+qp3WzUrlZD+slFueDiYHxeBa34dUXPuC0RmGKzl4lS5kFJYvKCq9cnNDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.36.0", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { "version": "7.0.5", "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", @@ -3134,33 +3060,17 @@ "node": ">= 4" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@typescript-eslint/parser": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.36.0.tgz", - "integrity": "sha512-FuYgkHwZLuPbZjQHzJXrtXreJdFMKl16BFYyRrLxDhWr6Qr7Kbcu2s1Yhu8tsiMXw1S0W1pjfFfYEt+R604s+Q==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.38.0.tgz", + "integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "debug": "^4.3.4" }, "engines": { @@ -3175,159 +3085,15 @@ "typescript": ">=4.8.4 <5.9.0" } }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/project-service": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.36.0.tgz", - "integrity": "sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.36.0", - "@typescript-eslint/types": "^8.36.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.36.0.tgz", - "integrity": "sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.36.0.tgz", - "integrity": "sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.36.0.tgz", - "integrity": "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.36.0.tgz", - "integrity": "sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.36.0", - "@typescript-eslint/tsconfig-utils": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.36.0.tgz", - "integrity": "sha512-vZrhV2lRPWDuGoxcmrzRZyxAggPL+qp3WzUrlZD+slFueDiYHxeBa34dUXPuC0RmGKzl4lS5kFJYvKCq9cnNDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.36.0", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@typescript-eslint/project-service": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", - "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz", + "integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.37.0", - "@typescript-eslint/types": "^8.37.0", + "@typescript-eslint/tsconfig-utils": "^8.38.0", + "@typescript-eslint/types": "^8.38.0", "debug": "^4.3.4" }, "engines": { @@ -3342,14 +3108,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", - "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz", + "integrity": "sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0" + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3360,9 +3126,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", - "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz", + "integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==", "dev": true, "license": "MIT", "engines": { @@ -3377,15 +3143,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz", - "integrity": "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.38.0.tgz", + "integrity": "sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/utils": "8.37.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -3402,9 +3168,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", - "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz", + "integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==", "dev": true, "license": "MIT", "engines": { @@ -3416,16 +3182,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", - "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz", + "integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.37.0", - "@typescript-eslint/tsconfig-utils": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/project-service": "8.38.0", + "@typescript-eslint/tsconfig-utils": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -3471,16 +3237,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz", - "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz", + "integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0" + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3495,13 +3261,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", - "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz", + "integrity": "sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/types": "8.38.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -4698,9 +4464,9 @@ } }, "node_modules/dotenv": { - "version": "16.6.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", - "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "version": "17.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.0.tgz", + "integrity": "sha512-Q4sgBT60gzd0BB0lSyYD3xM4YxrXA9y4uBDof1JNYGzOXrQdQ6yX+7XIAqoFOGQFOTK1D3Hts5OllpxMDZFONQ==", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -5031,6 +4797,112 @@ } }, "node_modules/eslint": { + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", + "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.0", + "@eslint/core": "^0.15.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.31.0", + "@eslint/plugin-kit": "^0.3.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-prettier": { + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-p": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/eslint-p/-/eslint-p-0.25.0.tgz", + "integrity": "sha512-e7oYgXN/tgtoaR3tZ0R2dKyPJtf5J41hYKsgpsBtwpi0t2Cxjf3l8G2QwrXCDwQTFVXW1hmD55hAqQZxiId1XA==", + "dev": true, + "license": "ISC", + "dependencies": { + "eslint": "9.30.1" + }, + "bin": { + "eslint-p": "lib/eslint-p.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/eslint-p/node_modules/@eslint/js": { + "version": "9.30.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.30.1.tgz", + "integrity": "sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/eslint-p/node_modules/eslint": { "version": "9.30.1", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.30.1.tgz", "integrity": "sha512-zmxXPNMOXmwm9E0yQLi5uqXHs7uq2UIiqEKo3Gq+3fwo1XrJ+hijAZImyF7hclW3E6oHz43Yk3RP8at6OTKflQ==", @@ -5091,38 +4963,6 @@ } } }, - "node_modules/eslint-config-prettier": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz", - "integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==", - "dev": true, - "license": "MIT", - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "funding": { - "url": "https://opencollective.com/eslint-config-prettier" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/eslint-p": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/eslint-p/-/eslint-p-0.25.0.tgz", - "integrity": "sha512-e7oYgXN/tgtoaR3tZ0R2dKyPJtf5J41hYKsgpsBtwpi0t2Cxjf3l8G2QwrXCDwQTFVXW1hmD55hAqQZxiId1XA==", - "dev": true, - "license": "ISC", - "dependencies": { - "eslint": "9.30.1" - }, - "bin": { - "eslint-p": "lib/eslint-p.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, "node_modules/eslint-plugin-compat": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/eslint-plugin-compat/-/eslint-plugin-compat-6.0.2.tgz", @@ -5160,9 +5000,9 @@ } }, "node_modules/eslint-plugin-svelte": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-3.10.1.tgz", - "integrity": "sha512-csCh2x0ge/DugXC7dCANh46Igi7bjMZEy6rHZCdS13AoGVJSu7a90Kru3I8oMYLGEemPRE1hQXadxvRPVMAAXQ==", + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-3.11.0.tgz", + "integrity": "sha512-KliWlkieHyEa65aQIkRwUFfHzT5Cn4u3BQQsu3KlkJOs7c1u7ryn84EWaOjEzilbKgttT4OfBURA8Uc4JBSQIw==", "dev": true, "license": "MIT", "dependencies": { @@ -5175,7 +5015,7 @@ "postcss-load-config": "^3.1.4", "postcss-safe-parser": "^7.0.0", "semver": "^7.6.3", - "svelte-eslint-parser": "^1.2.0" + "svelte-eslint-parser": "^1.3.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5285,31 +5125,19 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/@eslint/config-array": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", - "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "node_modules/eslint/node_modules/@eslint/core": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", + "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.6", - "debug": "^4.3.1", - "minimatch": "^3.1.2" + "@types/json-schema": "^7.0.15" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/eslint/node_modules/@eslint/config-helpers": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", - "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, "node_modules/esm-env": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz", @@ -5454,9 +5282,9 @@ } }, "node_modules/fabric": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/fabric/-/fabric-6.7.0.tgz", - "integrity": "sha512-+yKumsh1MvJ44Um2eOhb4Q6CyZ6e2XKBV3IfQvzuGKhl2UkRFQtIKPUi6f06m3gd0r5zspgMUl5iwxtT1dmFAQ==", + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/fabric/-/fabric-6.7.1.tgz", + "integrity": "sha512-dLxSmIvN4InJf4xOjbl1LFWh8WGOUIYtcuDIGs2IN0Z9lI0zGobfesDauyEhI1+owMLTPCCiEv01rpYXm7g2EQ==", "license": "MIT", "engines": { "node": ">=16.20.0" @@ -8022,15 +7850,15 @@ } }, "node_modules/prettier-plugin-organize-imports": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.1.0.tgz", - "integrity": "sha512-5aWRdCgv645xaa58X8lOxzZoiHAldAPChljr/MT0crXVOWTZ+Svl4hIWlz+niYSlO6ikE5UXkN1JrRvIP2ut0A==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.2.0.tgz", + "integrity": "sha512-Zdy27UhlmyvATZi67BTnLcKTo8fm6Oik59Sz6H64PgZJVs6NJpPD1mT240mmJn62c98/QaL+r3kx9Q3gRpDajg==", "dev": true, "license": "MIT", "peerDependencies": { "prettier": ">=2.0", "typescript": ">=2.9", - "vue-tsc": "^2.1.0" + "vue-tsc": "^2.1.0 || 3" }, "peerDependenciesMeta": { "vue-tsc": { @@ -9069,9 +8897,9 @@ } }, "node_modules/svelte-check": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.2.2.tgz", - "integrity": "sha512-1+31EOYZ7NKN0YDMKusav2hhEoA51GD9Ws6o//0SphMT0ve9mBTsTUEX7OmDMadUP3KjNHsSKtJrqdSaD8CrGQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.3.0.tgz", + "integrity": "sha512-Iz8dFXzBNAM7XlEIsUjUGQhbEE+Pvv9odb9+0+ITTgFWZBGeJRRYqHUUglwe2EkLD5LIsQaAc4IUJyvtKuOO5w==", "dev": true, "license": "MIT", "dependencies": { @@ -9093,9 +8921,9 @@ } }, "node_modules/svelte-eslint-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-1.2.0.tgz", - "integrity": "sha512-mbPtajIeuiyU80BEyGvwAktBeTX7KCr5/0l+uRGLq1dafwRNrjfM5kHGJScEBlPG3ipu6dJqfW/k0/fujvIEVw==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-1.3.0.tgz", + "integrity": "sha512-VCgMHKV7UtOGcGLGNFSbmdm6kEKjtzo5nnpGU/mnx4OsFY6bZ7QwRF5DUx+Hokw5Lvdyo8dpk8B1m8mliomrNg==", "dev": true, "license": "MIT", "dependencies": { @@ -9608,15 +9436,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.36.0.tgz", - "integrity": "sha512-fTCqxthY+h9QbEgSIBfL9iV6CvKDFuoxg6bHPNpJ9HIUzS+jy2lCEyCmGyZRWEBSaykqcDPf1SJ+BfCI8DRopA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.38.0.tgz", + "integrity": "sha512-FsZlrYK6bPDGoLeZRuvx2v6qrM03I0U0SnfCLPs/XCCPCFD80xU9Pg09H/K+XFa68uJuZo7l/Xhs+eDRg2l3hg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.36.0", - "@typescript-eslint/parser": "8.36.0", - "@typescript-eslint/utils": "8.36.0" + "@typescript-eslint/eslint-plugin": "8.38.0", + "@typescript-eslint/parser": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -9630,174 +9459,6 @@ "typescript": ">=4.8.4 <5.9.0" } }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/project-service": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.36.0.tgz", - "integrity": "sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.36.0", - "@typescript-eslint/types": "^8.36.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/scope-manager": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.36.0.tgz", - "integrity": "sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.36.0.tgz", - "integrity": "sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/types": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.36.0.tgz", - "integrity": "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.36.0.tgz", - "integrity": "sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.36.0", - "@typescript-eslint/tsconfig-utils": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.36.0.tgz", - "integrity": "sha512-VOqmHu42aEMT+P2qYjylw6zP/3E/HvptRwdn/PZxyV27KhZg2IOszXod4NcXisWzPAGSS4trE/g4moNj6XmH2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.36.0.tgz", - "integrity": "sha512-vZrhV2lRPWDuGoxcmrzRZyxAggPL+qp3WzUrlZD+slFueDiYHxeBa34dUXPuC0RmGKzl4lS5kFJYvKCq9cnNDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.36.0", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/typescript-eslint/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/typescript-eslint/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/uglify-js": { "version": "3.19.3", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", @@ -9888,9 +9549,9 @@ "license": "MIT" }, "node_modules/vite": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.4.tgz", - "integrity": "sha512-SkaSguuS7nnmV7mfJ8l81JGBFV7Gvzp8IzgE8A8t23+AxuNX61Q5H1Tpz5efduSN7NHC8nQXD3sKQKZAu5mNEA==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.5.tgz", + "integrity": "sha512-1mncVwJxy2C9ThLwz0+2GKZyEXuC3MyWtAAlNftlZZXZDP3AJt5FmwcMit/IGGaNZ8ZOB2BNO/HFUB+CpN0NQw==", "dev": true, "license": "MIT", "dependencies": { @@ -10001,9 +9662,9 @@ } }, "node_modules/vite/node_modules/@esbuild/aix-ppc64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz", - "integrity": "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz", + "integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==", "cpu": [ "ppc64" ], @@ -10018,9 +9679,9 @@ } }, "node_modules/vite/node_modules/@esbuild/android-arm": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.4.tgz", - "integrity": "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.8.tgz", + "integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==", "cpu": [ "arm" ], @@ -10035,9 +9696,9 @@ } }, "node_modules/vite/node_modules/@esbuild/android-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz", - "integrity": "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz", + "integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==", "cpu": [ "arm64" ], @@ -10052,9 +9713,9 @@ } }, "node_modules/vite/node_modules/@esbuild/android-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.4.tgz", - "integrity": "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.8.tgz", + "integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==", "cpu": [ "x64" ], @@ -10069,9 +9730,9 @@ } }, "node_modules/vite/node_modules/@esbuild/darwin-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz", - "integrity": "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz", + "integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==", "cpu": [ "arm64" ], @@ -10086,9 +9747,9 @@ } }, "node_modules/vite/node_modules/@esbuild/darwin-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz", - "integrity": "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz", + "integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==", "cpu": [ "x64" ], @@ -10103,9 +9764,9 @@ } }, "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz", - "integrity": "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz", + "integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==", "cpu": [ "arm64" ], @@ -10120,9 +9781,9 @@ } }, "node_modules/vite/node_modules/@esbuild/freebsd-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz", - "integrity": "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz", + "integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==", "cpu": [ "x64" ], @@ -10137,9 +9798,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-arm": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz", - "integrity": "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz", + "integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==", "cpu": [ "arm" ], @@ -10154,9 +9815,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz", - "integrity": "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz", + "integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==", "cpu": [ "arm64" ], @@ -10171,9 +9832,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-ia32": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz", - "integrity": "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz", + "integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==", "cpu": [ "ia32" ], @@ -10188,9 +9849,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-loong64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz", - "integrity": "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz", + "integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==", "cpu": [ "loong64" ], @@ -10205,9 +9866,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-mips64el": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz", - "integrity": "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz", + "integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==", "cpu": [ "mips64el" ], @@ -10222,9 +9883,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-ppc64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz", - "integrity": "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz", + "integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==", "cpu": [ "ppc64" ], @@ -10239,9 +9900,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-riscv64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz", - "integrity": "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz", + "integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==", "cpu": [ "riscv64" ], @@ -10256,9 +9917,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-s390x": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz", - "integrity": "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz", + "integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==", "cpu": [ "s390x" ], @@ -10273,9 +9934,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz", - "integrity": "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz", + "integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==", "cpu": [ "x64" ], @@ -10290,9 +9951,9 @@ } }, "node_modules/vite/node_modules/@esbuild/netbsd-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz", - "integrity": "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz", + "integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==", "cpu": [ "x64" ], @@ -10307,9 +9968,9 @@ } }, "node_modules/vite/node_modules/@esbuild/openbsd-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz", - "integrity": "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz", + "integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==", "cpu": [ "x64" ], @@ -10324,9 +9985,9 @@ } }, "node_modules/vite/node_modules/@esbuild/sunos-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz", - "integrity": "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz", + "integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==", "cpu": [ "x64" ], @@ -10341,9 +10002,9 @@ } }, "node_modules/vite/node_modules/@esbuild/win32-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz", - "integrity": "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz", + "integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==", "cpu": [ "arm64" ], @@ -10358,9 +10019,9 @@ } }, "node_modules/vite/node_modules/@esbuild/win32-ia32": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz", - "integrity": "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz", + "integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==", "cpu": [ "ia32" ], @@ -10375,9 +10036,9 @@ } }, "node_modules/vite/node_modules/@esbuild/win32-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz", - "integrity": "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz", + "integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==", "cpu": [ "x64" ], @@ -10392,9 +10053,9 @@ } }, "node_modules/vite/node_modules/esbuild": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz", - "integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz", + "integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -10405,31 +10066,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.4", - "@esbuild/android-arm": "0.25.4", - "@esbuild/android-arm64": "0.25.4", - "@esbuild/android-x64": "0.25.4", - "@esbuild/darwin-arm64": "0.25.4", - "@esbuild/darwin-x64": "0.25.4", - "@esbuild/freebsd-arm64": "0.25.4", - "@esbuild/freebsd-x64": "0.25.4", - "@esbuild/linux-arm": "0.25.4", - "@esbuild/linux-arm64": "0.25.4", - "@esbuild/linux-ia32": "0.25.4", - "@esbuild/linux-loong64": "0.25.4", - "@esbuild/linux-mips64el": "0.25.4", - "@esbuild/linux-ppc64": "0.25.4", - "@esbuild/linux-riscv64": "0.25.4", - "@esbuild/linux-s390x": "0.25.4", - "@esbuild/linux-x64": "0.25.4", - "@esbuild/netbsd-arm64": "0.25.4", - "@esbuild/netbsd-x64": "0.25.4", - "@esbuild/openbsd-arm64": "0.25.4", - "@esbuild/openbsd-x64": "0.25.4", - "@esbuild/sunos-x64": "0.25.4", - "@esbuild/win32-arm64": "0.25.4", - "@esbuild/win32-ia32": "0.25.4", - "@esbuild/win32-x64": "0.25.4" + "@esbuild/aix-ppc64": "0.25.8", + "@esbuild/android-arm": "0.25.8", + "@esbuild/android-arm64": "0.25.8", + "@esbuild/android-x64": "0.25.8", + "@esbuild/darwin-arm64": "0.25.8", + "@esbuild/darwin-x64": "0.25.8", + "@esbuild/freebsd-arm64": "0.25.8", + "@esbuild/freebsd-x64": "0.25.8", + "@esbuild/linux-arm": "0.25.8", + "@esbuild/linux-arm64": "0.25.8", + "@esbuild/linux-ia32": "0.25.8", + "@esbuild/linux-loong64": "0.25.8", + "@esbuild/linux-mips64el": "0.25.8", + "@esbuild/linux-ppc64": "0.25.8", + "@esbuild/linux-riscv64": "0.25.8", + "@esbuild/linux-s390x": "0.25.8", + "@esbuild/linux-x64": "0.25.8", + "@esbuild/netbsd-arm64": "0.25.8", + "@esbuild/netbsd-x64": "0.25.8", + "@esbuild/openbsd-arm64": "0.25.8", + "@esbuild/openbsd-x64": "0.25.8", + "@esbuild/openharmony-arm64": "0.25.8", + "@esbuild/sunos-x64": "0.25.8", + "@esbuild/win32-arm64": "0.25.8", + "@esbuild/win32-ia32": "0.25.8", + "@esbuild/win32-x64": "0.25.8" } }, "node_modules/vitefu": { @@ -10761,21 +10423,6 @@ "license": "ISC", "optional": true }, - "node_modules/yaml": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", - "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==", - "dev": true, - "license": "ISC", - "optional": true, - "peer": true, - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/yargs": { "version": "15.4.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", diff --git a/web/package.json b/web/package.json index c63c52a916..bb17955b08 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "immich-web", - "version": "1.135.3", + "version": "1.137.3", "license": "GNU Affero General Public License version 3", "type": "module", "scripts": { @@ -28,7 +28,7 @@ "dependencies": { "@formatjs/icu-messageformat-parser": "^2.9.8", "@immich/sdk": "file:../open-api/typescript-sdk", - "@immich/ui": "^0.23.2", + "@immich/ui": "^0.23.6", "@mapbox/mapbox-gl-rtl-text": "0.2.3", "@mdi/js": "^7.4.47", "@photo-sphere-viewer/core": "^5.11.5", @@ -68,9 +68,9 @@ "@koddsson/eslint-plugin-tscompat": "^0.2.0", "@socket.io/component-emitter": "^3.1.0", "@sveltejs/adapter-static": "^3.0.8", - "@sveltejs/enhanced-img": "^0.6.0", - "@sveltejs/kit": "^2.15.2", - "@sveltejs/vite-plugin-svelte": "^6.0.0", + "@sveltejs/enhanced-img": "^0.7.0", + "@sveltejs/kit": "^2.25.0", + "@sveltejs/vite-plugin-svelte": "6.1.0", "@tailwindcss/vite": "^4.1.7", "@testing-library/jest-dom": "^6.4.2", "@testing-library/svelte": "^5.2.8", @@ -83,9 +83,9 @@ "@types/qrcode": "^1.5.5", "@vitest/coverage-v8": "^3.0.0", "autoprefixer": "^10.4.17", - "dotenv": "^16.4.7", + "dotenv": "^17.0.0", "eslint": "^9.18.0", - "eslint-config-prettier": "^10.0.0", + "eslint-config-prettier": "^10.1.8", "eslint-p": "^0.25.0", "eslint-plugin-compat": "^6.0.2", "eslint-plugin-svelte": "^3.9.0", @@ -98,17 +98,17 @@ "prettier-plugin-sort-json": "^4.1.1", "prettier-plugin-svelte": "^3.3.3", "rollup-plugin-visualizer": "^6.0.0", - "svelte": "^5.25.3", + "svelte": "5.35.5", "svelte-check": "^4.1.5", "svelte-eslint-parser": "^1.2.0", "tailwindcss": "^4.1.7", "tslib": "^2.6.2", "typescript": "^5.7.3", "typescript-eslint": "^8.28.0", - "vite": "^7.0.0", + "vite": "^7.0.5", "vitest": "^3.0.0" }, "volta": { - "node": "22.17.0" + "node": "22.17.1" } } diff --git a/web/src/app.html b/web/src/app.html index ec8c4393ff..776764850f 100644 --- a/web/src/app.html +++ b/web/src/app.html @@ -21,11 +21,6 @@ html { height: 100%; width: 100%; - background-color: rgb(255, 255, 255); - } - - html.dark { - background-color: rgb(10, 10, 10); } body, @@ -34,10 +29,6 @@ padding: 0; } - body { - transition: background-color 0.15s ease; - } - @keyframes delayedVisibility { to { visibility: visible; diff --git a/web/src/lib/components/admin-page/jobs/jobs-panel.svelte b/web/src/lib/components/admin-page/jobs/jobs-panel.svelte index 73dfb30908..463bcb3d20 100644 --- a/web/src/lib/components/admin-page/jobs/jobs-panel.svelte +++ b/web/src/lib/components/admin-page/jobs/jobs-panel.svelte @@ -3,11 +3,11 @@ notificationController, NotificationType, } from '$lib/components/shared-components/notification/notification'; - import { modalManager } from '$lib/managers/modal-manager.svelte'; import { featureFlags } from '$lib/stores/server-config.store'; import { getJobName } from '$lib/utils'; import { handleError } from '$lib/utils/handle-error'; import { JobCommand, JobName, sendJobCommand, type AllJobStatusResponseDto, type JobCommandDto } from '@immich/sdk'; + import { modalManager } from '@immich/ui'; import { mdiContentDuplicate, mdiFaceRecognition, diff --git a/web/src/lib/components/admin-page/settings/auth/auth-settings.svelte b/web/src/lib/components/admin-page/settings/auth/auth-settings.svelte index a1926b4020..ce6dc26171 100644 --- a/web/src/lib/components/admin-page/settings/auth/auth-settings.svelte +++ b/web/src/lib/components/admin-page/settings/auth/auth-settings.svelte @@ -6,9 +6,9 @@ import SettingSelect from '$lib/components/shared-components/settings/setting-select.svelte'; import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte'; import { SettingInputFieldType } from '$lib/constants'; - import { modalManager } from '$lib/managers/modal-manager.svelte'; import AuthDisableLoginConfirmModal from '$lib/modals/AuthDisableLoginConfirmModal.svelte'; import { OAuthTokenEndpointAuthMethod, type SystemConfigDto } from '@immich/sdk'; + import { modalManager } from '@immich/ui'; import { isEqual } from 'lodash-es'; import { t } from 'svelte-i18n'; import { fade } from 'svelte/transition'; diff --git a/web/src/lib/components/admin-page/settings/machine-learning-settings/machine-learning-settings.svelte b/web/src/lib/components/admin-page/settings/machine-learning-settings/machine-learning-settings.svelte index c120cb3750..acc920981b 100644 --- a/web/src/lib/components/admin-page/settings/machine-learning-settings/machine-learning-settings.svelte +++ b/web/src/lib/components/admin-page/settings/machine-learning-settings/machine-learning-settings.svelte @@ -183,7 +183,7 @@ label={$t('admin.machine_learning_min_detection_score')} description={$t('admin.machine_learning_min_detection_score_description')} bind:value={config.machineLearning.facialRecognition.minScore} - step="0.1" + step="0.01" min={0.1} max={1} disabled={disabled || !config.machineLearning.enabled || !config.machineLearning.facialRecognition.enabled} @@ -196,7 +196,7 @@ label={$t('admin.machine_learning_max_recognition_distance')} description={$t('admin.machine_learning_max_recognition_distance_description')} bind:value={config.machineLearning.facialRecognition.maxDistance} - step="0.1" + step="0.01" min={0.1} max={2} disabled={disabled || !config.machineLearning.enabled || !config.machineLearning.facialRecognition.enabled} diff --git a/web/src/lib/components/admin-page/settings/template-settings/template-settings.svelte b/web/src/lib/components/admin-page/settings/template-settings/template-settings.svelte index 872b4b9833..a337aab2ae 100644 --- a/web/src/lib/components/admin-page/settings/template-settings/template-settings.svelte +++ b/web/src/lib/components/admin-page/settings/template-settings/template-settings.svelte @@ -4,11 +4,10 @@ import LoadingSpinner from '$lib/components/shared-components/loading-spinner.svelte'; import SettingAccordion from '$lib/components/shared-components/settings/setting-accordion.svelte'; import SettingTextarea from '$lib/components/shared-components/settings/setting-textarea.svelte'; - import { modalManager } from '$lib/managers/modal-manager.svelte'; import EmailTemplatePreviewModal from '$lib/modals/EmailTemplatePreviewModal.svelte'; import { handleError } from '$lib/utils/handle-error'; import { type SystemConfigDto, type SystemConfigTemplateEmailsDto, getNotificationTemplateAdmin } from '@immich/sdk'; - import { Button } from '@immich/ui'; + import { Button, modalManager } from '@immich/ui'; import { mdiEyeOutline } from '@mdi/js'; import { t } from 'svelte-i18n'; import { fade } from 'svelte/transition'; diff --git a/web/src/lib/components/album-page/album-map.svelte b/web/src/lib/components/album-page/album-map.svelte index fbb831a38b..7d7060ac0a 100644 --- a/web/src/lib/components/album-page/album-map.svelte +++ b/web/src/lib/components/album-page/album-map.svelte @@ -1,9 +1,8 @@ diff --git a/web/src/lib/components/asset-viewer/actions/keep-this-delete-others.svelte b/web/src/lib/components/asset-viewer/actions/keep-this-delete-others.svelte index be5e8f7827..dfb8d8910c 100644 --- a/web/src/lib/components/asset-viewer/actions/keep-this-delete-others.svelte +++ b/web/src/lib/components/asset-viewer/actions/keep-this-delete-others.svelte @@ -1,11 +1,10 @@ + + diff --git a/web/src/lib/components/asset-viewer/actions/set-profile-picture-action.svelte b/web/src/lib/components/asset-viewer/actions/set-profile-picture-action.svelte index 6887ae8ebb..0d7735d4f1 100644 --- a/web/src/lib/components/asset-viewer/actions/set-profile-picture-action.svelte +++ b/web/src/lib/components/asset-viewer/actions/set-profile-picture-action.svelte @@ -1,8 +1,8 @@ diff --git a/web/src/lib/components/asset-viewer/actions/unstack-action.svelte b/web/src/lib/components/asset-viewer/actions/unstack-action.svelte index 1adeead05f..0c8192a9e3 100644 --- a/web/src/lib/components/asset-viewer/actions/unstack-action.svelte +++ b/web/src/lib/components/asset-viewer/actions/unstack-action.svelte @@ -4,7 +4,7 @@ import { deleteStack } from '$lib/utils/asset-utils'; import { toTimelineAsset } from '$lib/utils/timeline-util'; import type { StackResponseDto } from '@immich/sdk'; - import { mdiImageMinusOutline } from '@mdi/js'; + import { mdiImageOffOutline } from '@mdi/js'; import { t } from 'svelte-i18n'; import type { OnAction } from './action'; @@ -23,4 +23,4 @@ }; - + diff --git a/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte b/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte index a376c37139..66061ebb01 100644 --- a/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte +++ b/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte @@ -9,6 +9,7 @@ import DownloadAction from '$lib/components/asset-viewer/actions/download-action.svelte'; import FavoriteAction from '$lib/components/asset-viewer/actions/favorite-action.svelte'; import KeepThisDeleteOthersAction from '$lib/components/asset-viewer/actions/keep-this-delete-others.svelte'; + import RemoveAssetFromStack from '$lib/components/asset-viewer/actions/remove-asset-from-stack.svelte'; import RestoreAction from '$lib/components/asset-viewer/actions/restore-action.svelte'; import SetAlbumCoverAction from '$lib/components/asset-viewer/actions/set-album-cover-action.svelte'; import SetFeaturedPhotoAction from '$lib/components/asset-viewer/actions/set-person-featured-action.svelte'; @@ -195,6 +196,9 @@ {#if stack?.primaryAssetId !== asset.id} + {#if stack?.assets?.length > 2} + + {/if} {/if} {/if} {#if album} diff --git a/web/src/lib/components/asset-viewer/asset-viewer.svelte b/web/src/lib/components/asset-viewer/asset-viewer.svelte index 4f793e7b52..452510f508 100644 --- a/web/src/lib/components/asset-viewer/asset-viewer.svelte +++ b/web/src/lib/components/asset-viewer/asset-viewer.svelte @@ -111,7 +111,7 @@ let zoomToggle = $state(() => void 0); const refreshStack = async () => { - if (authManager.key) { + if (authManager.isSharedLink) { return; } @@ -191,7 +191,7 @@ }); const handleGetAllAlbums = async () => { - if (authManager.key) { + if (authManager.isSharedLink) { return; } @@ -328,6 +328,13 @@ await handleGetAllAlbums(); break; } + case AssetAction.REMOVE_ASSET_FROM_STACK: { + stack = action.stack; + if (stack) { + asset = stack.assets[0]; + } + break; + } case AssetAction.SET_STACK_PRIMARY_ASSET: { stack = action.stack; break; diff --git a/web/src/lib/components/asset-viewer/detail-panel-star-rating.svelte b/web/src/lib/components/asset-viewer/detail-panel-star-rating.svelte index 0ec8692180..d333d73be1 100644 --- a/web/src/lib/components/asset-viewer/detail-panel-star-rating.svelte +++ b/web/src/lib/components/asset-viewer/detail-panel-star-rating.svelte @@ -25,7 +25,7 @@ }; -{#if !authManager.key && $preferences?.ratings.enabled} +{#if !authManager.isSharedLink && $preferences?.ratings.enabled}
handlePromiseError(handleChangeRating(rating))} />
diff --git a/web/src/lib/components/asset-viewer/detail-panel-tags.svelte b/web/src/lib/components/asset-viewer/detail-panel-tags.svelte index a6a080d52b..4dd05f520a 100644 --- a/web/src/lib/components/asset-viewer/detail-panel-tags.svelte +++ b/web/src/lib/components/asset-viewer/detail-panel-tags.svelte @@ -3,10 +3,10 @@ import Icon from '$lib/components/elements/icon.svelte'; import { AppRoute } from '$lib/constants'; import { authManager } from '$lib/managers/auth-manager.svelte'; - import { modalManager } from '$lib/managers/modal-manager.svelte'; import AssetTagModal from '$lib/modals/AssetTagModal.svelte'; import { removeTag } from '$lib/utils/asset-utils'; import { getAssetInfo, type AssetResponseDto } from '@immich/sdk'; + import { modalManager } from '@immich/ui'; import { mdiClose, mdiPlus } from '@mdi/js'; import { t } from 'svelte-i18n'; @@ -37,7 +37,7 @@ -{#if isOwner && !authManager.key} +{#if isOwner && !authManager.isSharedLink}

{$t('tags').toUpperCase()}

diff --git a/web/src/lib/components/asset-viewer/detail-panel.svelte b/web/src/lib/components/asset-viewer/detail-panel.svelte index ef4ddc13ce..e3c29e5c1f 100644 --- a/web/src/lib/components/asset-viewer/detail-panel.svelte +++ b/web/src/lib/components/asset-viewer/detail-panel.svelte @@ -85,7 +85,7 @@ const handleNewAsset = async (newAsset: AssetResponseDto) => { // TODO: check if reloading asset data is necessary - if (newAsset.id && !authManager.key) { + if (newAsset.id && !authManager.isSharedLink) { const data = await getAssetInfo({ id: asset.id }); people = data?.people || []; unassignedFaces = data?.unassignedFaces || []; @@ -195,7 +195,7 @@ - {#if !authManager.key && isOwner} + {#if !authManager.isSharedLink && isOwner}

{$t('people').toUpperCase()}

diff --git a/web/src/lib/components/asset-viewer/editor/editor-panel.svelte b/web/src/lib/components/asset-viewer/editor/editor-panel.svelte index f4d5c69c62..203f1c6587 100644 --- a/web/src/lib/components/asset-viewer/editor/editor-panel.svelte +++ b/web/src/lib/components/asset-viewer/editor/editor-panel.svelte @@ -1,10 +1,9 @@ diff --git a/web/src/lib/components/asset-viewer/slideshow-bar.svelte b/web/src/lib/components/asset-viewer/slideshow-bar.svelte index a52bcbf707..c2b2a63586 100644 --- a/web/src/lib/components/asset-viewer/slideshow-bar.svelte +++ b/web/src/lib/components/asset-viewer/slideshow-bar.svelte @@ -2,10 +2,9 @@ import { shortcuts } from '$lib/actions/shortcut'; import ProgressBar from '$lib/components/shared-components/progress-bar/progress-bar.svelte'; import { ProgressBarStatus } from '$lib/constants'; - import { modalManager } from '$lib/managers/modal-manager.svelte'; import SlideshowSettingsModal from '$lib/modals/SlideshowSettingsModal.svelte'; import { SlideshowNavigation, slideshowStore } from '$lib/stores/slideshow.store'; - import { IconButton } from '@immich/ui'; + import { IconButton, modalManager } from '@immich/ui'; import { mdiChevronLeft, mdiChevronRight, mdiClose, mdiCog, mdiFullscreen, mdiPause, mdiPlay } from '@mdi/js'; import { onDestroy, onMount } from 'svelte'; import { swipe } from 'svelte-gestures'; diff --git a/web/src/lib/components/assets/thumbnail/thumbnail.svelte b/web/src/lib/components/assets/thumbnail/thumbnail.svelte index 04f6d94e0f..53dce2cdf8 100644 --- a/web/src/lib/components/assets/thumbnail/thumbnail.svelte +++ b/web/src/lib/components/assets/thumbnail/thumbnail.svelte @@ -270,13 +270,13 @@ {/if} - {#if !authManager.key && asset.isFavorite} + {#if !authManager.isSharedLink && asset.isFavorite}
{/if} - {#if !authManager.key && showArchiveIcon && asset.visibility === AssetVisibility.Archive} + {#if !authManager.isSharedLink && showArchiveIcon && asset.visibility === AssetVisibility.Archive}
@@ -343,11 +343,12 @@
{/if} diff --git a/web/src/lib/components/elements/search-bar.svelte b/web/src/lib/components/elements/search-bar.svelte index 2440285704..dcf314f59d 100644 --- a/web/src/lib/components/elements/search-bar.svelte +++ b/web/src/lib/components/elements/search-bar.svelte @@ -1,9 +1,9 @@ + +
+ + + +

+ + {#snippet children({ message })} + + {message} + + {/snippet} + +

+
+ +

+ +

+ + + + 3 + + + + 2 + + + + 1 + + + +

+ + {#snippet children({ message })} + + {message} + + {/snippet} + +

+
+
diff --git a/web/src/lib/components/pages/SharedLinkErrorPage.svelte b/web/src/lib/components/pages/SharedLinkErrorPage.svelte new file mode 100644 index 0000000000..9103a7710c --- /dev/null +++ b/web/src/lib/components/pages/SharedLinkErrorPage.svelte @@ -0,0 +1,14 @@ + + + + Oops! Error - Immich + + +
+

Page not found :/

+ {#if page.error?.message} +

{page.error.message}

+ {/if} +
diff --git a/web/src/lib/components/pages/SharedLinkPage.svelte b/web/src/lib/components/pages/SharedLinkPage.svelte new file mode 100644 index 0000000000..a3f5599077 --- /dev/null +++ b/web/src/lib/components/pages/SharedLinkPage.svelte @@ -0,0 +1,108 @@ + + + + {title} + + +{#if passwordRequired} +
+
+
{$t('password_required')}
+
+ {$t('sharing_enter_password')} +
+
+
+ + + +
+
+
+
+ + {#snippet leading()} + + {/snippet} + + {#snippet trailing()} + + {/snippet} + +
+{/if} + +{#if !passwordRequired && sharedLink?.type == SharedLinkType.Album} + +{/if} +{#if !passwordRequired && sharedLink?.type == SharedLinkType.Individual} +
+ +
+{/if} diff --git a/web/src/lib/components/photos-page/actions/add-to-album.svelte b/web/src/lib/components/photos-page/actions/add-to-album.svelte index c00fd2e1c9..5ec2e879c9 100644 --- a/web/src/lib/components/photos-page/actions/add-to-album.svelte +++ b/web/src/lib/components/photos-page/actions/add-to-album.svelte @@ -1,9 +1,9 @@ diff --git a/web/src/lib/components/photos-page/actions/download-action.svelte b/web/src/lib/components/photos-page/actions/download-action.svelte index 73f1a77742..0a1376374c 100644 --- a/web/src/lib/components/photos-page/actions/download-action.svelte +++ b/web/src/lib/components/photos-page/actions/download-action.svelte @@ -4,11 +4,11 @@ import { authManager } from '$lib/managers/auth-manager.svelte'; import { downloadArchive, downloadFile } from '$lib/utils/asset-utils'; import { getAssetInfo } from '@immich/sdk'; + import { IconButton } from '@immich/ui'; import { mdiCloudDownloadOutline, mdiFileDownloadOutline, mdiFolderDownloadOutline } from '@mdi/js'; import { t } from 'svelte-i18n'; import MenuOption from '../../shared-components/context-menu/menu-option.svelte'; import { getAssetControlContext } from '../asset-select-control-bar.svelte'; - import { IconButton } from '@immich/ui'; interface Props { filename?: string; @@ -23,7 +23,7 @@ const assets = [...getAssets()]; if (assets.length === 1) { clearSelect(); - let asset = await getAssetInfo({ id: assets[0].id, key: authManager.key }); + let asset = await getAssetInfo({ ...authManager.params, id: assets[0].id }); await downloadFile(asset); return; } diff --git a/web/src/lib/components/photos-page/actions/link-live-photo-action.svelte b/web/src/lib/components/photos-page/actions/link-live-photo-action.svelte index 09ca94cb25..6e3a7c789c 100644 --- a/web/src/lib/components/photos-page/actions/link-live-photo-action.svelte +++ b/web/src/lib/components/photos-page/actions/link-live-photo-action.svelte @@ -1,15 +1,15 @@ {#if unstack} - + {:else} {/if} diff --git a/web/src/lib/components/photos-page/actions/tag-action.svelte b/web/src/lib/components/photos-page/actions/tag-action.svelte index 5bad8ca327..a5079cc028 100644 --- a/web/src/lib/components/photos-page/actions/tag-action.svelte +++ b/web/src/lib/components/photos-page/actions/tag-action.svelte @@ -1,8 +1,7 @@ - -
-
- {#if showLogo} - - {:else if icon} - - {/if} -

- {title} -

-
- - -
diff --git a/web/src/lib/components/shared-components/navigation-bar/account-info-panel.svelte b/web/src/lib/components/shared-components/navigation-bar/account-info-panel.svelte index b079ecb241..b1746932b1 100644 --- a/web/src/lib/components/shared-components/navigation-bar/account-info-panel.svelte +++ b/web/src/lib/components/shared-components/navigation-bar/account-info-panel.svelte @@ -3,13 +3,12 @@ import { focusTrap } from '$lib/actions/focus-trap'; import Icon from '$lib/components/elements/icon.svelte'; import { AppRoute } from '$lib/constants'; - import { modalManager } from '$lib/managers/modal-manager.svelte'; import AvatarEditModal from '$lib/modals/AvatarEditModal.svelte'; import HelpAndFeedbackModal from '$lib/modals/HelpAndFeedbackModal.svelte'; import { user } from '$lib/stores/user.store'; import { userInteraction } from '$lib/stores/user.svelte'; import { getAboutInfo, type ServerAboutResponseDto } from '@immich/sdk'; - import { Button, IconButton } from '@immich/ui'; + import { Button, IconButton, modalManager } from '@immich/ui'; import { mdiCog, mdiLogout, mdiPencil, mdiWrench } from '@mdi/js'; import { onMount } from 'svelte'; import { t } from 'svelte-i18n'; diff --git a/web/src/lib/components/shared-components/search-bar/search-bar.svelte b/web/src/lib/components/shared-components/search-bar/search-bar.svelte index 9f36431f19..4c807e7652 100644 --- a/web/src/lib/components/shared-components/search-bar/search-bar.svelte +++ b/web/src/lib/components/shared-components/search-bar/search-bar.svelte @@ -3,14 +3,13 @@ import { focusOutside } from '$lib/actions/focus-outside'; import { shortcuts } from '$lib/actions/shortcut'; import { AppRoute } from '$lib/constants'; - import { modalManager } from '$lib/managers/modal-manager.svelte'; import SearchFilterModal from '$lib/modals/SearchFilterModal.svelte'; import { searchStore } from '$lib/stores/search.svelte'; import { handlePromiseError } from '$lib/utils'; import { generateId } from '$lib/utils/generate-id'; import { getMetadataSearchQuery } from '$lib/utils/metadata-search'; import type { MetadataSearchDto, SmartSearchDto } from '@immich/sdk'; - import { IconButton } from '@immich/ui'; + import { IconButton, modalManager } from '@immich/ui'; import { mdiClose, mdiMagnify, mdiTune } from '@mdi/js'; import { onDestroy, tick } from 'svelte'; import { t } from 'svelte-i18n'; diff --git a/web/src/lib/components/shared-components/settings/setting-accordion-state.svelte b/web/src/lib/components/shared-components/settings/setting-accordion-state.svelte index 6b3ae81685..b0deb64316 100644 --- a/web/src/lib/components/shared-components/settings/setting-accordion-state.svelte +++ b/web/src/lib/components/shared-components/settings/setting-accordion-state.svelte @@ -12,6 +12,7 @@ import { goto } from '$app/navigation'; import type { Snippet } from 'svelte'; import { handlePromiseError } from '$lib/utils'; + import { SvelteURLSearchParams } from 'svelte/reactivity'; const getParamValues = (param: string) => { return new Set((page.url.searchParams.get(param) || '').split(' ').filter((x) => x !== '')); @@ -26,7 +27,7 @@ let { queryParam, state = writable(getParamValues(queryParam)), children }: Props = $props(); setAccordionState(state); - const searchParams = new URLSearchParams(page.url.searchParams); + const searchParams = new SvelteURLSearchParams(page.url.searchParams); $effect(() => { if ($state.size > 0) { diff --git a/web/src/lib/components/shared-components/settings/setting-input-field.spec.ts b/web/src/lib/components/shared-components/settings/setting-input-field.spec.ts index 80cb920074..a6b0140de4 100644 --- a/web/src/lib/components/shared-components/settings/setting-input-field.spec.ts +++ b/web/src/lib/components/shared-components/settings/setting-input-field.spec.ts @@ -46,6 +46,6 @@ describe('SettingInputField component', () => { expect(numberInput.value).toEqual(''); await user.click(document.body); - expect(numberInput.value).toEqual('0'); + expect(numberInput.value).toEqual(''); }); }); diff --git a/web/src/lib/components/shared-components/settings/setting-input-field.svelte b/web/src/lib/components/shared-components/settings/setting-input-field.svelte index 87451667cd..bb0e2d8bef 100644 --- a/web/src/lib/components/shared-components/settings/setting-input-field.svelte +++ b/web/src/lib/components/shared-components/settings/setting-input-field.svelte @@ -49,6 +49,11 @@ value = e.currentTarget.value; if (inputType === SettingInputFieldType.NUMBER) { + if (value === '' && !required) { + value = null; + return; + } + let newValue = Number(value) || 0; if (newValue < min) { newValue = min; diff --git a/web/src/lib/components/shared-components/side-bar/purchase-info.svelte b/web/src/lib/components/shared-components/side-bar/purchase-info.svelte index 5a984e94be..f21cc7de4d 100644 --- a/web/src/lib/components/shared-components/side-bar/purchase-info.svelte +++ b/web/src/lib/components/shared-components/side-bar/purchase-info.svelte @@ -5,7 +5,6 @@ import Portal from '$lib/components/shared-components/portal/portal.svelte'; import SupporterBadge from '$lib/components/shared-components/side-bar/supporter-badge.svelte'; import { AppRoute } from '$lib/constants'; - import { modalManager } from '$lib/managers/modal-manager.svelte'; import PurchaseModal from '$lib/modals/PurchaseModal.svelte'; import { purchaseStore } from '$lib/stores/purchase.store'; import { preferences } from '$lib/stores/user.store'; @@ -13,9 +12,10 @@ import { handleError } from '$lib/utils/handle-error'; import { getButtonVisibility } from '$lib/utils/purchase-utils'; import { updateMyPreferences } from '@immich/sdk'; - import { Button, IconButton } from '@immich/ui'; + import { Button, IconButton, modalManager } from '@immich/ui'; import { mdiClose, mdiInformationOutline } from '@mdi/js'; import { t } from 'svelte-i18n'; + import { SvelteDate } from 'svelte/reactivity'; import { fade } from 'svelte/transition'; let showMessage = $state(false); @@ -37,7 +37,7 @@ }; const hideButton = async (always: boolean) => { - const hideBuyButtonUntil = new Date(); + const hideBuyButtonUntil = new SvelteDate(); if (always) { hideBuyButtonUntil.setFullYear(2124); // see ya in 100 years diff --git a/web/src/lib/components/shared-components/side-bar/server-status.svelte b/web/src/lib/components/shared-components/side-bar/server-status.svelte index 0a9f3d9d8c..3c3da8eb5f 100644 --- a/web/src/lib/components/shared-components/side-bar/server-status.svelte +++ b/web/src/lib/components/shared-components/side-bar/server-status.svelte @@ -1,6 +1,5 @@ diff --git a/web/src/lib/components/sharedlinks-page/shared-link-card.svelte b/web/src/lib/components/sharedlinks-page/shared-link-card.svelte index 3e827281e7..bd1c2924b0 100644 --- a/web/src/lib/components/sharedlinks-page/shared-link-card.svelte +++ b/web/src/lib/components/sharedlinks-page/shared-link-card.svelte @@ -1,16 +1,16 @@ -
+
import { dateFormats } from '$lib/constants'; - import { modalManager } from '$lib/managers/modal-manager.svelte'; import ApiKeyModal from '$lib/modals/ApiKeyModal.svelte'; import ApiKeySecretModal from '$lib/modals/ApiKeySecretModal.svelte'; import { locale } from '$lib/stores/preferences.store'; import { createApiKey, deleteApiKey, getApiKeys, updateApiKey, type ApiKeyResponseDto } from '@immich/sdk'; - import { Button, IconButton } from '@immich/ui'; + import { Button, IconButton, modalManager } from '@immich/ui'; import { mdiPencilOutline, mdiTrashCanOutline } from '@mdi/js'; import { t } from 'svelte-i18n'; import { fade } from 'svelte/transition'; diff --git a/web/src/lib/components/user-settings-page/user-purchase-settings.svelte b/web/src/lib/components/user-settings-page/user-purchase-settings.svelte index 2690559d89..69825dcfda 100644 --- a/web/src/lib/components/user-settings-page/user-purchase-settings.svelte +++ b/web/src/lib/components/user-settings-page/user-purchase-settings.svelte @@ -5,7 +5,6 @@ import PurchaseContent from '$lib/components/shared-components/purchasing/purchase-content.svelte'; import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte'; import { dateFormats } from '$lib/constants'; - import { modalManager } from '$lib/managers/modal-manager.svelte'; import { locale } from '$lib/stores/preferences.store'; import { purchaseStore } from '$lib/stores/purchase.store'; import { preferences, user } from '$lib/stores/user.store'; @@ -20,7 +19,7 @@ isHttpError, type LicenseResponseDto, } from '@immich/sdk'; - import { Button } from '@immich/ui'; + import { Button, modalManager } from '@immich/ui'; import { mdiKey } from '@mdi/js'; import { onMount } from 'svelte'; import { t } from 'svelte-i18n'; diff --git a/web/src/lib/components/utilities-page/large-assets/large-asset-data.svelte b/web/src/lib/components/utilities-page/large-assets/large-asset-data.svelte new file mode 100644 index 0000000000..db1fe4615b --- /dev/null +++ b/web/src/lib/components/utilities-page/large-assets/large-asset-data.svelte @@ -0,0 +1,37 @@ + + +
+
+ onViewAsset(asset)} thumbnailSize={boxWidth} /> + + {#if !!asset.libraryId} +
External
+ {/if} +
+
+ {asset.originalFileName} +
+
+

{getFileSize(asset, 1)}

+
+
diff --git a/web/src/lib/components/utilities-page/utilities-menu.svelte b/web/src/lib/components/utilities-page/utilities-menu.svelte index 7deddc6bee..5484ce4ea0 100644 --- a/web/src/lib/components/utilities-page/utilities-menu.svelte +++ b/web/src/lib/components/utilities-page/utilities-menu.svelte @@ -1,7 +1,7 @@ @@ -17,4 +17,13 @@ {$t('review_duplicates')} + + + + {$t('review_large_files')} +
diff --git a/web/src/lib/constants.ts b/web/src/lib/constants.ts index 1a40f8522e..f2de6d5deb 100644 --- a/web/src/lib/constants.ts +++ b/web/src/lib/constants.ts @@ -11,6 +11,7 @@ export enum AssetAction { UNSTACK = 'unstack', KEEP_THIS_DELETE_OTHERS = 'keep-this-delete-others', SET_STACK_PRIMARY_ASSET = 'set-stack-primary-asset', + REMOVE_ASSET_FROM_STACK = 'remove-asset-from-stack', SET_VISIBILITY_LOCKED = 'set-visibility-locked', SET_VISIBILITY_TIMELINE = 'set-visibility-timeline', } @@ -50,6 +51,7 @@ export enum AppRoute { UTILITIES = '/utilities', DUPLICATES = '/utilities/duplicates', + LARGE_FILES = '/utilities/large-files', FOLDERS = '/folders', TAGS = '/tags', diff --git a/web/src/lib/managers/auth-manager.svelte.ts b/web/src/lib/managers/auth-manager.svelte.ts index 9e8e30b773..0128892c3f 100644 --- a/web/src/lib/managers/auth-manager.svelte.ts +++ b/web/src/lib/managers/auth-manager.svelte.ts @@ -6,7 +6,8 @@ import { isSharedLinkRoute } from '$lib/utils/navigation'; import { logout } from '@immich/sdk'; class AuthManager { - key = $derived(isSharedLinkRoute(page.route?.id) ? page.params.key : undefined); + isSharedLink = $derived(isSharedLinkRoute(page.route?.id)); + params = $derived(this.isSharedLink ? { key: page.params.key, slug: page.params.slug } : {}); async logout() { let redirectUri; diff --git a/web/src/lib/managers/modal-manager.svelte.ts b/web/src/lib/managers/modal-manager.svelte.ts deleted file mode 100644 index 73967aef15..0000000000 --- a/web/src/lib/managers/modal-manager.svelte.ts +++ /dev/null @@ -1,53 +0,0 @@ -import ConfirmModal from '$lib/modals/ConfirmModal.svelte'; -import { mount, unmount, type Component, type ComponentProps } from 'svelte'; - -type OnCloseData = T extends { onClose: (data?: infer R) => void } - ? R | undefined - : T extends { onClose: (data: infer R) => void } - ? R - : never; -type ExtendsEmptyObject = keyof T extends never ? never : T; -type StripValueIfOptional = T extends undefined ? undefined : T; - -// if the modal does not expect any props, makes the props param optional but also allows passing `{}` and `undefined` -type OptionalParamIfEmpty = ExtendsEmptyObject extends never ? [] | [Record | undefined] : [T]; - -class ModalManager { - show(Component: Component, ...props: OptionalParamIfEmpty>) { - return this.open(Component, ...props).onClose; - } - - open>( - Component: Component, - ...props: OptionalParamIfEmpty> - ) { - let modal: object = {}; - let onClose: (...args: [StripValueIfOptional]) => Promise; - - const deferred = new Promise>((resolve) => { - onClose = async (...args: [StripValueIfOptional]) => { - await unmount(modal); - setTimeout(() => resolve(args?.[0]), 0); - }; - - modal = mount(Component, { - target: document.body, - props: { - ...((props?.[0] ?? {}) as T), - onClose, - }, - }); - }); - - return { - onClose: deferred, - close: (...args: [StripValueIfOptional]) => onClose(args[0]), - }; - } - - showDialog(options: Omit, 'onClose'>) { - return this.show(ConfirmModal, options); - } -} - -export const modalManager = new ModalManager(); diff --git a/web/src/lib/managers/timeline-manager/day-group.svelte.ts b/web/src/lib/managers/timeline-manager/day-group.svelte.ts index 2a949499ec..9d5008bf83 100644 --- a/web/src/lib/managers/timeline-manager/day-group.svelte.ts +++ b/web/src/lib/managers/timeline-manager/day-group.svelte.ts @@ -4,6 +4,7 @@ import type { CommonLayoutOptions } from '$lib/utils/layout-utils'; import { getJustifiedLayoutFromAssets, getPosition } from '$lib/utils/layout-utils'; import { plainDateTimeCompare } from '$lib/utils/timeline-util'; +import { SvelteSet } from 'svelte/reactivity'; import type { MonthGroup } from './month-group.svelte'; import type { AssetOperation, Direction, MoveAsset, TimelineAsset } from './types'; import { ViewerAsset } from './viewer-asset.svelte'; @@ -109,13 +110,13 @@ export class DayGroup { if (ids.size === 0) { return { moveAssets: [] as MoveAsset[], - processedIds: new Set(), + processedIds: new SvelteSet(), unprocessedIds: ids, changedGeometry: false, }; } - const unprocessedIds = new Set(ids); - const processedIds = new Set(); + const unprocessedIds = new SvelteSet(ids); + const processedIds = new SvelteSet(); const moveAssets: MoveAsset[] = []; let changedGeometry = false; for (const assetId of unprocessedIds) { diff --git a/web/src/lib/managers/timeline-manager/group-insertion-cache.svelte.ts b/web/src/lib/managers/timeline-manager/group-insertion-cache.svelte.ts index 66cca61d45..aa4bae8919 100644 --- a/web/src/lib/managers/timeline-manager/group-insertion-cache.svelte.ts +++ b/web/src/lib/managers/timeline-manager/group-insertion-cache.svelte.ts @@ -1,5 +1,6 @@ -import { setDifference, type TimelinePlainDate } from '$lib/utils/timeline-util'; +import { setDifference, type TimelineDate } from '$lib/utils/timeline-util'; import { AssetOrder } from '@immich/sdk'; +import { SvelteSet } from 'svelte/reactivity'; import type { DayGroup } from './day-group.svelte'; import type { MonthGroup } from './month-group.svelte'; import type { TimelineAsset } from './types'; @@ -9,14 +10,14 @@ export class GroupInsertionCache { [year: number]: { [month: number]: { [day: number]: DayGroup } }; } = {}; unprocessedAssets: TimelineAsset[] = []; - changedDayGroups = new Set(); - newDayGroups = new Set(); + changedDayGroups = new SvelteSet(); + newDayGroups = new SvelteSet(); - getDayGroup({ year, month, day }: TimelinePlainDate): DayGroup | undefined { + getDayGroup({ year, month, day }: TimelineDate): DayGroup | undefined { return this.#lookupCache[year]?.[month]?.[day]; } - setDayGroup(dayGroup: DayGroup, { year, month, day }: TimelinePlainDate) { + setDayGroup(dayGroup: DayGroup, { year, month, day }: TimelineDate) { if (!this.#lookupCache[year]) { this.#lookupCache[year] = {}; } @@ -31,7 +32,7 @@ export class GroupInsertionCache { } get updatedBuckets() { - const updated = new Set(); + const updated = new SvelteSet(); for (const group of this.changedDayGroups) { updated.add(group.monthGroup); } @@ -39,7 +40,7 @@ export class GroupInsertionCache { } get bucketsWithNewDayGroups() { - const updated = new Set(); + const updated = new SvelteSet(); for (const group of this.newDayGroups) { updated.add(group.monthGroup); } diff --git a/web/src/lib/managers/timeline-manager/internal/load-support.svelte.ts b/web/src/lib/managers/timeline-manager/internal/load-support.svelte.ts index ebe8acbecb..82a9e8083d 100644 --- a/web/src/lib/managers/timeline-manager/internal/load-support.svelte.ts +++ b/web/src/lib/managers/timeline-manager/internal/load-support.svelte.ts @@ -1,7 +1,6 @@ import { authManager } from '$lib/managers/auth-manager.svelte'; import { toISOYearMonthUTC } from '$lib/utils/timeline-util'; import { getTimeBucket } from '@immich/sdk'; - import type { MonthGroup } from '../month-group.svelte'; import type { TimelineManager } from '../timeline-manager.svelte'; import type { TimelineManagerOptions } from '../types'; @@ -18,12 +17,11 @@ export async function loadFromTimeBuckets( } const timeBucket = toISOYearMonthUTC(monthGroup.yearMonth); - const key = authManager.key; const bucketResponse = await getTimeBucket( { + ...authManager.params, ...options, timeBucket, - key, }, { signal }, ); @@ -35,9 +33,9 @@ export async function loadFromTimeBuckets( if (options.timelineAlbumId) { const albumAssets = await getTimeBucket( { + ...authManager.params, albumId: options.timelineAlbumId, timeBucket, - key, }, { signal }, ); diff --git a/web/src/lib/managers/timeline-manager/internal/operations-support.svelte.ts b/web/src/lib/managers/timeline-manager/internal/operations-support.svelte.ts index 82ec78499b..4bc99c0315 100644 --- a/web/src/lib/managers/timeline-manager/internal/operations-support.svelte.ts +++ b/web/src/lib/managers/timeline-manager/internal/operations-support.svelte.ts @@ -1,6 +1,7 @@ -import { setDifference, type TimelinePlainDate } from '$lib/utils/timeline-util'; +import { setDifference, type TimelineDate } from '$lib/utils/timeline-util'; import { AssetOrder } from '@immich/sdk'; +import { SvelteSet } from 'svelte/reactivity'; import { GroupInsertionCache } from '../group-insertion-cache.svelte'; import { MonthGroup } from '../month-group.svelte'; import type { TimelineManager } from '../timeline-manager.svelte'; @@ -18,7 +19,7 @@ export function addAssetsToMonthGroups( } const addContext = new GroupInsertionCache(); - const updatedMonthGroups = new Set(); + const updatedMonthGroups = new SvelteSet(); const monthCount = timelineManager.months.length; for (const asset of assets) { let month = getMonthGroupByDate(timelineManager, asset.localDateTime); @@ -63,13 +64,13 @@ export function runAssetOperation( options: { order: AssetOrder }, ) { if (ids.size === 0) { - return { processedIds: new Set(), unprocessedIds: ids, changedGeometry: false }; + return { processedIds: new SvelteSet(), unprocessedIds: ids, changedGeometry: false }; } - const changedMonthGroups = new Set(); - let idsToProcess = new Set(ids); - const idsProcessed = new Set(); - const combinedMoveAssets: { asset: TimelineAsset; date: TimelinePlainDate }[][] = []; + const changedMonthGroups = new SvelteSet(); + let idsToProcess = new SvelteSet(ids); + const idsProcessed = new SvelteSet(); + const combinedMoveAssets: { asset: TimelineAsset; date: TimelineDate }[][] = []; for (const month of timelineManager.months) { if (idsToProcess.size > 0) { const { moveAssets, processedIds, changedGeometry } = month.runAssetOperation(idsToProcess, operation); diff --git a/web/src/lib/managers/timeline-manager/internal/search-support.svelte.ts b/web/src/lib/managers/timeline-manager/internal/search-support.svelte.ts index f86e2c1501..7e6ae734dc 100644 --- a/web/src/lib/managers/timeline-manager/internal/search-support.svelte.ts +++ b/web/src/lib/managers/timeline-manager/internal/search-support.svelte.ts @@ -1,4 +1,5 @@ -import { plainDateTimeCompare, type TimelinePlainYearMonth } from '$lib/utils/timeline-util'; +import { plainDateTimeCompare, type TimelineYearMonth } from '$lib/utils/timeline-util'; +import { AssetOrder } from '@immich/sdk'; import type { MonthGroup } from '../month-group.svelte'; import type { TimelineManager } from '../timeline-manager.svelte'; import type { AssetDescriptor, Direction, TimelineAsset } from '../types'; @@ -41,7 +42,7 @@ export function findMonthGroupForAsset(timelineManager: TimelineManager, id: str export function getMonthGroupByDate( timelineManager: TimelineManager, - targetYearMonth: TimelinePlainYearMonth, + targetYearMonth: TimelineYearMonth, ): MonthGroup | undefined { return timelineManager.months.find( (month) => month.yearMonth.year === targetYearMonth.year && month.yearMonth.month === targetYearMonth.month, @@ -113,11 +114,10 @@ export async function retrieveRange(timelineManager: TimelineManager, start: Ass if (!endMonthGroup || !endAsset) { return []; } - let direction: Direction = 'earlier'; - if (plainDateTimeCompare(true, startAsset.localDateTime, endAsset.localDateTime) < 0) { + const assetOrder: AssetOrder = timelineManager.getAssetOrder(); + if (plainDateTimeCompare(assetOrder === AssetOrder.Desc, startAsset.localDateTime, endAsset.localDateTime) < 0) { [startAsset, endAsset] = [endAsset, startAsset]; [startMonthGroup, endMonthGroup] = [endMonthGroup, startMonthGroup]; - direction = 'earlier'; } const range: TimelineAsset[] = []; @@ -126,7 +126,6 @@ export async function retrieveRange(timelineManager: TimelineManager, start: Ass startMonthGroup, startDayGroup, startAsset, - direction, })) { range.push(targetAsset); if (targetAsset.id === endAsset.id) { @@ -136,7 +135,7 @@ export async function retrieveRange(timelineManager: TimelineManager, start: Ass return range; } -export function findMonthGroupForDate(timelineManager: TimelineManager, targetYearMonth: TimelinePlainYearMonth) { +export function findMonthGroupForDate(timelineManager: TimelineManager, targetYearMonth: TimelineYearMonth) { for (const month of timelineManager.months) { const { year, month: monthNum } = month.yearMonth; if (monthNum === targetYearMonth.month && year === targetYearMonth.year) { diff --git a/web/src/lib/managers/timeline-manager/month-group.svelte.ts b/web/src/lib/managers/timeline-manager/month-group.svelte.ts index bbcfe88caa..03d138f680 100644 --- a/web/src/lib/managers/timeline-manager/month-group.svelte.ts +++ b/web/src/lib/managers/timeline-manager/month-group.svelte.ts @@ -10,13 +10,14 @@ import { fromTimelinePlainYearMonth, getTimes, setDifference, - type TimelinePlainDateTime, - type TimelinePlainYearMonth, + type TimelineDateTime, + type TimelineYearMonth, } from '$lib/utils/timeline-util'; import { t } from 'svelte-i18n'; import { get } from 'svelte/store'; +import { SvelteSet } from 'svelte/reactivity'; import { DayGroup } from './day-group.svelte'; import { GroupInsertionCache } from './group-insertion-cache.svelte'; import type { TimelineManager } from './timeline-manager.svelte'; @@ -46,11 +47,11 @@ export class MonthGroup { isHeightActual: boolean = $state(false); readonly monthGroupTitle: string; - readonly yearMonth: TimelinePlainYearMonth; + readonly yearMonth: TimelineYearMonth; constructor( store: TimelineManager, - yearMonth: TimelinePlainYearMonth, + yearMonth: TimelineYearMonth, initialCount: number, order: AssetOrder = AssetOrder.Desc, ) { @@ -115,15 +116,15 @@ export class MonthGroup { if (ids.size === 0) { return { moveAssets: [] as MoveAsset[], - processedIds: new Set(), + processedIds: new SvelteSet(), unprocessedIds: ids, changedGeometry: false, }; } const { dayGroups } = this; let combinedChangedGeometry = false; - let idsToProcess = new Set(ids); - const idsProcessed = new Set(); + let idsToProcess = new SvelteSet(ids); + const idsProcessed = new SvelteSet(); const combinedMoveAssets: MoveAsset[][] = []; let index = dayGroups.length; while (index--) { @@ -350,7 +351,7 @@ export class MonthGroup { } } - findClosest(target: TimelinePlainDateTime) { + findClosest(target: TimelineDateTime) { const targetDate = fromTimelinePlainDateTime(target); let closest = undefined; let smallestDiff = Infinity; diff --git a/web/src/lib/managers/timeline-manager/timeline-manager.svelte.ts b/web/src/lib/managers/timeline-manager/timeline-manager.svelte.ts index 8aacd0a90a..2e31fa9bc1 100644 --- a/web/src/lib/managers/timeline-manager/timeline-manager.svelte.ts +++ b/web/src/lib/managers/timeline-manager/timeline-manager.svelte.ts @@ -3,10 +3,10 @@ import { AssetOrder, getAssetInfo, getTimeBuckets } from '@immich/sdk'; import { authManager } from '$lib/managers/auth-manager.svelte'; import { CancellableTask } from '$lib/utils/cancellable-task'; -import { toTimelineAsset, type TimelinePlainDateTime, type TimelinePlainYearMonth } from '$lib/utils/timeline-util'; +import { toTimelineAsset, type TimelineDateTime, type TimelineYearMonth } from '$lib/utils/timeline-util'; import { clamp, debounce, isEqual } from 'lodash-es'; -import { SvelteSet } from 'svelte/reactivity'; +import { SvelteDate, SvelteMap, SvelteSet } from 'svelte/reactivity'; import { updateIntersectionMonthGroup } from '$lib/managers/timeline-manager/internal/intersection-support.svelte'; import { updateGeometry } from '$lib/managers/timeline-manager/internal/layout-support.svelte'; @@ -288,12 +288,12 @@ export class TimelineManager { async #initializeMonthGroups() { const timebuckets = await getTimeBuckets({ + ...authManager.params, ...this.#options, - key: authManager.key, }); this.months = timebuckets.map((timeBucket) => { - const date = new Date(timeBucket.timeBucket); + const date = new SvelteDate(timeBucket.timeBucket); return new MonthGroup( this, { year: date.getUTCFullYear(), month: date.getUTCMonth() + 1 }, @@ -387,7 +387,7 @@ export class TimelineManager { }; } - async loadMonthGroup(yearMonth: TimelinePlainYearMonth, options?: { cancelable: boolean }): Promise { + async loadMonthGroup(yearMonth: TimelineYearMonth, options?: { cancelable: boolean }): Promise { let cancelable = true; if (options) { cancelable = options.cancelable; @@ -423,7 +423,7 @@ export class TimelineManager { if (monthGroup) { return monthGroup; } - const asset = toTimelineAsset(await getAssetInfo({ id, key: authManager.key })); + const asset = toTimelineAsset(await getAssetInfo({ ...authManager.params, id })); if (!asset || this.isExcluded(asset)) { return; } @@ -433,7 +433,7 @@ export class TimelineManager { } } - async #loadMonthGroupAtTime(yearMonth: TimelinePlainYearMonth, options?: { cancelable: boolean }) { + async #loadMonthGroupAtTime(yearMonth: TimelineYearMonth, options?: { cancelable: boolean }) { await this.loadMonthGroup(yearMonth, options); return getMonthGroupByDate(this, yearMonth); } @@ -456,14 +456,14 @@ export class TimelineManager { } updateAssetOperation(ids: string[], operation: AssetOperation) { - runAssetOperation(this, new Set(ids), operation, { order: this.#options.order ?? AssetOrder.Desc }); + runAssetOperation(this, new SvelteSet(ids), operation, { order: this.#options.order ?? AssetOrder.Desc }); } updateAssets(assets: TimelineAsset[]) { - const lookup = new Map(assets.map((asset) => [asset.id, asset])); + const lookup = new SvelteMap(assets.map((asset) => [asset.id, asset])); const { unprocessedIds } = runAssetOperation( this, - new Set(lookup.keys()), + new SvelteSet(lookup.keys()), (asset) => { updateObject(asset, lookup.get(asset.id)); return { remove: false }; @@ -480,7 +480,7 @@ export class TimelineManager { removeAssets(ids: string[]) { const { unprocessedIds } = runAssetOperation( this, - new Set(ids), + new SvelteSet(ids), () => { return { remove: true }; }, @@ -514,7 +514,7 @@ export class TimelineManager { return await getAssetWithOffset(this, assetDescriptor, interval, 'earlier'); } - async getClosestAssetToDate(dateTime: TimelinePlainDateTime) { + async getClosestAssetToDate(dateTime: TimelineDateTime) { const monthGroup = findMonthGroupForDate(this, dateTime); if (!monthGroup) { return; @@ -540,4 +540,8 @@ export class TimelineManager { isMismatched(this.#options.isTrashed, asset.isTrashed) ); } + + getAssetOrder() { + return this.#options.order ?? AssetOrder.Desc; + } } diff --git a/web/src/lib/managers/timeline-manager/types.ts b/web/src/lib/managers/timeline-manager/types.ts index 8e5523758b..18ee0426f3 100644 --- a/web/src/lib/managers/timeline-manager/types.ts +++ b/web/src/lib/managers/timeline-manager/types.ts @@ -1,4 +1,4 @@ -import type { TimelinePlainDate, TimelinePlainDateTime } from '$lib/utils/timeline-util'; +import type { TimelineDate, TimelineDateTime } from '$lib/utils/timeline-util'; import type { AssetStackResponseDto, AssetVisibility } from '@immich/sdk'; export type AssetApiGetTimeBucketsRequest = Parameters[0]; @@ -17,8 +17,8 @@ export type TimelineAsset = { ownerId: string; ratio: number; thumbhash: string | null; - localDateTime: TimelinePlainDateTime; - fileCreatedAt: TimelinePlainDateTime; + localDateTime: TimelineDateTime; + fileCreatedAt: TimelineDateTime; visibility: AssetVisibility; isFavorite: boolean; isTrashed: boolean; @@ -35,7 +35,7 @@ export type TimelineAsset = { export type AssetOperation = (asset: TimelineAsset) => { remove: boolean }; -export type MoveAsset = { asset: TimelineAsset; date: TimelinePlainDate }; +export type MoveAsset = { asset: TimelineAsset; date: TimelineDate }; export interface Viewport { width: number; diff --git a/web/src/lib/modals/AlbumOptionsModal.svelte b/web/src/lib/modals/AlbumOptionsModal.svelte index f9c9cab204..e56fe784a6 100644 --- a/web/src/lib/modals/AlbumOptionsModal.svelte +++ b/web/src/lib/modals/AlbumOptionsModal.svelte @@ -4,7 +4,6 @@ import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte'; import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte'; import UserAvatar from '$lib/components/shared-components/user-avatar.svelte'; - import { modalManager } from '$lib/managers/modal-manager.svelte'; import { handleError } from '$lib/utils/handle-error'; import { AlbumUserRole, @@ -15,7 +14,7 @@ type AlbumResponseDto, type UserResponseDto, } from '@immich/sdk'; - import { Modal, ModalBody } from '@immich/ui'; + import { Modal, ModalBody, modalManager } from '@immich/ui'; import { mdiArrowDownThin, mdiArrowUpThin, mdiDotsVertical, mdiPlus } from '@mdi/js'; import { findKey } from 'lodash-es'; import { t } from 'svelte-i18n'; diff --git a/web/src/lib/modals/AlbumPickerModal.svelte b/web/src/lib/modals/AlbumPickerModal.svelte index 3e16e03b80..1a6431d179 100644 --- a/web/src/lib/modals/AlbumPickerModal.svelte +++ b/web/src/lib/modals/AlbumPickerModal.svelte @@ -28,7 +28,7 @@ onMount(async () => { albums = await getAllAlbums({ shared: shared || undefined }); - recentAlbums = albums.sort((a, b) => (new Date(a.createdAt) > new Date(b.createdAt) ? -1 : 1)).slice(0, 3); + recentAlbums = albums.sort((a, b) => (new Date(a.updatedAt) > new Date(b.updatedAt) ? -1 : 1)).slice(0, 3); loading = false; }); diff --git a/web/src/lib/modals/AlbumShareModal.svelte b/web/src/lib/modals/AlbumShareModal.svelte index 83f63141ce..0b52008cf0 100644 --- a/web/src/lib/modals/AlbumShareModal.svelte +++ b/web/src/lib/modals/AlbumShareModal.svelte @@ -32,7 +32,7 @@ let sharedLinkUrl = $state(''); const handleViewQrCode = (sharedLink: SharedLinkResponseDto) => { - sharedLinkUrl = makeSharedLinkUrl(sharedLink.key); + sharedLinkUrl = makeSharedLinkUrl(sharedLink); }; const roleOptions: Array<{ title: string; value: AlbumUserRole | 'none'; icon?: string }> = [ diff --git a/web/src/lib/modals/AlbumUsersModal.svelte b/web/src/lib/modals/AlbumUsersModal.svelte index 4f1b8aa94d..32c8cd28cf 100644 --- a/web/src/lib/modals/AlbumUsersModal.svelte +++ b/web/src/lib/modals/AlbumUsersModal.svelte @@ -6,7 +6,6 @@ notificationController, } from '$lib/components/shared-components/notification/notification'; import UserAvatar from '$lib/components/shared-components/user-avatar.svelte'; - import { modalManager } from '$lib/managers/modal-manager.svelte'; import { handleError } from '$lib/utils/handle-error'; import { AlbumUserRole, @@ -16,7 +15,7 @@ type AlbumResponseDto, type UserResponseDto, } from '@immich/sdk'; - import { Modal, ModalBody } from '@immich/ui'; + import { Modal, ModalBody, modalManager } from '@immich/ui'; import { mdiDotsVertical } from '@mdi/js'; import { onMount } from 'svelte'; import { t } from 'svelte-i18n'; diff --git a/web/src/lib/modals/ApiKeyModal.svelte b/web/src/lib/modals/ApiKeyModal.svelte index 15902c8e53..e0e1197240 100644 --- a/web/src/lib/modals/ApiKeyModal.svelte +++ b/web/src/lib/modals/ApiKeyModal.svelte @@ -5,11 +5,18 @@ } from '$lib/components/shared-components/notification/notification'; import ApiKeyGrid from '$lib/components/user-settings-page/user-api-key-grid.svelte'; import { Permission } from '@immich/sdk'; - import { Button, Checkbox, HStack, Label, Modal, ModalBody, ModalFooter } from '@immich/ui'; - import { mdiKeyVariant } from '@mdi/js'; + import { Button, Checkbox, Field, HStack, IconButton, Input, Label, Modal, ModalBody, ModalFooter } from '@immich/ui'; + import { mdiClose, mdiKeyVariant } from '@mdi/js'; import { onMount } from 'svelte'; import { t } from 'svelte-i18n'; + const matches = (value: string) => { + value = value.toLowerCase(); + return ([title, items]: [string, Permission[]]) => { + return title.toLowerCase().includes(value) || items.some((item) => item.toLowerCase().includes(value)); + }; + }; + interface Props { apiKey: { name: string; permissions: Permission[] }; title: string; @@ -19,137 +26,26 @@ } let { apiKey = $bindable(), title, cancelText = $t('cancel'), submitText = $t('save'), onClose }: Props = $props(); + let name = $derived(apiKey.name); let selectedItems: Permission[] = $state(apiKey.permissions); let selectAllItems = $derived(selectedItems.length === Object.keys(Permission).length - 1); - const permissions: Map = new Map(); + const permissions: Record = {}; + for (const permission of Object.values(Permission)) { + if (permission === Permission.All) { + continue; + } - permissions.set('activity', [ - Permission.ActivityCreate, - Permission.ActivityRead, - Permission.ActivityUpdate, - Permission.ActivityDelete, - Permission.ActivityStatistics, - ]); + const [group] = permission.split('.'); + if (!permissions[group]) { + permissions[group] = []; + } + permissions[group].push(permission); + } - permissions.set('api_key', [ - Permission.ApiKeyCreate, - Permission.ApiKeyRead, - Permission.ApiKeyUpdate, - Permission.ApiKeyDelete, - ]); - - permissions.set('asset', [ - Permission.AssetRead, - Permission.AssetUpdate, - Permission.AssetDelete, - Permission.AssetShare, - Permission.AssetView, - Permission.AssetDownload, - Permission.AssetUpload, - ]); - - permissions.set('album', [ - Permission.AlbumCreate, - Permission.AlbumRead, - Permission.AlbumUpdate, - Permission.AlbumDelete, - Permission.AlbumStatistics, - - Permission.AlbumAddAsset, - Permission.AlbumRemoveAsset, - Permission.AlbumShare, - Permission.AlbumDownload, - ]); - - permissions.set('auth_device', [Permission.AuthDeviceDelete]); - - permissions.set('archive', [Permission.ArchiveRead]); - - permissions.set('face', [Permission.FaceCreate, Permission.FaceRead, Permission.FaceUpdate, Permission.FaceDelete]); - - permissions.set('library', [ - Permission.LibraryCreate, - Permission.LibraryRead, - Permission.LibraryUpdate, - Permission.LibraryDelete, - Permission.LibraryStatistics, - ]); - - permissions.set('timeline', [Permission.TimelineRead, Permission.TimelineDownload]); - - permissions.set('memory', [ - Permission.MemoryCreate, - Permission.MemoryRead, - Permission.MemoryUpdate, - Permission.MemoryDelete, - ]); - - permissions.set('notification', [ - Permission.NotificationCreate, - Permission.NotificationRead, - Permission.NotificationUpdate, - Permission.NotificationDelete, - ]); - - permissions.set('partner', [ - Permission.PartnerCreate, - Permission.PartnerRead, - Permission.PartnerUpdate, - Permission.PartnerDelete, - ]); - - permissions.set('person', [ - Permission.PersonCreate, - Permission.PersonRead, - Permission.PersonUpdate, - Permission.PersonDelete, - Permission.PersonStatistics, - Permission.PersonMerge, - Permission.PersonReassign, - ]); - - permissions.set('session', [ - Permission.SessionCreate, - Permission.SessionRead, - Permission.SessionUpdate, - Permission.SessionDelete, - Permission.SessionLock, - ]); - - permissions.set('sharedLink', [ - Permission.SharedLinkCreate, - Permission.SharedLinkRead, - Permission.SharedLinkUpdate, - Permission.SharedLinkDelete, - ]); - - permissions.set('stack', [ - Permission.StackCreate, - Permission.StackRead, - Permission.StackUpdate, - Permission.StackDelete, - ]); - - permissions.set('systemConfig', [Permission.SystemConfigRead, Permission.SystemConfigUpdate]); - - permissions.set('systemMetadata', [Permission.SystemMetadataRead, Permission.SystemMetadataUpdate]); - - permissions.set('tag', [ - Permission.TagCreate, - Permission.TagRead, - Permission.TagUpdate, - Permission.TagDelete, - Permission.TagAsset, - ]); - - permissions.set('adminUser', [ - Permission.AdminUserCreate, - Permission.AdminUserRead, - Permission.AdminUserUpdate, - Permission.AdminUserDelete, - ]); + let searchValue = $state(''); + let filteredResults = $derived(Object.entries(permissions).filter(matches(searchValue))); const handleSelectItems = (permissions: Permission[]) => { selectedItems = Array.from(new Set([...selectedItems, ...permissions])); @@ -176,9 +72,9 @@ }); } else { if (selectAllItems) { - onClose({ name: apiKey.name, permissions: [Permission.All] }); + onClose({ name, permissions: [Permission.All] }); } else { - onClose({ name: apiKey.name, permissions: selectedItems }); + onClose({ name, permissions: selectedItems }); } } }; @@ -199,22 +95,37 @@
- - + + +
- -
- -
{/await} {:then { default: Map }} - + {/await}
diff --git a/web/src/lib/modals/SharedLinkCreateModal.svelte b/web/src/lib/modals/SharedLinkCreateModal.svelte index b4b9eaf98f..6d46405ad7 100644 --- a/web/src/lib/modals/SharedLinkCreateModal.svelte +++ b/web/src/lib/modals/SharedLinkCreateModal.svelte @@ -1,16 +1,13 @@ + + diff --git a/web/src/routes/(user)/s/[slug]/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/s/[slug]/[[photos=photos]]/[[assetId=id]]/+page.svelte new file mode 100644 index 0000000000..25c24ab028 --- /dev/null +++ b/web/src/routes/(user)/s/[slug]/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -0,0 +1,12 @@ + + + diff --git a/web/src/routes/(user)/s/[slug]/[[photos=photos]]/[[assetId=id]]/+page.ts b/web/src/routes/(user)/s/[slug]/[[photos=photos]]/[[assetId=id]]/+page.ts new file mode 100644 index 0000000000..92d4dd042e --- /dev/null +++ b/web/src/routes/(user)/s/[slug]/[[photos=photos]]/[[assetId=id]]/+page.ts @@ -0,0 +1,4 @@ +import { loadSharedLink } from '$lib/utils/shared-links'; +import type { PageLoad } from './$types'; + +export const load = (async ({ params, url }) => loadSharedLink({ params, url })) satisfies PageLoad; diff --git a/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte index acb7176a7c..48ead6e6b4 100644 --- a/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -330,9 +330,9 @@ > {#each getObjectKeys(terms) as searchKey (searchKey)} {@const value = terms[searchKey]} -
+
{getHumanReadableSearchKey(searchKey as keyof SearchTerms)} diff --git a/web/src/routes/(user)/share/[key]/+error.svelte b/web/src/routes/(user)/share/[key]/+error.svelte index 9103a7710c..2c4d2a4d0e 100644 --- a/web/src/routes/(user)/share/[key]/+error.svelte +++ b/web/src/routes/(user)/share/[key]/+error.svelte @@ -1,14 +1,5 @@ - - Oops! Error - Immich - - -
-

Page not found :/

- {#if page.error?.message} -

{page.error.message}

- {/if} -
+ diff --git a/web/src/routes/(user)/share/[key]/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/share/[key]/[[photos=photos]]/[[assetId=id]]/+page.svelte index d16ba622e9..25c24ab028 100644 --- a/web/src/routes/(user)/share/[key]/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/share/[key]/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -1,97 +1,12 @@ - - {title} - - -{#if passwordRequired} -
-
-
{$t('password_required')}
-
- {$t('sharing_enter_password')} -
-
-
- - - -
-
-
-
- - {#snippet leading()} - - {/snippet} - - {#snippet trailing()} - - {/snippet} - -
-{/if} - -{#if !passwordRequired && sharedLink?.type == SharedLinkType.Album} - -{/if} -{#if !passwordRequired && sharedLink?.type == SharedLinkType.Individual} -
- -
-{/if} + diff --git a/web/src/routes/(user)/share/[key]/[[photos=photos]]/[[assetId=id]]/+page.ts b/web/src/routes/(user)/share/[key]/[[photos=photos]]/[[assetId=id]]/+page.ts index c0edb5e669..92d4dd042e 100644 --- a/web/src/routes/(user)/share/[key]/[[photos=photos]]/[[assetId=id]]/+page.ts +++ b/web/src/routes/(user)/share/[key]/[[photos=photos]]/[[assetId=id]]/+page.ts @@ -1,44 +1,4 @@ -import { getAssetThumbnailUrl, setSharedLink } from '$lib/utils'; -import { authenticate } from '$lib/utils/auth'; -import { getFormatter } from '$lib/utils/i18n'; -import { getAssetInfoFromParam } from '$lib/utils/navigation'; -import { getMySharedLink, isHttpError } from '@immich/sdk'; +import { loadSharedLink } from '$lib/utils/shared-links'; import type { PageLoad } from './$types'; -export const load = (async ({ params, url }) => { - const { key } = params; - await authenticate(url, { public: true }); - - const $t = await getFormatter(); - - try { - const [sharedLink, asset] = await Promise.all([getMySharedLink({ key }), getAssetInfoFromParam(params)]); - setSharedLink(sharedLink); - const assetCount = sharedLink.assets.length; - const assetId = sharedLink.album?.albumThumbnailAssetId || sharedLink.assets[0]?.id; - const assetPath = assetId ? getAssetThumbnailUrl(assetId) : '/feature-panel.png'; - - return { - sharedLink, - sharedLinkKey: key, - asset, - meta: { - title: sharedLink.album ? sharedLink.album.albumName : $t('public_share'), - description: sharedLink.description || $t('shared_photos_and_videos_count', { values: { assetCount } }), - imageUrl: assetPath, - }, - }; - } catch (error) { - if (isHttpError(error) && error.data.message === 'Invalid password') { - return { - passwordRequired: true, - sharedLinkKey: key, - meta: { - title: $t('password_required'), - }, - }; - } - - throw error; - } -}) satisfies PageLoad; +export const load = (async ({ params, url }) => loadSharedLink({ params, url })) satisfies PageLoad; diff --git a/web/src/routes/(user)/shared-links/[[id=id]]/+page.svelte b/web/src/routes/(user)/shared-links/[[id=id]]/+page.svelte index 7062966b71..2f732646ec 100644 --- a/web/src/routes/(user)/shared-links/[[id=id]]/+page.svelte +++ b/web/src/routes/(user)/shared-links/[[id=id]]/+page.svelte @@ -9,10 +9,10 @@ } from '$lib/components/shared-components/notification/notification'; import SharedLinkCard from '$lib/components/sharedlinks-page/shared-link-card.svelte'; import { AppRoute } from '$lib/constants'; - import { modalManager } from '$lib/managers/modal-manager.svelte'; import SharedLinkCreateModal from '$lib/modals/SharedLinkCreateModal.svelte'; import { handleError } from '$lib/utils/handle-error'; import { getAllSharedLinks, removeSharedLink, SharedLinkType, type SharedLinkResponseDto } from '@immich/sdk'; + import { modalManager } from '@immich/ui'; import { onMount } from 'svelte'; import { t } from 'svelte-i18n'; import type { PageData } from './$types'; diff --git a/web/src/routes/(user)/tags/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/tags/[[photos=photos]]/[[assetId=id]]/+page.svelte index 503ea72d54..32e910e4a9 100644 --- a/web/src/routes/(user)/tags/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/tags/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -8,14 +8,13 @@ import TreeItems from '$lib/components/shared-components/tree/tree-items.svelte'; import Sidebar from '$lib/components/sidebar/sidebar.svelte'; import { AppRoute, AssetAction, QueryParameter } from '$lib/constants'; - import { modalManager } from '$lib/managers/modal-manager.svelte'; import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte'; import TagCreateModal from '$lib/modals/TagCreateModal.svelte'; import TagEditModal from '$lib/modals/TagEditModal.svelte'; import { AssetInteraction } from '$lib/stores/asset-interaction.svelte'; import { joinPaths, TreeNode } from '$lib/utils/tree-utils'; import { deleteTag, getAllTags, type TagResponseDto } from '@immich/sdk'; - import { Button, HStack, Text } from '@immich/ui'; + import { Button, HStack, modalManager, Text } from '@immich/ui'; import { mdiPencil, mdiPlus, mdiTag, mdiTagMultiple, mdiTrashCanOutline } from '@mdi/js'; import { onDestroy } from 'svelte'; import { t } from 'svelte-i18n'; diff --git a/web/src/routes/(user)/trash/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/trash/[[photos=photos]]/[[assetId=id]]/+page.svelte index bae1786891..2989a78a13 100644 --- a/web/src/routes/(user)/trash/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/trash/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -9,18 +9,17 @@ import AssetSelectControlBar from '$lib/components/photos-page/asset-select-control-bar.svelte'; import EmptyPlaceholder from '$lib/components/shared-components/empty-placeholder.svelte'; import { - NotificationType, notificationController, + NotificationType, } from '$lib/components/shared-components/notification/notification'; import { AppRoute } from '$lib/constants'; - import { modalManager } from '$lib/managers/modal-manager.svelte'; import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte'; import { AssetInteraction } from '$lib/stores/asset-interaction.svelte'; import { featureFlags, serverConfig } from '$lib/stores/server-config.store'; import { handlePromiseError } from '$lib/utils'; import { handleError } from '$lib/utils/handle-error'; import { emptyTrash, restoreTrash } from '@immich/sdk'; - import { Button, HStack, Text } from '@immich/ui'; + import { Button, HStack, modalManager, Text } from '@immich/ui'; import { mdiDeleteForeverOutline, mdiHistory } from '@mdi/js'; import { onDestroy } from 'svelte'; import { t } from 'svelte-i18n'; diff --git a/web/src/routes/(user)/user-settings/+page.svelte b/web/src/routes/(user)/user-settings/+page.svelte index fb3af28823..43c214fd07 100644 --- a/web/src/routes/(user)/user-settings/+page.svelte +++ b/web/src/routes/(user)/user-settings/+page.svelte @@ -1,9 +1,8 @@ + + +
+ {#if assets && data.assets.length > 0} + {#each assets as asset (asset.id)} + setAsset(asset)} /> + {/each} + {:else} +

+ {$t('no_assets_to_show')} +

+ {/if} +
+
+ +{#if $showAssetViewer} + {#await import('$lib/components/asset-viewer/asset-viewer.svelte') then { default: AssetViewer }} + + 1} + {onNext} + {onPrevious} + {onRandom} + {onAction} + onClose={() => { + assetViewingStore.showAssetViewer(false); + handlePromiseError(navigate({ targetRoute: 'current', assetId: null })); + }} + /> + + {/await} +{/if} diff --git a/web/src/routes/(user)/utilities/large-files/[[photos=photos]]/[[assetId=id]]/+page.ts b/web/src/routes/(user)/utilities/large-files/[[photos=photos]]/[[assetId=id]]/+page.ts new file mode 100644 index 0000000000..6780fdb023 --- /dev/null +++ b/web/src/routes/(user)/utilities/large-files/[[photos=photos]]/[[assetId=id]]/+page.ts @@ -0,0 +1,17 @@ +import { authenticate } from '$lib/utils/auth'; +import { getFormatter } from '$lib/utils/i18n'; +import { searchLargeAssets } from '@immich/sdk'; +import type { PageLoad } from './$types'; + +export const load = (async ({ url }) => { + await authenticate(url); + const assets = await searchLargeAssets({ minFileSize: 0 }); + const $t = await getFormatter(); + + return { + assets, + meta: { + title: $t('large_files'), + }, + }; +}) satisfies PageLoad; diff --git a/web/src/routes/+layout.svelte b/web/src/routes/+layout.svelte index 6e1c193276..71958d9d9f 100644 --- a/web/src/routes/+layout.svelte +++ b/web/src/routes/+layout.svelte @@ -9,7 +9,6 @@ import NotificationList from '$lib/components/shared-components/notification/notification-list.svelte'; import UploadPanel from '$lib/components/shared-components/upload-panel.svelte'; import { eventManager } from '$lib/managers/event-manager.svelte'; - import { modalManager } from '$lib/managers/modal-manager.svelte'; import VersionAnnouncementModal from '$lib/modals/VersionAnnouncementModal.svelte'; import { serverConfig } from '$lib/stores/server-config.store'; import { user } from '$lib/stores/user.store'; @@ -22,7 +21,7 @@ import { copyToClipboard } from '$lib/utils'; import { isAssetViewerRoute } from '$lib/utils/navigation'; import type { ServerVersionResponseDto } from '@immich/sdk'; - import { setTranslations } from '@immich/ui'; + import { modalManager, setTranslations } from '@immich/ui'; import { onMount, type Snippet } from 'svelte'; import { t } from 'svelte-i18n'; import { run } from 'svelte/legacy'; diff --git a/web/src/routes/admin/jobs-status/+page.svelte b/web/src/routes/admin/jobs-status/+page.svelte index 9985fd949d..d1bd4e41d0 100644 --- a/web/src/routes/admin/jobs-status/+page.svelte +++ b/web/src/routes/admin/jobs-status/+page.svelte @@ -2,11 +2,10 @@ import JobsPanel from '$lib/components/admin-page/jobs/jobs-panel.svelte'; import AdminPageLayout from '$lib/components/layouts/AdminPageLayout.svelte'; import { AppRoute } from '$lib/constants'; - import { modalManager } from '$lib/managers/modal-manager.svelte'; import JobCreateModal from '$lib/modals/JobCreateModal.svelte'; import { asyncTimeout } from '$lib/utils'; import { getAllJobsStatus, type AllJobStatusResponseDto } from '@immich/sdk'; - import { Button, HStack, Text } from '@immich/ui'; + import { Button, HStack, modalManager, Text } from '@immich/ui'; import { mdiCog, mdiPlus } from '@mdi/js'; import { onDestroy, onMount } from 'svelte'; import { t } from 'svelte-i18n'; diff --git a/web/src/routes/admin/library-management/+page.svelte b/web/src/routes/admin/library-management/+page.svelte index 3d8792749c..d16fc4bc7c 100644 --- a/web/src/routes/admin/library-management/+page.svelte +++ b/web/src/routes/admin/library-management/+page.svelte @@ -10,7 +10,6 @@ notificationController, NotificationType, } from '$lib/components/shared-components/notification/notification'; - import { modalManager } from '$lib/managers/modal-manager.svelte'; import LibraryImportPathModal from '$lib/modals/LibraryImportPathModal.svelte'; import LibraryRenameModal from '$lib/modals/LibraryRenameModal.svelte'; import LibraryUserPickerModal from '$lib/modals/LibraryUserPickerModal.svelte'; @@ -32,7 +31,7 @@ type LibraryStatsResponseDto, type UserResponseDto, } from '@immich/sdk'; - import { Button, Text } from '@immich/ui'; + import { Button, modalManager, Text } from '@immich/ui'; import { mdiDotsVertical, mdiPlusBoxOutline, mdiSync } from '@mdi/js'; import { onMount } from 'svelte'; import { t } from 'svelte-i18n'; diff --git a/web/src/routes/admin/users/+page.svelte b/web/src/routes/admin/users/+page.svelte index 3ebc67dabf..4ed91147da 100644 --- a/web/src/routes/admin/users/+page.svelte +++ b/web/src/routes/admin/users/+page.svelte @@ -7,7 +7,6 @@ notificationController, } from '$lib/components/shared-components/notification/notification'; import { AppRoute } from '$lib/constants'; - import { modalManager } from '$lib/managers/modal-manager.svelte'; import UserCreateModal from '$lib/modals/UserCreateModal.svelte'; import UserDeleteConfirmModal from '$lib/modals/UserDeleteConfirmModal.svelte'; import UserRestoreConfirmModal from '$lib/modals/UserRestoreConfirmModal.svelte'; @@ -17,7 +16,7 @@ import { websocketEvents } from '$lib/stores/websocket'; import { getByteUnitString } from '$lib/utils/byte-units'; import { UserStatus, searchUsersAdmin, type UserAdminResponseDto } from '@immich/sdk'; - import { Button, HStack, IconButton, Text } from '@immich/ui'; + import { Button, HStack, IconButton, Text, modalManager } from '@immich/ui'; import { mdiDeleteRestore, mdiEyeOutline, mdiInfinity, mdiPlusBoxOutline, mdiTrashCanOutline } from '@mdi/js'; import { DateTime } from 'luxon'; import { onMount } from 'svelte'; diff --git a/web/src/routes/admin/users/[id]/+page.svelte b/web/src/routes/admin/users/[id]/+page.svelte index 79e76e5d0f..960b006d4b 100644 --- a/web/src/routes/admin/users/[id]/+page.svelte +++ b/web/src/routes/admin/users/[id]/+page.svelte @@ -1,12 +1,12 @@